编辑:我已阅读Passing and argument to a slot它有用,但没有解决我将多个引用传递给我通过信号槽调用的函数的问题。
我目前正在研究一种基本上是单位转换器的Qt应用程序。我正在使用QDoubleSpinBoxes作为输入和输出来实现它。我遇到了一个我正在寻求帮助的问题。实现的想法是用户将输入他们想要转换的任何值,并且当用户失去焦点在旋转框上或按下回车时,将填充其他单位类型框的答案。
以下是我目前的工作方式:
// creates a custom spinbox for each option
modifiedSpinbox *FahrenheitDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *CelciusDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *KelvinDblSpinbox = new modifiedSpinbox();
modifiedSpinbox *RankineDblSpinbox = new modifiedSpinbox();
// Creates a signal mapper that allows passing of a parameter
// to the convert functions anytime a number is entered
QSignalMapper *tempMapper = new QSignalMapper(this);
// Connects the spinbox editing complete signal with the mapper
connect(tempMapper, SIGNAL(mapped(int)),
this,SLOT(on_userInput(int whoSignaled)));
// Connects the mapper with the function that calls the convert class
connect(FahrenheitDblSpinbox, SIGNAL(editingFinished()),
tempMapper, SLOT(map()));
tempMapper->setMapping(FahrenheitDblSpinbox, 1);
我想实现这个转换函数的方法是,在用户完成对spinbox的输入时,让代码向slot函数发送一个信号(editingFinished()),该函数调用一个具有所需函数的converttools类做实际的转换。我遇到的问题是我无法弄清楚如何将这些spinbox对象的引用传递给我的converttools类,以便我可以直接设置spinbox值。 我最接近的是使用QSignalMapper(如上所示)将单个 int或Qwidget传递给我的插槽函数,而不是我想要的对象。
我想知道如何在发出信号后将多个引用传递给我的自定义类。我在这看了很多问题,似乎仍然无法弄清楚如何做到这一点或更好的方式,我没有看到。
谢谢!
答案 0 :(得分:1)
您正在寻找的是:
QDoubleSpinBox
指定温度的信号QDoubleSpinBox
发送信号以更新它们QDoubleSpinBox
特定温度单位的转换 QSignalMapper
is a bad choice here, because:
此类收集一组无参数信号,并使用与发送信号的对象相对应的整数,字符串或窗口小部件参数重新发出它们
所以我们不能接受指定的温度。相反,让我们从map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>>
开始,它将分别用于从给定的QDoubleSpinBox
映射到“转换为公共温度单位”和“从公共温度单位转换”。
然后,我们将围绕此map
构建一个对象,如下所示:
class SlotMapper : public QObject
{
Q_OBJECT
map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> mapping;
public:
SlotMapper() = default;
SlotMapper(const map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>> mapping) : mapping(mapping) {};
AddMapping(QDoubleSpinBox* key, function<double(double)> valueFirst, function<double(double)> valueSecond) { mapping.insert_or_assign(key, make_pair(valueFirst, valueSecond)); }
void map(const double assignedTemperature) const {
const auto commonTemperatureUnit = mapping.at(QObject()::sender).first(assignedTemperature);
for(auto it = cbegin(mapping); it != cend(mapping); ++it) {
if(it->first != QObject()::sender) {
it->first->blockSignals(true);
it->first->setValue(it->second.second(commonTemperatureUnit));
it->first->blockSignals(false);
}
}
}
};
此对象应使用所有必需的转换函数构建。在您的情况下,可能看起来像:
SlotMapper mySlotMapper(map<QDoubleSpinBox*, pair<function<double(double)>, function<double(double)>>>{ {FahrenheitDblSpinbox, make_pair([](const double param){ return (param - 32.0) * 5.0 / 9.0; }, [](const double param){ return param * 9.0 / 5.0 + 32.0; })},
{CelciusDblSpinbox, make_pair([](const double param){ return param; }, [](const double param){ return param; })},
{KelvinDblSpinbox, make_pair([](const double param){ return param - 273.15; }, [](const double param){ return param + 273.15; })},
{RankineDblSpinbox, make_pair([](const double param){ return (param - 491.67) * 5.0 / 9.0; }, [](const double param){ return (param + 273.15) * 9.0 / 5.0; })} });
就你的联系而言,它们看起来像是:
connect(FahrenheitDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(CelciusDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(KelvinDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
connect(RankineDblSpinbox, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), &mySlotMapper, &SlotMapper::map);
答案 1 :(得分:1)
QSignalMapper
在C ++ 11中已经过时了。它只是一种创可贴,可以防止在C ++ 98中声明仿函数的麻烦。如果您使用的是C ++ 11编译器,则不必使用QSignalMapper
。
Lambdas让它变得微不足道 - 你真的不需要传球太多。附注:理想情况下,您应该使用现代的C ++ 11单元库。
这是一个完整的例子:
// https://github.com/KubaO/stackoverflown/tree/master/questions/tempconvert-42648860
#include <QtWidgets>
const char kUnit[] = "unit";
class MyWidget : public QWidget {
QFormLayout m_layout{this};
QDoubleSpinBox m_fahrenheit, m_celsius, m_kelvin;
QList<QDoubleSpinBox*> const m_spinBoxes{&m_fahrenheit, &m_celsius, &m_kelvin};
enum Unit { Fahrenheit, Celsius, Kelvin };
static double Q_DECL_RELAXED_CONSTEXPR fromK(Unit to, double val) {
if (to == Fahrenheit) return (val-273.15)*1.8 + 32.0;
else if (to == Celsius) return val - 273.15;
else return val;
}
static double Q_DECL_RELAXED_CONSTEXPR toK(Unit from, double val) {
if (from == Fahrenheit) return (val-32.0)/1.8 + 273.15;
else if (from == Celsius) return val + 273.15;
else return val;
}
void setTemp(Unit unit, double temp, QDoubleSpinBox * skip = nullptr) {
for (auto spin : m_spinBoxes) if (spin != skip) {
QSignalBlocker b{spin};
spin->setValue(fromK(unitOf(spin), toK(unit, temp)));
}
}
static Unit unitOf(QDoubleSpinBox * spin) {
return static_cast<Unit>(spin->property(kUnit).toInt());
}
public:
MyWidget(QWidget * parent = nullptr) : QWidget{parent} {
m_layout.addRow("Fahreneheit", &m_fahrenheit);
m_layout.addRow("Celsius", &m_celsius);
m_layout.addRow("Kelvin", &m_kelvin);
m_fahrenheit.setProperty(kUnit, Fahrenheit);
m_celsius.setProperty(kUnit, Celsius);
m_kelvin.setProperty(kUnit, Kelvin);
for (auto const spin : m_spinBoxes) {
auto const unit = unitOf(spin);
spin->setRange(fromK(unit, 0.), fromK(unit, 1000.));
connect(spin, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
[=]{ setTemp(unit, spin->value(), spin); });
}
setTemp(Celsius, 20.);
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MyWidget ui;
ui.show();
return app.exec();
}