在 Python 中使用 PyQt5 创建后退按钮

Creating a back button using PyQt5 in Python

1) 我正在尝试使用 PyQt5 创建一个应用程序。登录成功后,我会进入使用 class Ui_select 创建的 select 页面。这很好用。但是,当我出于某种原因尝试使用后退箭头注销时,即使我先 运行 gui_select.py 文件,页面也会崩溃,就好像它是主文件一样,后退箭头可以正常工作。

2) 我使用 Qt Designer 创建了 Windows 但我试图编辑它们以在不检查文件是否为 main 的情况下工作 ==> if name == "main": 以上 app = QtWidgets.QApplication(sys.argv) 因为我只希望创建登录 window 的代码成为主文件。

*这是登录页面的代码:

from PyQt5 import QtCore, QtGui, QtWidgets
import gui_select
import time, getpass

number = 2

users = {'m': 1} #Test the dictionary tommorow to see if it will work accordingly

class Ui_MainWindow(object):

    def openWindow(self, user_id, password):
        x = users.get(user_id)
        if x == int(password):  # Figure out how to make the dictionary work
            MainWindow.close()
            self.window = QtWidgets.QMainWindow()
            self.ui = gui_select.Ui_select()
            self.ui.setupUi(self.window)
            self.window.show()
        else:
            self.login.setText("Wrong Password, Please Retry")
            time.sleep(1)
            self.login.setText("Login")

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1099, 775)
        MainWindow.setStyleSheet("background-color: rgb(4, 112, 54)\n"
"")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(490, 10, 111, 41))
        font = QtGui.QFont()
        font.setFamily("Broadway")
        font.setPointSize(35)
        self.label_2.setFont(font)
        self.label_2.setStyleSheet("color: rgb(255, 255, 255)")
        self.label_2.setObjectName("label_2")
        self.groupBox_login_info = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox_login_info.setGeometry(QtCore.QRect(390, 517, 274, 171))
        font = QtGui.QFont()
        font.setFamily("Bahnschrift Light Condensed")
        font.setPointSize(14)
        self.groupBox_login_info.setFont(font)
        self.groupBox_login_info.setStyleSheet("color: rgb(255, 255, 255)")
        self.groupBox_login_info.setObjectName("groupBox_login_info")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox_login_info)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.lineEdit_password = QtWidgets.QLineEdit(self.groupBox_login_info)
        self.lineEdit_password.setStyleSheet("color: rgb(255, 255, 255)")
        self.lineEdit_password.setEchoMode(QtWidgets.QLineEdit.Password)
        self.lineEdit_password.setObjectName("lineEdit_password")
        self.gridLayout.addWidget(self.lineEdit_password, 1, 1, 1, 1)
        self.label_3 = QtWidgets.QLabel(self.groupBox_login_info)
        font = QtGui.QFont()
        font.setFamily("Bahnschrift Light Condensed")
        font.setPointSize(18)
        font.setBold(True)
        font.setWeight(75)
        self.label_3.setFont(font)
        self.label_3.setStyleSheet("color: rgb(255, 255, 255)")
        self.label_3.setObjectName("label_3")
        self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1)
        self.label = QtWidgets.QLabel(self.groupBox_login_info)
        font = QtGui.QFont()
        font.setFamily("Bahnschrift Light Condensed")
        font.setPointSize(18)
        font.setBold(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setStyleSheet("color: rgb(255, 255, 255)")
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        self.lineEdit_user_id = QtWidgets.QLineEdit(self.groupBox_login_info)
        self.lineEdit_user_id.setStyleSheet("color: rgb(255, 255, 255)")
        self.lineEdit_user_id.setObjectName("lineEdit_user_id")
        self.gridLayout.addWidget(self.lineEdit_user_id, 0, 1, 1, 1)
        self.login = QtWidgets.QPushButton(self.groupBox_login_info)
        font = QtGui.QFont()
        font.setFamily("Bahnschrift Light Condensed")
        font.setPointSize(14)
        self.login.setFont(font)
        self.login.setObjectName("login")

        self.login.clicked.connect(lambda: self.openWindow(self.lineEdit_user_id.text(), self.lineEdit_password.text()))

        self.gridLayout.addWidget(self.login, 2, 0, 1, 2)
        self.horizontalLayout.addLayout(self.gridLayout)
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(130, 60, 791, 451))
        self.label_4.setMaximumSize(QtCore.QSize(791, 471))
        self.label_4.setText("")
        self.label_4.setPixmap(QtGui.QPixmap("Resource File\Landing page image.png"))
        self.label_4.setObjectName("label_4")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1099, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(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.label_2.setText(_translate("MainWindow", "Blu"))
        self.groupBox_login_info.setTitle(_translate("MainWindow", "Login info:"))
        self.label_3.setText(_translate("MainWindow", "Password:"))
        self.label.setText(_translate("MainWindow", "User ID:"))
        self.login.setText(_translate("MainWindow", "Login"))


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

*这是选项 select 页面的代码:

from PyQt5 import QtCore, QtGui, QtWidgets
import gui_login
import os
from PIL import Image

number = 2

this_dir = os.path.abspath(os.path.dirname(__file__))
some_image = os.path.join(this_dir, 'Resource File', 'Back_arrow_image.png')

class Ui_select(object):

    def openWindow(self):
        select.close()

        self.window = QtWidgets.QMainWindow()
        self.ui = gui_login.Ui_MainWindow()
        self.ui.setupUi(self.window)
        self.window.show()

    def back_arrow(self):
        self.back_button = QtWidgets.QPushButton(self.centralwidget)
        self.back_button.setGeometry(QtCore.QRect(770, 30, 221, 81))
        self.back_button.setObjectName("back_button")
        BlackArrow = Image.open(some_image)
        new_image = BlackArrow.resize((1920, 1920))
        new_image.save("WhiteArrow.png")
        self.back_button.setIcon(QtGui.QIcon("WhiteArrow.png"))
        self.back_button.clicked.connect(lambda: self.openWindow())

    def setupUi(self, select):
        select.setObjectName("select")
        select.resize(1090, 600)
        select.setStyleSheet("background-color: rgb(4, 112, 54)\n"
"")

        self.centralwidget = QtWidgets.QWidget(select)
        self.centralwidget.setObjectName("centralwidget")
        self.set_parameters = QtWidgets.QPushButton(self.centralwidget)
        self.set_parameters.setGeometry(QtCore.QRect(380, 300, 281, 71))
        font = QtGui.QFont()
        font.setFamily("Bahnschrift Light Condensed")
        font.setPointSize(18)
        self.set_parameters.setFont(font)
        self.set_parameters.setStyleSheet("color: rgb(255, 255, 255)")
        self.set_parameters.setObjectName("set_parameters")
        self.set_parameters_2 = QtWidgets.QPushButton(self.centralwidget)
        self.set_parameters_2.setGeometry(QtCore.QRect(380, 390, 281, 71))
        font = QtGui.QFont()
        font.setFamily("Bahnschrift Light Condensed")
        font.setPointSize(18)
        self.set_parameters_2.setFont(font)
        self.set_parameters_2.setStyleSheet("color: rgb(255, 255, 255)")
        self.set_parameters_2.setObjectName("set_parameters_2")
        self.set_parameters_3 = QtWidgets.QPushButton(self.centralwidget)
        self.set_parameters_3.setGeometry(QtCore.QRect(380, 210, 281, 71))
        font = QtGui.QFont()
        font.setFamily("Bahnschrift Light Condensed")
        font.setPointSize(18)
        self.set_parameters_3.setFont(font)
        self.set_parameters_3.setStyleSheet("color: rgb(255, 255, 255)")
        self.set_parameters_3.setObjectName("set_parameters_3")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(470, 60, 111, 41))
        font = QtGui.QFont()
        font.setFamily("Broadway")
        font.setPointSize(35)
        self.label_2.setFont(font)
        self.label_2.setStyleSheet("color: rgb(255, 255, 255)")
        self.label_2.setObjectName("label_2")
        select.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(select)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1090, 26))
        self.menubar.setObjectName("menubar")
        self.menuMenu = QtWidgets.QMenu(self.menubar)
        self.menuMenu.setObjectName("menuMenu")
        select.setMenuBar(self.menubar)
        self.back_arrow()
        self.statusbar = QtWidgets.QStatusBar(select)
        self.statusbar.setObjectName("statusbar")
        select.setStatusBar(self.statusbar)
        self.menubar.addAction(self.menuMenu.menuAction())

        self.retranslateUi(select)
        QtCore.QMetaObject.connectSlotsByName(select)

    def retranslateUi(self, select):
        _translate = QtCore.QCoreApplication.translate
        select.setWindowTitle(_translate("select", "MainWindow"))
        self.set_parameters.setText(_translate("select", "Set Parameters"))
        self.set_parameters_2.setText(_translate("select", "History"))
        self.set_parameters_3.setText(_translate("select", "Auto Detect"))
        self.label_2.setText(_translate("select", "Blu"))
        self.menuMenu.setTitle(_translate("select", "Menu "))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    select = QtWidgets.QMainWindow()
    ui = Ui_select()
    ui.setupUi(select)
    select.show()
    sys.exit(app.exec_())

