2017-10-12 3 views
1

Ich mag hatte die folgende Funktionalität in meine Qt-Anwendung implementieren:QWidget bestimmen, die zuletzt Fokus vor Taste drücken

  • Benutzer öffnen eine oder mehr ‚Input‘ Widgets (Instanzen einer InputWidget Klasse), das jeweils eine Aufnahme QLineEdit Widget
  • Benutzer öffnet ein 'Helfer' Dialog
  • Benutzer einen Wert in der 'Helfer' Dialog wählt
  • Benutzer drückt die 'Einfügen' QPushButton im 'Helfer' Dialog
  • Der ausgewählte Wert aus der ‚Helfer‘ Dialogeingabe ‚Dialog, der den letzten Fokus hatte, bevor das‚‘in die QLineEdit diesen eingefügt‘ Button‘Einfügen grundsätzlich So

, gedrückt wurde, was ich will, ist Wenn der Benutzer im folgenden Screenshot auf "Einfügen" klickt, sollte die Zeichenfolge "Apple" im fokussierten Eingabedialog erscheinen. Das folgende Codebeispiel funktioniert, nur dass die Zeichenfolge (normalerweise, siehe unten) in die zweite eingefügt wird.

enter image description here

Hier ist das Codebeispiel, das dieses Setup erstellt:

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() 

Der fehlende Teil ist die determineWhichWidgetHadLastFocus() Funktion.

Diese Funktion soll etwas Wunderbares tun, das es ermöglicht, zu bestimmen, welcher 'Eingang' zuletzt der Fokus war. Derzeit durchläuft es die Liste der Top-Level-Widgets von QApplication, aber die Reihenfolge der Top-Level-Widgets spiegelt nicht die Aktivierungsreihenfolge wider (normalerweise, aber scheint nicht immer die Reihenfolge der Erstellung zu sein).

Eine Idee, die mir einfiel, war, einen Ereignisfilter zu installieren, der die FocusIn Ereignisse verfolgt. Das wäre für die Klasse InputWidget in meinem Beispiel einfach, aber funktioniert möglicherweise nicht so gut für meine reale Anwendung, die viele QLineEdit s, QTextEdit s und abgeleiteten Klassen überall hat. Ich würde lieber nicht so gehen.

Irgendwelche anderen Ideen?

Antwort

0

Es stellt sich heraus, dass die Event-Filter-Idee der richtige Weg ist. Was ich tat, wurde zum ersten Mal ein Ereignis Filterklasse erstellen, die ein Signal, wenn das sendende Objekt emittiert ein QLineEdit Objekt:

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) 

Dieser Filter für eine benutzerdefinierte QApplication Klasse installiert ist, so dass jedes Ereignis, das den Filter passiert erstellt wird. Das Signal focusIn wird eine setter Funktion verbunden, die den letzten Eingabe-Widget erinnert mich, dass Fokus erhalten:

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 statt QApplication in der ersten Zeile der main() verwendet. Jetzt kann der Anruf zu determineWhichWidgetHadLastFocus() in HelperWidget.insertString() durch QApplication.instance().inputFocusWidget() ersetzt werden, und alles funktioniert wie beabsichtigt.