处理来自引用未知的小部件的信号

Posted

技术标签:

【中文标题】处理来自引用未知的小部件的信号【英文标题】:Handle signal from a widget whose reference is unkown 【发布时间】:2015-04-01 10:07:54 【问题描述】:

假设我已经实现了一个小部件 A,它需要处理小部件 C 发送的信号。但是小部件 a 没有小部件 C 的引用(至少获取起来非常麻烦)。正确的做法是什么?

这是一个简单的例子:

class WidgetA(QtGui.QWidget):
    def __init__(self):
        self._tab_widget = QtGui.QTabWidget()
        self.layout().addWidget(_tab_widget)
        self._tab_widget.addTab(WidgetB())
        self._tab_widget.addTab(...)

    def show_another_tab(self, index):
        self._tab_widget.setCurrentIndex(index)

class WidgetB(QtGui.QWidget):
    def __init__(self):
        self.layout().addWidget(WidgetC())

class WidgetC(QtGui.QWidget):
    show_another_tab = QtCore.pyqtSignal()
    def __init__(self):
        button = QtGui.QPushButton("show_another_tab")
        button.clicked.connect(lambda: self.show_another_tab.emit(2))

所以基本上,我有一个以上的标签。其中一个选项卡中有一个小按钮。当用户单击按钮时,我想切换到另一个选项卡。遗憾的是,该按钮是 WidgetA 的子项的子项,因此在 WidgetA 中保留对按钮的引用对我来说很麻烦。更多的按钮可以在运行时动态创建。

在这种情况下使用信号的正确方法是什么?或者我应该只是在全局变量中引用 WidgetA(我真的不想这样做,但似乎是最简单的方法)?

【问题讨论】:

将一个信号从小部件 c 连接到小部件 b 的信号,并将该信号连接到小部件 a 的插槽? @thuga 我不知道这是否是个好主意,如果我有另一个小部件 d,它是小部件 c 的子部件,而小部件 a 不想处理来自 d 的信号。跨度> 然后你可以重新设计你的应用程序,这样你就不会遇到这样的问题,或者你可以将小部件 d 的信号连接到小部件 c 的信号。 【参考方案1】:

如果您在设置父母方面始终如一,那么获取参考资料就不会很麻烦。如果你在你的例子中这样做了,你可以这样做:

class WidgetA(QtGui.QWidget):
    def __init__(self):
        ...
        self._tab_widget.addTab(WidgetB(self))

    def show_another_tab(self, index):
        self._tab_widget.setCurrentIndex(index)

class WidgetB(QtGui.QWidget):
    def __init__(self, parent):
        self.layout().addWidget(WidgetC(self))

class WidgetC(QtGui.QWidget):
    show_another_tab = QtCore.pyqtSignal()
    def __init__(self, parent):
        ...
        button.clicked.connect(
            lambda: self.parent().parent().show_another_tab.emit(2))

但是,如果您想避免parent() 调用的长链,更全局的方法可能会更好。一种方法是将qApp 设置为QApplication 的子类:

# do this before QtGui is imported elsewhere
QtGui.qApp = MyApplication()

然后您可以提供访问器方法,允许您从上到下导航您的小部件层次结构:

       button.clicked.connect(
            lambda: QtGui.qApp.tabManager().setCurrentIndex(2))

(注意:显式设置 qApp 不是可选的 - 如果不这样做,您将无法访问子类的任何属性)。

【讨论】:

我想在其他选项卡甚至其他应用程序中重用 WidgetC。因此,如果 WidgetC 不需要知道父母,而只是发出它的信号会更好。 Qt 中没有通用信号之类的东西。你总是连接两个特定的对象,即使它们不是父子关系。但事实上,在您的示例中,WidgetC 实例确实知道他们的父母,因为他们引用了show_another_tab。他们还知道其他选项卡,因为它们引用了它们的特定索引。确实,WidgetA 类应该负责连接信号,WidgetBWidgetC 类应该具有通用接口(即像标准项目模型及其标准项目)。 对不起,我没有把例子写得更清楚。 WidgetC 没有引用 show_another_tabshow_another_tab 本身就是一个信号。 WidgetA 恰好有一个 show_another_tab 刚刚成为我想在同名的函数中处理 show_another_tab 信号。我注意到qt中除了信号系统之外还有一个事件系统。我可以创建一个自定义事件并通过 eventFilter 在WidgetA 中处理此事件吗?我正在寻找这种方法。谢谢。 @kkpattern。抱歉,很清楚 - 我只是错过了那个细节。不过,它并没有真正改变我之前的评论。 WidgetC 类知道它是选项卡小部件中选项卡的一部分,并且它还了解其他选项卡(即它们的特定索引)。可能是您的示例不切实际,但这对我来说似乎倒退了。 WidgetA 类应该管理标签,而不是标签内的小部件试图自下而上控制标签管理器。 您可以将WidgetC作为热链接,供用户跳转到特定标签。由于WidgetC 可以放在任何地方(在WidgetB 内部,WidgetD... 内部),它不会知道到WidgetA 的父链的长度。我认为将WidgetA 放在全局变量中会是更好的方法。通过这种方式,WidgetC很难在其他应​​用程序中重用。

以上是关于处理来自引用未知的小部件的信号的主要内容,如果未能解决你的问题,请参考以下文章

从主窗口中的 UI 文件加载带有信号槽定义的小部件

如何禁用来自另一个站点的小部件上的链接?

来自 Room 的小部件中的 setImageViewUri

在 MarshMallow 的小部件中显示来自 SDCard 的图像

使用继承的小部件传递小部件键

来自窗口的 pyqt 信号不更改主窗口小部件