Qt:在发出信号后尝试将多个引用传递给槽函数

时间:2017-03-07 12:53:11

标签: qt signals-slots

编辑:我已阅读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传递给我的插槽函数,而不是我想要的对象。

我想知道如何在发出信号后将多个引用传递给我的自定义类。我在这看了很多问题,似乎仍然无法弄清楚如何做到这一点或更好的方式,我没有看到。

谢谢!

2 个答案:

答案 0 :(得分:1)

您正在寻找的是:

  1. 发出QDoubleSpinBox指定温度的信号
  2. 转换为常用温度单位
  3. 向所有其他QDoubleSpinBox发送信号以更新它们
  4. 从公共温度单位到每个QDoubleSpinBox特定温度单位的转换
  5. 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();
}