提前致谢

如果您 运行 您的代码在 shell/prompt 上,您将看到以下错误:

Exception "unhandled NameError"
name 'select' is not defined
File: /tmp/gui_select.py, Line: 14

这是因为 select 仅在第二个文件的 if __name__ 中声明。

存在的重要性__main__

当加载 python 文件时,其主缩进级别中的所有内容 都会被处理,即使在导入脚本时也是如此。
在您的情况下,第二个文件中 __name__ 检查中的内容是 not 执行的(因为对于 that 文件,__name__ 实际上是 gui_select),这意味着整个块将被忽略并且不会创建 select,因此崩溃并出现上述错误。

if __name__ 检查不仅是好的做法,而且通常是强制性的。
如果将该块中的所有内容移动到 外部 if,它将在您从主脚本导入文件后立即执行;结果将是,当您启动第一个脚本时,它将导入第二个脚本,该脚本将被完全执行,因此它将创建 QApplication 实例和 select window,然后立即显示它而不用更进一步,直到 window 关闭; 然后 它将退出程序(由于 sys.exit 调用),根本不显示登录 window。


现在,虽然理论上可以轻松避免该问题,但您的情况存在更大的问题:您修改了 pyuic 生成的文件以创建您的程序。

这是非常气馁的,因为它经常会导致大量的问题和误解(就像在这种情况下),但主要原因是每当你需要修改出于任何原因再次使用 GUI,将现有代码与 pyuic 创建的新文件合并时会遇到严重的麻烦。如官方 guidelines about using Designer 所示(这些文件中的“WARNING”部分建议),这些脚本必须 never 手动修改和应该总是 用作导入。

