QPushButton.clicked () запускается дважды при автоматическом подключении с использованием формы .ui - PullRequest
0 голосов
/ 02 ноября 2018

Рассмотрим эту настройку:

Основной скрипт, main.py:

import sys
from PyQt5 import uic
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow

class MainWindow(QMainWindow):

    def __init__(self, parent=None):
        super().__init__(parent)

        self.ui = uic.loadUi("mw.ui", self)

    def on_btnFunc_clicked(self):
        print('naked function call')

    @pyqtSlot()
    def on_btnSlot_clicked(self, bool):
        print('slotted function call')

app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())

Форма Qt Designer .ui, mw.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>153</width>
    <height>83</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QPushButton" name="btnFunc">
      <property name="text">
       <string>naked func</string>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="btnSlot">
      <property name="text">
       <string>slotted func</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

Эта установка использует механику автоматической передачи сигналов Qt для привязки нажатий кнопок к соответствующим обратным вызовам. Почему голый обратный вызов вызывается дважды, а один - только один раз, как и предполагалось?

Я нашел это и это , но эти настройки немного отличаются от моих, поскольку я не связываю сигналы вручную и не устанавливаю фильтр событий.

Я думал, что это может происходить из-за того, что сигналы с разными сигнатурами привязаны к одному и тому же слоту, но (если я правильно понимаю) QPushButton имеет только один clicked() сигнал.

Может кто-нибудь, пожалуйста, объясните?

1 Ответ

0 голосов
/ 02 ноября 2018

Прежде всего, если используется механика автопровода Qt для слота сигнала, используется метод QMetaObject::connectSlotsByName(), поэтому такое поведение связано с переводом этой функции из c ++ в Python, в случае C ++ QMetaObject: : функция connectSlotsByName () подключается только к слотам, но в Python она расширена для вызова функций, которые не являются слотами.

Проблема в том, что при нажатии появляется перегруженный сигнал, который в случае C ++ позволяет реализовать с использованием параметра по умолчанию:

void QAbstractButton::clicked(bool checked = false)

но в python 2 должны использоваться подписи:

clicked = QtCore.pyqtSignal([], [bool])

Следовательно, в соединении, установленном PyQt к слоту, он используется к QMetaObject::connectSlotsByName(), который использует QMetaObject объекта, который получает подписи с использованием QMetaMethod, однако, если это не слот, вы не можете получите эту информацию, чтобы соединение было эквивалентно вызову.


В случае @pyqtSlot() иметь следующую подпись:

@pyqtSlot()
def on_btnSlot_clicked(self):
    print('slotted function call')

Соединение, выполненное PyQt, следующее:

self.btnSlot.clicked.connect(self.on_btnSlot_clicked)

но если подпись @pyqtSlot(bool):

@pyqtSlot(bool)
def on_btnSlot_clicked(self, checked):
    print('slotted function call', checked)

Соединение, выполненное PyQt, следующее:

self.btnSlot.clicked[bool].connect(self.on_btnSlot_clicked)

Но в случае, если он подключен к функции, которая не является слотом, он не учитывает эти элементы, поскольку использует QMetaObject, поэтому он будет устанавливать соединения со всеми возможными сигнатурами.

self.btnSlot.clicked[bool].connect(self.on_btnFunc_clicked)
self.btnSlot.clicked.connect(self.on_btnFunc_clicked)

В заключение:

  • Когда используется QMetaObject::connectSlotsByName(...), если он подключен к @pyqtSlot(...), подписи проверяются. Если сигнал подключен к функции, отличной от @pyqtSlot(...), он соединится со всеми возможными сигнатурами, поэтому, если сигнал перегружен n сигнатурами, он будет вызван n раз.

  • Вы должны использовать @pyqtSlot(), чтобы избежать предыдущей проблемы, так как кроме этого у нее есть преимущества для быстроты и экономии ресурсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...