需要 PyQt5 GUI 结构建议

Posted

技术标签:

【中文标题】需要 PyQt5 GUI 结构建议【英文标题】:PyQt5 GUI Structure advice needed 【发布时间】:2017-02-28 09:54:20 【问题描述】:

我正在为用户界面编写程序。我对 Qt 很陌生,但我很喜欢使用它,特别感谢 Qt 设计师。该程序应如下所示:它有一个带有 2 个选项卡的主窗口。第一个选项卡是登录,带有用户/密码字段和两个按钮,“登录”和“退出”。 “退出”当然会退出应用程序,“登录”会尝试向服务器发送 SOAP 请求以进行登录。如果不成功,将显示一个带有错误的弹出窗口。如果成功,将显示带有成功消息的弹出窗口,并激活并显示 Tab2,用户可以在其中插入一些数据(组合框的值取决于程序连接到服务器后下载的某些表)。这是代码的精简版:

class Ui_PopupError(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(322, 101)
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(60, 20, 221, 16))
        self.label.setObjectName("label")
        self.pushButton = QtWidgets.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(120, 60, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.retranslateUi(Dialog)
        self.pushButton.clicked.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)
    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        self.label.setText(_translate("Dialog", "Wrong Username or Password. Try Again"))
        self.pushButton.setText(_translate("Dialog", "Ok"))
class Ui_PopupSuccess(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(322, 101)
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(60, 20, 221, 16))
        self.label.setObjectName("label")
        self.pushButton = QtWidgets.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(120, 60, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.retranslateUi(Dialog)
        self.pushButton.clicked.connect(Dialog.accept)
        QtCore.QMetaObject.connectSlotsByName(Dialog)
    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        self.label.setText(_translate("Dialog", "Successfully connected."))
        self.pushButton.setText(_translate("Dialog", "Ok"))

class Ui_MainWindow(object):
    def LoginMacro(self):
        Username = self.Usernamefield.text()
        Password = self.Passwordfield.text()
        req = urllib.request.Request("server")
        body = SOAPBody
        response = urllib.request.urlopen(req, body)
        try:
            soup = BeautifulSoup(response.read(), 'html.parser')
            testo = soup.get_text().strip()
            tree = ET.fromstring(testo)
            sessionid = tree.attrib['sessionid']
            self.popup = QtWidgets.QDialog()
            self.popupui = Ui_PopupSuccess()
            self.popupui.setupUi(self.popup)
            self.popup.show()
            self.tabWidget.setCurrentIndex(1)
            self.Recap_Tab.setEnabled(True)
        except:
            self.popup = QtWidgets.QDialog()
            self.popupui = Ui_PopupError()
            self.popupui.setupUi(self.popup)
            self.popup.show()
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(798, 867)
        #MainWindow created with QT Designer (long code...)
        self.Exit.clicked.connect(MainWindow.close)
        self.Login.clicked.connect(self.LoginMacro)
        #This is an example of the value which should feed the combobox:
        self.Handling_Type.addItems(REF_UDF_VALUES['udf_reference_value'][REF_UDF_VALUES['udf_cd']=='Handling Type'])
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        #Retranslate created with Qt designer....(cut)

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_())

我只是想问你这样做是否正确。特别是,我很难理解将执行“真实”事情(与服务器通信)的代码放在哪里。例如,我将登录代码放在 MainWindow 类中,但我不确定这是否有效,因为我需要从该代码中捕获 jsessionid 以便运行另一个下载表的代码。我应该把这个放在哪里?如果我把它放在代码的“尝试”部分,它会告诉我,当它尝试初始化主窗口中的组合框时,它找不到引用表。我知道,我是个烂摊子! :)

谢谢你,很抱歉这个问题太长了!

【问题讨论】:

【参考方案1】:

与您的后端的通信应该与您的 GUI 定义分开。我创建了一个名为API 的单独类来处理请求/响应,并创建一个API 实例供我的QMainWindow 使用。下面是一个示例,我们希望使用用户列表填充 QComboBox

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.api = API()

        users = self.api.get_users()
        self.user_widget = self.ui.userCombo
        self.user_widget.setModel(UserListModel(users, self))

class API:
    def __init__(self):
        self.base_url = 'http://localhost:5000/api'
        self.users_url = '/users'
        self.timeout = 5

    def get_users(self):
        try:
            r = requests.get(self.base_url + self.users_url, timeout=self.timeout)
        except ConnectionError:
            print("Could not connect to API")
            return API.make_default_user_list()

        users = r.json(object_hook=API.api_hook_handler)

        return users

如您所见,MainWindow 不关心对用户数据的请求是成功还是失败。无论哪种方式,api.get_users() 都会返回 MainWindow 可以使用的东西。

【讨论】:

以上是关于需要 PyQt5 GUI 结构建议的主要内容,如果未能解决你的问题,请参考以下文章

Python界面设计——GUI编程之PyQt5

需要 tkinter GUI 的建议 - 按钮定位不一致

无法在 Pyqt5 中更改 qtextedit 的背景边框,有啥建议吗?

OpenGL / C++ / Qt - 需要的建议

项目实战基于Pthon+PyQt5的GUI点名程序(附完整源码)

PyQt5 - CSV导入,显示和滚动建议 - 视图与小部件,QTreeView与其他