确定在按下按钮之前最后一个焦点的 QWidget

Posted

技术标签:

【中文标题】确定在按下按钮之前最后一个焦点的 QWidget【英文标题】:Determine QWidget that had last focus before button press 【发布时间】:2017-10-12 09:57:07 【问题描述】:

我想在我的 Qt 应用程序中实现以下功能:

用户打开一个或多个“输入”小部件(InputWidget 类的实例),每个小部件都包含一个 QLineEdit 小部件 用户打开一个“帮助”对话框 用户在“帮助”对话框中选择一个值 用户在“帮助”对话框中按下“插入”QPushButton “帮助”对话框中的选定值被插入到“输入”对话框的 QLineEdit 中,该对话框在按下“插入”按钮之前具有最后一个焦点

所以,基本上,我想要的是,如果用户在以下屏幕截图中单击“插入”,则字符串“Apple”应该出现在焦点输入对话框中。下面的代码示例确实有效,只是将字符串(通常见下文)插入到第二个字符串中。

这是创建此设置的代码示例:

from PyQt5.QtWidgets import (QApplication, QWidget, QHBoxLayout,
                             QLineEdit, QLabel, QPushButton, QComboBox)
import sys


# this is the missing bit
def determineWhichWidgetHadLastFocus():
    for widget in QApplication.instance().topLevelWidgets():
        if isinstance(widget, InputWidget):
            # do something wonderful to determine whether this widget 
            # is the one that had last focus 
            wonderful = True

        if wonderful: 
            return widget
    return None


class BaseWidget(QWidget):
    """ Base widget type """

    def __init__(self, name):
        super(BaseWidget, self).__init__()
        self.setWindowTitle(name)
        self.setupUi()
        self.show()

    def setupUi(self):
        pass


class InputWidget(BaseWidget):
    """ InputWidget contains a QLabel and a QLineEdit widget """

    def setupUi(self):
        self.label = QLabel("Input string:")
        self.edit = QLineEdit()

        layout = QHBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.edit)


class HelperWidget(BaseWidget):
    """ HelperWidget contains a QLineEdit and a QPushButton widget. Pressing 
    the button inserts the content of the edit widget into the edit widget of 
    the last activated InputWidget """ 

    def setupUi(self):
        self.combo = QComboBox()
        self.combo.addItems(["Apple", "Pear", "Banana"])
        self.button = QPushButton("Insert")
        self.button.clicked.connect(self.insertString)

        layout = QHBoxLayout(self)
        layout.addWidget(self.combo)
        layout.addWidget(self.button)

    def insertString(self):
        widget = determineWhichWidgetHadLastFocus()
        if widget:
            widget.edit.insert(self.combo.currentText())

def main():
    app = QApplication(sys.argv)

    diag1 = InputWidget("Input dialog")
    diag2 = InputWidget("Another input")

    helper = HelperWidget("Helper")

    app.exec_()


if __name__ == "__main__":
    main()

缺少的部分是determineWhichWidgetHadLastFocus() 函数。

这个函数应该做一些很棒的事情,允许它确定哪个“输入”是最后一个保持焦点的。目前,它会遍历来自QApplication 的***小部件列表,但***小部件的顺序并不反映激活顺序(通常,但并不总是显示为创建顺序)。

我想到的一个想法是安装一个事件过滤器来跟踪FocusIn 事件。这对于我的示例中的 InputWidget 类来说很容易,但对于我的实际应用程序可能不太适用,因为它有许多 QLineEdits、QTextEdits 和遍布各处的后代类。我宁愿不走那条路。

还有其他想法吗?

【问题讨论】:

【参考方案1】:

事实证明,事件过滤器的想法毕竟是要走的路。我所做的是首先创建一个事件过滤器类,如果发送对象是QLineEdit 对象,它会发出信号:

from PyQt5.QtCore import QObject, QEvent, pyqtSignal


class inputFocusFilter(QObject):
    focusIn = pyqtSignal(object)

    def eventFilter(self, widget, event):
        if event.type() == QEvent.FocusIn and isinstance(widget, QLineEdit):
            # emit a `focusIn` signal, with the widget as its argument:
            self.focusIn.emit(widget)
        return super(inputFocusFilter, self).eventFilter(widget, event)

为自定义QApplication 类安装此过滤器,以便创建的任何事件都通过过滤器。信号focusIn 连接到一个设置函数,该函数会记住最后一个获得焦点的输入小部件:

class MyApplication(QApplication):
    def __init__(self, *arg, **kwarg):
        super(MyApplication, self).__init__(*arg, **kwarg)

        self._input_focus_widget = None

        self.event_filter = inputFocusFilter()
        self.event_filter.focusIn.connect(self.setInputFocusWidget)
        self.installEventFilter(self.event_filter)

    def setInputFocusWidget(self, widget):
        self._input_focus_widget = widget

    def inputFocusWidget(self):
        return self._input_focus_widget

main() 的第一行使用MyApplication 代替QApplication。现在,HelperWidget.insertString() 中对determineWhichWidgetHadLastFocus() 的调用可以替换为QApplication.instance().inputFocusWidget(),一切正常。

【讨论】:

以上是关于确定在按下按钮之前最后一个焦点的 QWidget的主要内容,如果未能解决你的问题,请参考以下文章

如何在按下按钮之前激活按钮?

在按下标签按钮时删除占位符

如何在按下另一个按钮之前更改按钮状态?

Qt:脚本在按下按钮之前运行

在按下 Arduino 上的特定键盘按钮之前,如何让步进电机运行?

跟踪哪个是特定类型的最后按下按钮的方法