为什么有些人在Qt编程中使用sender()
时要谨慎?典型的答案是它违反了代码模块化的原则。来自Qt documentation about pitfalls in PySide:
如果要获取发出信号的对象,可以这样做 使用QtCore.QObject.sender(),虽然你应该三思而后行 在使用它之前(有关详细信息,请参阅QObject的API文档)。如果你 真的,真的决定你必须使用它,请注意正确 现在,你不能把它称为静态方法,而只能从一个 QObject插槽。
作为一名新的PySide程序员,我不会考虑在除了一个插槽之外的任何地方使用sender
,所以第二个问题似乎没有实际意义。
第一个原因建议检查doc的API。让我们来看看。发件人From the API docs:
这个功能违反了面向对象的模块化原则。
在Summerfield的PyQt书中也提到了这个警告,他在那里写道
有些程序员不喜欢使用
sender()
因为他们有这种感觉 不是很好的面向对象风格。
他说的全部。
与使用PyQt / PySide / Qt的其他标准做法相比,是什么使sender
特别是非模块化的?在PySide中,我们不断在模型/视图框架中调用internalPointers之类的东西,或者通过使它们成为self
的属性(有效地将它们视为全局变量)来隐藏在方法中使用的参数。这些和其他此类实践似乎在技术上违反了模块化,并且似乎在GUI编程中无处不在。
那么为什么人们认为sender
值得特别挑剔,特别令人担忧的模块化?或者也许等价,Qt编程的模块化程度如何呢?
总的来说,sender
似乎是一种非常有用,简单的方法。与partial
函数或lambda
表达式或QSignalMapper
之类的内容相比,使用和教授其他人更容易。因此,我可以看到使用sender
的好处,坦率地说很难理解将它们包含在官方Qt文档中的缺点。
答案 0 :(得分:3)
一般而言,对象的插槽可以完全没有信号或任何兼容信号来调用。在前一种情况下,没有发件人这样的东西 - 例如,使用QMetaObject::invokeMethod
时。当通过信号激活插槽时,发送方对象可以是任何类。你可以从sender()
得到的最多的是它是一个QObject,如果它是任何东西的话。
信号槽机制设计用于解耦对象,但是依赖于sender()
你正在反转它并且正在耦合到发送对象。这有一些应用程序,当设计需要这样的耦合时,例如当你实现widget hider时,或者当你想要将插槽的动作转移到相关的特定实例时宾语。在这种有限的情况之外,需要按设计进行耦合,sender()
的使用表明设计和耦合不良是没有充分理由的。
文档仅仅意味着在选择将耦合引入设计之前,理解信号槽机制的目的以及解耦软件设计的好处。
Lambda表达式实际上完全消除了sender()
和QSignalMapper
的需要,所以你在那里反对自己:
connect(foo, &Foo::aSignal, [&foo]{ foo.method(); });
// as opposed to
connect(foo, &Foo::aSignal, [&foo]{ qobject_cast<Foo*>(sender())->method(); });
// and also
QList<QPushButton*> buttons;
QPointer<QLabel> label;
for (int i = 0; i < buttons; ++i)
connect(buttons[i], &QAbstractButton::clicked, [i, label] {
label->setText(QString("Button %i was clicked.").arg(i+1);
});
另一方面,QSignalMapper
存在的原因无论如何都被夸大了,因为你已经拥有了可以在Qt4风格的代码中使用的属性系统。
class Helper : public QObject {
Q_OBJECT
QPointer<QLabel> m_label;
public:
Helper(QLabel * label, QObject * parent = 0) : QObject(parent), m_label(label) {}
Q_SLOT void showIndex() {
m_label->setText(
QString("Button %i was clicked.").arg(sender()->property("index").toInt())
);
}
};
void setup(QList<QAbstractButton*> buttons) {
int i = 1;
foreach (QAbstractButton* button, buttons) {
button->setProperty("index", i++);
connect(button, SIGNAL(clicked()), helper, SLOT(showIndex());
}
}
&#34;内部&#34; QModelIndex
的指针只是一种在索引中携带特定于模型的数据的方式,在设计和发布时遵循合理的约束条件:
它是它所设计的时代的产物。当然,它可以变得更好,而不会将internalXxx
方法暴露给广大公众,但这就是为什么这些方法被称为内部首先。最终,如果你真的希望用脚射击自己,没有人会阻止你。作为索引的消费者,你应该假装那些方法不存在。这就是它的全部内容。
是的,即使回到VC6时代,也可以使用专用的池分配器为索引和类似的类做低开销的pimpl。甚至可以利用所述池分配器来确定派生类的运行时类型,并实现虚拟析构函数机制而不需要vtable指针开销。当然。但它并没有这样做,而且决定权在我们身边。鉴于模型实施者需要大量解释来安全地利用这种&#34;魔术&#34;,我认为内部指针机制是一个更安全的赌注。