Я использую PyQT6, PyCharm и Python 3.11.
Очень кратко, я пытаюсь (мысленно) конвертировать из VBA в Python.
Я сделал графический интерфейс в pyQT6, назовем его test.ui. Он имеет несколько кнопок и таблицу. Я конвертирую это в файл Python с помощью утилиты pyuic6. Теперь у меня есть файл Test.py в той же папке. Пока все хорошо.
я добавить
self.mybutton.clicked.connect("buttonpushed")
на скрипт Test.py и отметьте на
def buttonpushed(self):
print("Button Pushed")
Это работает отлично и как и ожидалось. Я намерен создать форму со множеством кнопок, поэтому вместо того, чтобы включать def в файл Test.py, который будет меняться каждый раз, когда я запускаю pyuic6, я перемещаю его в другой файл с именем ExtraBits.py и включаю его в Test.py со строкой
import ExtraBits
Он по-прежнему работает должным образом и печатает слова правильно.
Теперь моя проблема: Я хочу, чтобы эта кнопка скрывала таблицу в моем графическом интерфейсе. Поэтому я добавил
self.mytbl.hide()
к процедуре buttonpushed в Test.py и она работает. Если я добавлю это в процедуру, когда она находится в ExtraBits.py, я все равно получу напечатанные слова, но таблица не скроется, и графический интерфейс закроется.
Я убедил себя, что это связано с тем, что я неправильно ссылаюсь на объект mytable в импортированном файле ExtraBits.py, но не знаю, как это исправить.
Я перепробовал все возможные комбинации, включая имя объекта главного окна и т. д. Я не получаю никаких ошибок, просто hide() не скрывается, и графический интерфейс вылетает, но только если он находится в файле ExtraBits.py.
Переделал файл вминимальные.py и ExtraBits.py, чтобы попытаться подчеркнуть мою проблему.
from PyQt6 import QtCore, QtGui, QtWidgets
import ExtraBits
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.tableWidget = QtWidgets.QTableWidget(parent=self.centralwidget)
self.tableWidget.setGeometry(QtCore.QRect(30, 20, 256, 192))
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(0)
self.tableWidget.setRowCount(0)
self.pushButton = QtWidgets.QPushButton(parent=self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(40, 260, 75, 23))
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(ExtraBits.PushButton)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec())
и ExtraBits.py
def PushButton(self):
print("Button pressed")
self.tableWidget.hide()
Я действительно использую файл pyuic в качестве основного, я следил за видео на YouTube, которое делало это таким образом, и, поскольку оно работало с первого раза, я не стал еще больше усложнять ситуацию. Вы конечно правы, я тоже неправильно сделал разъем в тексте выше. Я напишу минимальный код и посмотрю, что получится...
К сожалению, как и почти в любой области, на YouTube есть множество обучающих программ, которые являются просто дерьмом, опубликованными безрассудными людьми, считающими, что достаточно поиграть с чем-то в течение нескольких дней, чтобы стать «экспертами», полностью игнорируя свои обязанности. такое обучение предполагает отдание приоритета накоплению просмотров и подписчиков вместо ответственного обмена знаниями (что требует времени и терпения, что редко сочетается с ритмом подобных видеорелизов). Фактически, поверх любого файла, сгенерированного Pyuic, есть четкое предупреждение о его редактировании, и я »
» видел людей, явно предлагающих игнорировать это, что является крайне безответственным советом! Это предупреждение (которое, осмелюсь сказать, было добавлено по моему собственному предложению) существует по важной причине, а именно для предотвращения многих проблем, которые обычно возникают у новичков, когда они начинают редактировать такие файлы, что также может быть в вашем случае. . Это распространенная ошибка, понятная неосведомленным новичкам, которые могут не осознавать последствий своих действий в собственном коде, но непростительная для таких «создателей контента», которые игнорируют последствия своих действий для зрителей.
@ musicamante Интернет — это самая большая библиотека, которую вы когда-либо могли пожелать, но в ней нет библиотекарей, которые следили бы за порядком. Я многому узнаю из видеороликов на YouTube, это потрясающий ресурс, но, как вы правильно выразились, контроль над тем, что рассказывается, практически отсутствует, и учащиеся могут легко пойти по неправильному пути. На самом деле я предпочитаю обучение в классе.. < < Я просто старый, наверное :-)
Это хорошая предпосылка. Осведомленность о «цели» преподавания и наши собственные возможности как «делителей знаний» еще более важны. Одна из главных проблем нынешнего «голода славы» заключается в том, что он часто вызывает безответственные ошибочные представления. Не то чтобы их не было раньше, но нынешнее состояние социальных сетей еще больше подчеркивает эту проблему. Настоящая проблема, как всегда было и, вероятно, всегда будет, заключается не в том, как люди учатся, а в том, как те, у кого они учатся, учатся самостоятельно. Хорошее обучение (следовательно, преподавание) демонстрирует обоснование метода, а не решение.
Я думаю, мы согласны с этим, тогда musicamante. Есть какие-нибудь предложения, куда я могу пойти, чтобы узнать, как заставить мой код работать? Я надеялся, что это будет очень простая вещь для новичка - неправильная ссылка на объект, но, поскольку ответов не было, мне интересно, не сложнее ли это. Я прочитал все по той ссылке, которую вы мне дали, но это не пролило никакого света на мою проблему. Ну, если я не пропустил это, я не думаю, что это произошло.






