Основная проблема, с которой я столкнулся, связана с интеграцией пользовательского интерфейса, разработанного с помощью Qt Designer (или аналогичных инструментов), в приложение Python на основе PySide6. Пользовательский интерфейс включает в себя QMainWindow в качестве главного окна, содержащего QTabWidget для навигации по вкладкам. Предполагается, что каждая вкладка в этом QTabWidget имеет QScrollArea, позволяющую прокручивать содержимое внутри каждой вкладки, что особенно полезно для содержимого, которое выходит за пределы видимой области.
При попытке запустить приложение PySide6 для отображения разработанного пользовательского интерфейса вы столкнулись с проблемой: окно приложения кажется пустым, без отображения каких-либо разработанных элементов пользовательского интерфейса, таких как вкладки, области прокрутки, таблицы, кнопки и т. д. Несмотря на приложение работает без сбоев или явных ошибок, указывающих на невозможность загрузки файла пользовательского интерфейса, ожидаемые компоненты пользовательского интерфейса не отображаются в окне приложения.
Я убедился, что пользовательский интерфейс загружается, и могу получить имена объектов в пользовательском интерфейсе, однако ничего не отображается.
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
from PySide6.QtUiTools import QUiLoader
from PySide6.QtCore import QFile, QIODevice
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractScrollArea, QApplication, QFrame, QGraphicsView,
QHBoxLayout, QHeaderView, QLabel, QLineEdit,
QMainWindow, QPushButton, QScrollArea, QSizePolicy,
QStatusBar, QTabWidget, QTableView, QTableWidget,
QTableWidgetItem, QVBoxLayout, QWidget)
class AppDemo(QMainWindow):
def __init__(self):
super(AppDemo, self).__init__()
self.load_ui()
def load_ui(self):
loader = QUiLoader()
ui_file = QFile('GUI v2.ui')
if not ui_file.open(QIODevice.ReadOnly):
print(f"Cannot open '{ui_file.fileName()}': {ui_file.errorString()}")
sys.exit(-1)
self.ui = loader.load(ui_file, self)
ui_file.close()
if not self.ui:
print("Cannot load UI")
sys.exit(-1)
print("UI loaded successfully")
button = self.ui.findChild(QPushButton, 'br_submit')
if button:
print(button.objectName())
else:
print("PushButton not found.")
self.setCentralWidget(self.ui)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = AppDemo()
window.show()
sys.exit(app.exec())
вот файл .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>1947</width>
<height>3664</height>
</rect>
</property>
<property name = "windowTitle">
<string>MainWindow</string>
</property>
<widget class = "QWidget" name = "centralwidget">
<property name = "minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<widget class = "QTabWidget" name = "tabWidget">
<property name = "geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1911</width>
<height>2231</height>
</rect>
</property>
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name = "minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name = "toolTip">
<string><html><head/><body><p><br/></p></body></html></string>
</property>
<property name = "autoFillBackground">
<bool>true</bool>
</property>
<property name = "currentIndex">
<number>11</number>
</property>
<widget class = "QWidget" name = "tab">
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name = "whatsThis">
<string><html><head/><body><p><br/></p></body></html></string>
</property>
<attribute name = "title">
<string>BR Feasibility Summary</string>
</attribute>
<widget class = "QFrame" name = "br_input">
<property name = "geometry">
<rect>
<x>0</x>
<y>80</y>
<width>311</width>
<height>201</height>
</rect>
</property>
<property name = "frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name = "frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
<widget class = "QFrame" name = "br_Output">
<property name = "geometry">
<rect>
<x>0</x>
<y>340</y>
<width>311</width>
<height>481</height>
</rect>
</property>
<property name = "frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name = "frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
<widget class = "QScrollArea" name = "scrollArea">
<property name = "geometry">
<rect>
<x>-1</x>
<y>-1</y>
<width>1171</width>
<height>861</height>
</rect>
</property>
<property name = "widgetResizable">
<bool>true</bool>
</property>
<widget class = "QWidget" name = "scrollAreaWidgetContents">
<property name = "geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1169</width>
<height>859</height>
</rect>
</property>
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class = "QLabel" name = "label">
<property name = "geometry">
<rect>
<x>0</x>
<y>0</y>
<width>241</width>
<height>51</height>
</rect>
</property>
<property name = "text">
<string><html><head/><body><p><span style=" font-size:16pt; font-weight:700; text-decoration: underline;">BR Feasibility Summary</span></p></body></html></string>
</property>
</widget>
<widget class = "QTableView" name = "br_input_table">
<property name = "geometry">
<rect>
<x>20</x>
<y>60</y>
<width>291</width>
<height>181</height>
</rect>
</property>
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name = "sizeAdjustPolicy">
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents</enum>
</property>
<property name = "alternatingRowColors">
<bool>true</bool>
</property>
<attribute name = "horizontalHeaderVisible">
<bool>true</bool>
</attribute>
</widget>
<widget class = "QPushButton" name = "br_submit">
<property name = "geometry">
<rect>
<x>100</x>
<y>260</y>
<width>111</width>
<height>41</height>
</rect>
</property>
<property name = "text">
<string>Submit</string>
</property>
</widget>
<widget class = "QTableView" name = "br_input_table_3">
<property name = "geometry">
<rect>
<x>20</x>
<y>320</y>
<width>291</width>
<height>451</height>
</rect>
</property>
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name = "sizeAdjustPolicy">
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents</enum>
</property>
</widget>
</widget>
</widget>
</widget>
<widget class = "QWidget" name = "assumptions1Tab">
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name = "title">
<string>Assumptions 1</string>
</attribute>
<widget class = "QScrollArea" name = "scrollAreaAssumtions">
<property name = "geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1531</width>
<height>781</height>
</rect>
</property>
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name = "verticalScrollBarPolicy">
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum>
</property>
<property name = "horizontalScrollBarPolicy">
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum>
</property>
<property name = "sizeAdjustPolicy">
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored</enum>
</property>
<property name = "widgetResizable">
<bool>false</bool>
</property>
<widget class = "QWidget" name = "scrollAreaWidgetContents_2">
<property name = "geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1882</width>
<height>2922</height>
</rect>
</property>
<property name = "sizePolicy">
<sizepolicy hsizetype = "Expanding" vsizetype = "Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class = "QLabel" name = "label_64">
<property name = "geometry">
<rect>
<x>560</x>
<y>2090</y>
<width>142</width>
<height>32</height>
</rect>
</property>
<property name = "maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name = "text">
<string><html><head/><body><p><span style=" font-weight:700;">Energy / Fuel (USD / litre)</span></p></body></html></string>
</property>
</widget>
<widget class = "QTableWidget" name = "assumptionsTableMKLink_Boiklep">
<property name = "geometry">
<rect>
<x>0</x>
<y>850</y>
<width>1861</width>
<height>71</height>
</rect>
</property>
</widget>
<widget class = "QLabel" name = "label_33">
<property name = "geometry">
<rect>
<x>690</x>
<y>2320</y>
<width>258</width>
<height>16</height>
</rect>
</property>
<property name = "text">
<string><html><head/><body><p><span style=" font-weight:700;">OPEX Escalation Value (% / annum from Year 7)</span></p></body></html></string>
</property>
</widget>
<widget class = "QLabel" name = "label_83">
<property name = "geometry">
<rect>
<x>10</x>
<y>2630</y>
<width>108</width>
<height>57</height>
</rect>
</property>
<property name = "sizePolicy">
<sizepolicy hsizetype = "Minimum" vsizetype = "Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name = "text">
<string><html><head/><body><p><span style=" font-size:10pt; font-weight:700;">9) Revenue</span></p></body></html></string>
</property>
<property name = "margin">
<number>20</number>
</property>
</widget>
<widget class = "QLabel" name = "label_21">
<property name = "geometry">
Qt для Python или PySide — очень мощные инструменты, но они могут быть не очень дружелюбны при первом контакте, поскольку необходимо настроить ряд деталей. Мой совет — начать с рабочего примера (из документации или руководства), а затем постепенно адаптировать его к вашим требованиям. Это может показаться большой работой, но, по крайней мере, у вас будет рабочая отправная точка, так что будет гораздо легче найти, в какой момент пользовательский интерфейс сломался.
Можете ли вы открыть свой файл пользовательского интерфейса, который вы разместили здесь, с помощью дизайнера? я не могу
Отвечает ли это на ваш вопрос? PySide2 QUiLoader возвращает пустое окно
Опубликованный вами файл пользовательского интерфейса является неполным.
@musicamante, поэтому я спросил, может ли он открыть это в Designer






ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ Это решение просто заставит ваш код отображать то окно, которое вы ожидаете. Это неправильный способ использования QUiLoader():
попробуйте удалить: window.show() (это пустое окно, которое вы видите/показываете)
от:
if __name__ == '__main__':
app = QApplication(sys.argv)
window = AppDemo()
window.show()
sys.exit(app.exec())
и добавьте после self.setCentralWidget(self.ui) в def load_ui(self) из class AppDemo(QMainWindow):
эта строка:
self.ui.show()
подробнее см. PySide2 QUiLoader возвращает пустое окно:
QUiLoader().load() возвращает виджет как объект, поэтому, если вы присвоите его переменной, он ничего не сделает, вам следует использовать show():
Пример из ссылки выше, принятый ответ:
import sys
from PySide2.QtWidgets import QApplication
from PySide2 import QtUiTools
app = QApplication(sys.argv)
w = QtUiTools.QUiLoader().load("dialog1.ui")
w.show()
sys.exit(app.exec_())
не совсем, теперь вам нужно выяснить, почему закрытие окна не освобождает консоль в Centos
если вы хотите иметь возможность остановить свой скрипт, по крайней мере, в Centos, вам нужно будет сохранить пустое окно, поэтому сохраните строку window.show() и закройте это окно, чтобы завершить ваш скрипт, но лучше используйте другой метод, чтобы загрузите свой пользовательский интерфейс, см.: stackoverflow.com/questions/20115693/…
см. forum.qt.io/topic/132276/… forum.qt.io/topic/132276/how-can-quiloader-load-ui-to-self-and-trigger-closeevenэто тоже здесь
Первоначальное решение является неподходящим и концептуально ошибочным, поскольку оно делает наследование AppDemo QMainWindow совершенно бессмысленным, поскольку этот экземпляр никогда не используется как таковой. В этот момент это будет почти то же самое, что создание подкласса от QObject или даже object. Проблема в том, что файл пользовательского интерфейса уже описывает QMainWindow, который возвращает QUiLoader, но QMainWindow уже существует, а другой не следует устанавливать в качестве центрального виджета.
@musicamante, ты говоришь о forum.qt.io/topic/132276/… или о моем ответе? Я понимаю, что это не тот способ использования QUiLoader, который я объяснил в комментарии выше со ссылкой на stackoverflow.com/questions/20115693/…, но это был единственный простой способ заставить скрипт вопроса работать.
@musicamante удаление строки self.setCentralWidget(self.ui) из .code не меняет поведение ни вопроса, ни ответа.
@ pippo1980 pippo1980 Один из ответов в ветке форума qt объясняет проблему, заключающуюся в том, что PySide и PyQt используют разные способы построения пользовательского интерфейса из файла Designer: PySide всегда создает новый виджет с экземпляром верхнего класса виджетов (например, QMainWindow, в данном случае), в то время как PyQt может настроить существующий виджет (второй параметр loadUi()) с содержимым пользовательского интерфейса. Я никогда не понимал, почему разработчики Qt не сделали то же самое для PySide, поскольку подход PyQt гораздо более непосредственный и интуитивно понятный. Я знаю, что ваше решение «работает», но оно не соответствует реальному AppDemo »
Экземпляр @pippo1980 не используется должным образом, что может привести к неожиданной работе любой последующей реализации, например, если OP попытается напрямую вызвать функции QMainWindow (например, базовый self.close()) или, как в случае с форумом, переопределить обработчики событий. Тот факт, что удаление setCentralWidget() является случайным: loader.load(ui_file, self) создает QMainWindow с self в качестве родительского, но, как и в случае с QDialog, родительский аргумент не делает окно дочерним виджетом; setCentralWidget() фактически делает его дочерним виджетом, только если виджет еще не является дочерним, в противном случае »
@pippo1980 » он будет только пытаться добавить его в макет, предполагая, что это уже дочерний виджет; поскольку load() отношение родитель/потомок уже определено, неловкий результат состоит в том, что окно все еще остается окном, и поэтому вы можете show() его использовать. Если вы измените на loader.load(ui_file), то вызов self.ui.show() не сработает, потому что окно было преобразовано в настоящий дочерний виджет. Хотя это можно рассматривать как ошибку, моменты остаются: 1. почти никогда не следует устанавливать QMainWindow в качестве центрального виджета другого; 2. использование QUiLoader с QMainWindow неприемлемо »
@ pippo1980 » для подкласса QMainWindow. Однако есть возможные обходные пути: 1. создавайте в Дизайнере только центральный виджет (как «форму»), но тогда вы потеряете возможность добавлять панели меню и т. д.; 2. используйте loadUiType(), который возвращает класс, который можно использовать для создания подклассов; 3. подкласс QUILoader, как описано здесь. (Я видел, что вы обновили свой ответ заявлением об отказе от ответственности, хорошо!)
Извините, но вопросы должны быть самостоятельными, и мы не принимаем код на других ресурсах, который может быть недоступен некоторым пользователям или содержание которого может измениться, что сделает вопрос недействительным. Пожалуйста, отредактируйте свой вопрос и предоставьте минимально воспроизводимый пример.