这是您需要遵循的步骤:

  • 创建一个新脚本,最终复制您为 classes 添加的函数,这样它们就不会被删除,您可以修改它们而无需从头开始编写;
  • 用pyuic再次生成.ui文件(确保第二个window的对象名select 在设计器中);
  • 用这样的东西修改新脚本:
from PyQt5 import QtWidgets
from gui_login import Ui_MainWindow
from gui_select import Ui_select

class LoginWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.login.clicked.connect(self.showSelect)

    def showSelect(self):
        user_id = self.lineEdit_user_id.text()
        password = self.lineEdit_password.text()
        x = users.get(user_id)
        if x == int(password):
            self.close()
            self.selectWindow = SelectWindow()
            self.selectWindow.show()
        else:
            # this is *wrong*, see below
            self.login.setText("Wrong Password, Please Retry")
            time.sleep(1)
            self.login.setText("Login")


class SelectWindow(QtWidgets.QMainWindow, Ui_select):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.back_button.clicked.connect(self.showLogin)

    def showLogin(self):
        self.close()
        self.loginWindow = LoginWindow()
        self.loginWindow.show()


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    loginWindow = LoginWindow()
    loginWindow.show()
    sys.exit(app.exec_())

一些注意事项:

  • Qt已经提供了图片缩放功能,你不需要使用PIL(另外,你不应该每次都保存一张新图片);如果您需要缩放图像,请使用 image = QtGui.QPixmap('path_to_image.png').scaled(width, height, transformMode=QtCore.Qt.SmoothTransformation);
  • QPushButton自动根据其iconSize()属性缩放图标,所以不清楚为什么要将图片缩放到1920x1920;如果要指定更大的尺寸,请使用 self.back_button.setIconSize(QtCore.QSize(width, height));
  • 如果您不需要为连接到信号的函数提供自定义参数,请不要使用 lambda
  • 固定几何形状通常被认为是不好的做法,原因有很多:您在 Designer 上看到的与您在 运行 程序时在屏幕上看到的并不完全相同,而且用户很少会看到在他们身上;此外,如果用户(或 OS)调整 window 的大小,UI 的部分内容将变得不可用;使用 layout managers 代替;
  • 阻塞函数(如第一个文件中的time.sleep(1))应该永远不会在主Qt线程中调用,因为它们不仅阻止交互,而且最重要的是正确GUI update/drawing:事实上,您可能会看到按钮文本 而不是 更新为“密码错误”文本;改为使用 QMessageBox,或连接到更新标签的插槽的 QTimer:
class LoginWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    # ...
    def showSelect(self):
        # ...
        else:
            self.login.setText("Wrong Password, Please Retry")
            self.login.setEnabled(False)
            QtCore.QTimer.singleShot(2000, self.restoreLogin)

    def restoreLogin(self):
        self.login.setText("Login")
        self.login.setEnabled(True)
  • 虽然根据建议的修改,这不再是真正的问题,但您不应将另一个 window/ui 命名为 self.windowself.ui,因为这可能会导致混淆:那些属性应引用当前 class 实例的 window 和 ui,如果它们属于另一个实例,您可能应该用 self.otherWindow 和 [=33= 之类的名称命名它们];这不是程序问题,而是代码问题 reading/reviewing:使用精心选择的名称可以提高阅读和理解能力,这非常重要,即使是您自己的代码;