Подключение сигналов из файла пользовательского интерфейса

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

from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QDialog, QMessageBox, QVBoxLayout
from PySide2.QtWidgets import QPushButton, QLineEdit
from PySide2.QtCore import QFile, Slot
from PySide2.QtUiTools import QUiLoader

class Dialog(QDialog):
    def __init__(self, parent = None):
        super(Dialog, self).__init__(parent)
        print('hey')

    def alert(self):
        print('Alert')

if __name__ == '__main__':
    app = QApplication([])

    loader = QUiLoader()
    loader.registerCustomWidget(Dialog)

    ui_file = QFile('alert-quit.ui')
    ui_file.open(QFile.ReadOnly)
    dialog = loader.load(ui_file)
    ui_file.close()

    dialog.show()

Диалог показывает правильно, но я получаю ошибку QObject::connect: No such slot QDialog::alert() и кнопка ничего не делает. (The hey текст тоже не показывается.)

.ui файл содержит определение QDialog с сигналом от кнопки "Оповещение":

Я не уверен в том, что registerCustomWidget() из другого ответа, казалось, что это то, что нужно сделать. К сожалению, официальная документация терпит неудачу, что, как circle.ui содержит.

И официальная документация по загрузке .ui файлы не показывают, как взаимодействовать с элементами, определенными в .ui сам файл.

Как я могу загрузить полный QDialog из .ui файл и получить кнопки для запуска действий в моем коде?

PS: так как я не могу прикрепить .ui файл, вот его XML:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>344</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLineEdit" name="lineEdit"/>
   </item>
   <item>
    <widget class="QPushButton" name="alert_button">
     <property name="maximumSize">
      <size>
       <width>100</width>
       <height>16777215</height>
      </size>
     </property>
     <property name="text">
      <string>Alert</string>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QPushButton" name="quit_button">
     <property name="maximumSize">
      <size>
       <width>100</width>
       <height>16777215</height>
      </size>
     </property>
     <property name="text">
      <string>Quit</string>
     </property>
    </widget>
   </item>
   <item>
    <spacer name="verticalSpacer">
     <property name="orientation">
      <enum>Qt::Vertical</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>20</width>
       <height>40</height>
      </size>
     </property>
    </spacer>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>alert_button</sender>
   <signal>clicked()</signal>
   <receiver>Dialog</receiver>
   <slot>alert()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>91</x>
     <y>30</y>
    </hint>
    <hint type="destinationlabel">
     <x>133</x>
     <y>51</y>
    </hint>
   </hints>
  </connection>
 </connections>
 <slots>
  <slot>alert()</slot>
  <slot>quit()</slot>
 </slots>
</ui>

2 ответа

Как я уже писал в комментариях к моему вопросу, я нашел рабочее решение. Я не уверен на 100%, это правильный (по крайней мере, для части, касающейся ручного редактирования .ui файл), но я получаю диалоговое окно, чтобы показать и вести себя так, как я ожидал.

Сначала я должен был изменить XML в .ui файл и использовать два разных имени класса в .py а также .ui файлы. Я изменил один в .ui быть названным AlertDialog и быть типом Dialog вместо QDialog, Я не смог найти способ изменить тип в Qt Designer.

3,4c3,4
<  <class>Dialog</class>
<  <widget class="QDialog" name="Dialog">
---
>  <class>AlertDialog</class>
>  <widget class="Dialog" name="AlertDialog">
18c18
<     <widget class="QLineEdit" name="lineEdit"/>
---
>     <widget class="QLineEdit" name="text_field"/>
66c66
<    <receiver>Dialog</receiver>
---
>    <receiver>AlertDialog</receiver>

Как вы видите выше, в названии поля ввода также была небольшая опечатка: по этой причине я получил .ui файл вставлен в вопрос!).

В коде Python я просто должен был правильно оформить свою функцию как Slot, Вот полный код:

from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QDialog, QMessageBox
from PySide2.QtCore import QFile, Slot
from PySide2.QtUiTools import QUiLoader

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

    @Slot()
    def alert(self):
        alert = QMessageBox()
        alert.setText(self.text_field.text())
        alert.exec_()


if __name__ == '__main__':
    app = QApplication([])

    loader = QUiLoader()
    loader.registerCustomWidget(Dialog)

    ui_file = QFile('alert-quit.ui')
    ui_file.open(QFile.ReadOnly)
    dialog = loader.load(ui_file)
    ui_file.close()

    dialog.show()
    app.exec_()

Аккуратно, не правда ли? Если кто-нибудь (включая мое будущее я) знает, как избавиться от .ui доработка или иное улучшение этого решения, ваши комментарии очень приветствуются!

Так как я много боролся с Pyside2, я делюсь здесь, как мне это удалось, я не уверен, что это лучший способ продолжить, но он работает для меня.

Сначала вы должны установить pyqt5 с:

pip3 install pyqt5

И при условии, что ваш файл выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>344</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLineEdit" name="lineEdit"/>
   </item>
   <item>
    <widget class="QPushButton" name="alert_button">
     <property name="maximumSize">
      <size>
       <width>100</width>
       <height>16777215</height>
      </size>
     </property>
     <property name="text">
      <string>Alert</string>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QPushButton" name="quit_button">
     <property name="maximumSize">
      <size>
       <width>100</width>
       <height>16777215</height>
      </size>
     </property>
     <property name="text">
      <string>Quit</string>
     </property>
    </widget>
   </item>
   <item>
    <spacer name="verticalSpacer">
     <property name="orientation">
      <enum>Qt::Vertical</enum>
     </property>
     <property name="sizeHint" stdset="0">
      <size>
       <width>20</width>
       <height>40</height>
      </size>
     </property>
    </spacer>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>alert_button</sender>
   <signal>clicked()</signal>
   <receiver>Dialog</receiver>
   <slot>alert()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>91</x>
     <y>30</y>
    </hint>
    <hint type="destinationlabel">
     <x>133</x>
     <y>51</y>
    </hint>
   </hints>
  </connection>
 </connections>
 <slots>
  <slot>alert()</slot>
  <slot>quit()</slot>
 </slots>
</ui>

Вы просто набираете команду:

pyuic5 path_to_your_ui_file -o path_to_the_output_python_file # don't forget the extension .py for the output

Обычно есть выход:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'test.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(344, 300)
        self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
        self.verticalLayout.setObjectName("verticalLayout")
        self.lineEdit = QtWidgets.QLineEdit(Dialog)
        self.lineEdit.setObjectName("lineEdit")
        self.verticalLayout.addWidget(self.lineEdit)
        self.alert_button = QtWidgets.QPushButton(Dialog)
        self.alert_button.setMaximumSize(QtCore.QSize(100, 16777215))
        self.alert_button.setObjectName("alert_button")
        self.verticalLayout.addWidget(self.alert_button)
        self.quit_button = QtWidgets.QPushButton(Dialog)
        self.quit_button.setMaximumSize(QtCore.QSize(100, 16777215))
        self.quit_button.setObjectName("quit_button")
        self.verticalLayout.addWidget(self.quit_button)
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem)

        self.retranslateUi(Dialog)
        self.alert_button.clicked.connect(Dialog.alert)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        self.alert_button.setText(_translate("Dialog", "Alert"))
        self.quit_button.setText(_translate("Dialog", "Quit"))

Все, что вам нужно сделать, это исправить импорт и загрузить сгенерированный файл:

from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import QFile, QObject 
import mainwindow  # if your generated file name is mainWindow.py

class MainDialog(QDialog, mainwindow.Ui_Dialog):

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

app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
Другие вопросы по тегам