Хотя это типичный случай, когда дубликат изменений QtDesigner будет потерян после редизайна пользовательского интерфейса, возможно, он заслуживает соответствующего ответа.
Проблема заключается в том, что все, что делается в ExtraBits, не имеет абсолютно никакой ссылки на контекст, из которого оно вызывается.
В частности, ваша попытка использовать self ошибочна:
def PushButton(self):
print("Button pressed")
self.tableWidget.hide()
Это по двум причинам:
self на самом деле является соглашением; на самом деле это может быть любое допустимое имя Python, и его цель — дать ссылку на сам экземпляр, и оно неявно задается и устанавливается, когда функция в классе вызывается из ее экземпляра, что делает ее методом; см. этот пост по теме, чтобы узнать цель self;self не то, что вы думаете: на самом деле это аргумент сигнала, который в случае QAbstractButton.clicked является его проверенным состоянием (по умолчанию False);Вышеупомянутое происходит потому, что PushButton() — это стандартная функция, а не метод экземпляра, поэтому для нее нет предопределенного self аргумента, указывающего экземпляр: это функция, экземпляра нет.
Учти это:
def someFunction(self, *args):
print('hello', self)
class Test(object):
def someFunction(self, *args):
print('hello', self)
someFunction('world')
Test().someFunction('world')
Хотя функции вызываются с одним и тем же аргументом, стандартная функция на самом деле выведет hello world, а вторая, являющаяся методом экземпляра, выведет hello, за которым следует представление адреса памяти экземпляра Test() (что-то вроде <__main__.Test object at 0xdeadbeef>).
Таким образом, при использовании сигналов с аргументами любой позиционный аргумент, который принимает функция, устанавливается со значениями аргументов сигнала.
Измените свою функцию на следующее:
def PushButton(self):
print(self)
И оно напечатается False. Это последняя проблема вашего кода: поскольку self является логическим значением, у него, очевидно, нет атрибута tableWidget. Это вызывает неуправляемое исключение AttributeError, что приводит к сбою программы.
Это также одна из основных причин, по которой программы всегда следует тестировать вне IDE (поскольку они иногда неспособны обеспечить полную обратную трассировку) и в контексте, который показывает выходные данные отладки: если вы запустили исходный код в терминале или подскажите, вы бы сразу увидели это исключение.
Итак, как это решить?
Одним из вариантов может быть использование лямбды и «отправка» экземпляра в качестве аргумента функции:
self.pushButton.clicked.connect(lambda: ExtraBits.PushButton(self))
Хотя этот подход технически обоснован, он сопряжен с важными проблемами; среди них:
Это может быть приемлемо для одного случая, но как только программа усложнится, все это сделает код излишне сложным и трудным для чтения, а также вызовет больше проблем при отладке.
На самом деле, всего этого можно легко избежать, если использовать правильный подход, который заключается в том, чтобы никогда не редактировать файлы pyuic, а вместо этого следовать официальным рекомендациям по использованию Designer: вместо этого следует использовать настоящий, отдельный основной скрипт, с пьюическими классами, которые в конечном итоге были импортированы в него.
Используйте следующий код в качестве основного сценария вашей программы и перестройте файл пользовательского интерфейса с помощью pyuic (в данном случае я назвал его TestUi.py, что и используется для второго оператора import):
from PyQt6.QtWidgets import *
from TestUi import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.buttonClicked)
def buttonClicked(self):
self.tableWidget.hide()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Я настоятельно рекомендую вам потратить время на изучение фундаментальных аспектов ООП, включая классы, экземпляры и то, как работают их члены и методы.
Некоторые надежные руководства по PyQt можно найти в официальной вики Python и на действующих веб-сайтах ресурсов Python, таких как realpython.com и pythonguis.com.
Спасибо за подробный и очень полезный ответ. Это не только работает (что всегда хорошо :-)) но и привело к тому, что я стал смотреть на это по-другому. Это кажется очень разумным подходом к тому, что я пытался сделать. В диких землях YouTube, похоже, также ведутся дебаты о том, чтобы оставить формат пользовательского интерфейса и импортировать или преобразовать .UI в .PY, как мы это сделали здесь. Я собираюсь немного почитать. Еще раз спасибо, музыкант.
@perfo Всегда пожалуйста. Что касается выбора между файлами pyuic и динамической загрузкой пользовательского интерфейса, то это зависит от многих факторов, и оба варианта имеют свои плюсы и минусы. Например, подход pyuic работает быстрее во время выполнения, поскольку после импорта он ведет себя так же, как любой другой класс Python, а загрузка файла пользовательского интерфейса требует его анализа каждый раз, когда создается новый экземпляр. OTOH, использование файлов пользовательского интерфейса является более быстрым, поскольку вам не нужно заново создавать файлы Python для любых изменений, внесенных в Designer (иногда вы просто об этом забываете). Если производительность загрузки не является проблемой, я обычно предпочитаю (и предлагаю) подход с пользовательским интерфейсом.
Понял, спасибо
Это сигнальное соединение недействительно, поскольку в нем не должно быть кавычек. Затем, пожалуйста, предоставьте правильный минимально воспроизводимый пример вашей попытки, хотя у меня сложилось впечатление, что вы пытаетесь использовать сгенерированный pyuic файл в качестве основного сценария, в то время как общий подход совершенно противоположный (см. официальные рекомендации по использованию Designer).