2014-09-24 4 views
2

У меня есть QPlainTextEdit и вы хотите обработать контент, когда он теряет фокус. Я видел, что могу либо сделать это с помощью события focusChanged, либо с помощью виртуальной функции focusOutEvent.Как перехватить, когда виджет теряет фокус

Я не знаю, как передать параметры с новым синтаксисом (то есть my_app.focusChanged.connect(my_handler), где my_handler - локально определенная функция). Поэтому я попытался работать с виртуальной функцией.

Поскольку интерфейс создан с помощью QT Designer, наследующего QPlainTextEdit, это будет излишним, поэтому я попытался переопределить виртуальную функцию, просто используя my_text_edit.focusOutEvent = my_handler. Это перехватило сообщение так, как я хотел, но он, по-видимому, перезаписал некоторые встроенные функции в QPlainTextEdit, и я получаю артефакты, а именно, курсор из текстового редактирования не исчезает, когда он теряет фокус. Я понял, что я должен как-то называть оригинальное событие и то, что работал для меня было следующее:

В моем __init__ метод у меня есть:

self.original_handler = self.my_text_edit.focusOutEvent 
    self.my_text_edit.focusOutEvent = self.my_handler 

Определение my_handler является:

def my_handler(self, event): 
     self.original_handler(event) 
     # my own handling follows... 

Я в основном реплицирую то, что я ожидаю от библиотеки для меня. Я нахожу это слишком неуклюжим, и я вижу, как во время технического обслуживания он может иметь неприятные последствия. Может ли кто-нибудь предложить более аккуратный способ сделать это? Благодаря!

+0

Так я это делаю, поэтому меня тоже интересуют любые ответы. – ballsatballsdotballs

ответ

4

Лично я никогда не использую стиль обезьяны латание первостепенных виртуальных методов, но правильный способ сохранить первоначальное поведение было бы вызвать метод базового класса непосредственно, как это:

def my_handler(self, event): 
    QtGui.QPlainTextEdit.focusOutEvent(self.my_text_edit, event) 
    # my own handling follows... 

I не понимаю, почему вы не можете использовать сигнал focusChanged. Обработчик для него было бы просто:

def my_handler(self, old, new): 
    if old is self.my_text_edit: 
     print('focus out') 
    elif new is self.my_text_edit: 
     print('focus in') 

Однако, мое собственное предпочтение было бы использовать event filter:

class Window(QtGui.QMainWindow) 
    def __init__(self): 
     ... 
     self.my_text_edit.installEventFilter(self) 

    def eventFilter(self, source, event): 
     if (event.type() == QtCore.QEvent.FocusOut and 
      source is self.my_text_edit): 
      print('eventFilter: focus out') 
      # return true here to bypass default behaviour 
     return super(Window, self).eventFilter(source, event) 

Это гораздо более гибкое решение, которое обеспечивает общий способ справиться с любой event type для любого виджета, который был импортирован с помощью Qt Designer (или, фактически, любого виджета, который вы просто не хотите подкласса).

Существует также возможность widget promotion, которая может быть использована для непосредственной замены виджетов в сгенерированных модулях ui на ваши собственные подклассы. Это позволило бы вам переопределить любые виртуальные методы с помощью наследования, а не обезвреживать отдельные экземпляры. Если вы импортируете несколько виджетов одного типа из Qt Designer, которые имеют множество пользовательских функций, это может обеспечить очень чистое решение.

Простое объяснение того, как сделать продвижение виджета в PyQt, можно найти в моем ответе на: Replace QWidget objects at runtime.

+0

Как я уже упоминал в вопросе, моя проблема с сигналом 'focusChanged' заключается в том, что я не знаю, что такое синтаксис для его регистрации. Как передать два параметра виджета (см. Второй абзац вопроса)? – mapto

+0

@mapto. Я не знаю, что вы подразумеваете под «зарегистрировать».Сигнал выдается QApplication, который автоматически отправит правильные параметры - все, что вам нужно сделать, - это подключить к нему соответствующий обработчик. Если вы хотите самостоятельно выпустить сигнал, вы можете сделать 'my_app.focusChanged.emit (old, new)'. Но я ничего не вижу в вашем вопросе, который подсказывает, зачем вам это нужно. Вы действительно пробовали какие-либо решения в моем ответе? Если да, то почему именно они не работают на вас? – ekhumoro

+0

Я назвал метод базовой линии, как вы предложили, и это сработало для меня. Я также внедрил фильтр событий, и он также работал. – mapto

Смежные вопросы