确定在按下按钮之前具有最后焦点的 QWidget

Determine QWidget that had last focus before button press

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

所以,基本上,我想要的是,如果用户在以下屏幕截图中单击 'Insert',则字符串 '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() 函数。

这个函数应该做一些很棒的事情,让它可以确定哪个 'input' 是最后一个保持焦点的。目前,它从 QApplication 开始遍历顶级小部件列表,但顶级小部件的顺序并不反映激活顺序(它通常但并不总是显示为创建顺序)。

我想到的一个想法是安装一个事件过滤器来跟踪 FocusIn 事件。在我的示例中,这对于 InputWidget class 来说很容易,但对于我的具有许多 QLineEditQTextEdit 和下降 class到处都是。我宁愿不走那条路。

还有其他想法吗?

事实证明,事件过滤器的想法毕竟是可行的方法。我所做的是首先创建一个事件过滤器 class,如果发送对象是 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 class 安装的,因此创建的任何事件都可以通过过滤器。信号 focusIn 连接到一个 setter 函数,该函数记住最后一个获得焦点的输入小部件:

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

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