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

Posted

技术标签:

【中文标题】在 Python 中使用 PyQt5 创建后退按钮【英文标题】:Creating a back button using PyQt5 in Python 【发布时间】:2020-10-12 17:12:34 【问题描述】:

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

2) 我使用 Qt Designer 创建了 Windows,但我试图编辑它们以使其正常工作,而不使用检查文件是否为 main==> if name == "ma​​in": 上面 app = QtWidgets.QApplication(sys.argv) 因为我只希望创建登录窗口的代码是主文件。

*这是登录页面的代码:

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

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

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

提前致谢

【问题讨论】:

【参考方案1】:

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

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

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

存在的重要性__main__

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

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

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

强烈不鼓励这样做,因为它通常会导致大量问题和误解(就像在这种情况下),但主要原因是每当您需要再次修改 GUI 时原因,将现有代码与 pyuic 创建的新文件合并会遇到严重的麻烦。正如关于using Designer 的官方指南所示(以及这些文件中的“警告”部分建议),这些脚本必须永远手动修改,并且应该始终使用作为进口。

这是您需要遵循的步骤:

创建一个新脚本,最终复制您为类添加的函数,这样它们就不会被删除,您可以修改它们而无需从头开始编写它们; 用pyuic再次生成.ui文件(确保第二个窗口的对象名在Designer中是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 上看到的与运行程序时在屏幕上看到的不完全一样,而且用户在他们的屏幕上看到的也很少;此外,如果用户(或操作系统)调整窗口大小,部分 UI 将变得不可用;请改用layout managers; 阻塞函数(如第一个文件中的time.sleep(1))应该永远在主Qt线程中调用,因为它们不仅阻止交互,而且最重要的是阻止正确的GUI更新/绘图:事实上,您可能会看到按钮文本没有更新为“密码错误”文本;改用 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)
虽然建议的修改不再是真正的问题,但您不应将其他窗口/ui 命名为 self.windowself.ui,因为这可能会导致混淆:这些属性应引用窗口和 ui对于当前类实例,如果它们属于另一个实例,您可能应该将它们命名为 self.otherWindowself.otherUi;这不是程序问题,而是代码阅读/审查问题:使用精心挑选的名称可以提高阅读和理解能力,即使是您自己的代码,这一点也非常重要;

【讨论】:

以上是关于在 Python 中使用 PyQt5 创建后退按钮的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Python、PyQt5 和 Pyinstaller 修复未正确显示的按钮

如何在 python 中使用 pyqt5 显示 2 个按钮和 2 个标签?

如何在 PyQt5 GUI python 中单击按钮时绘制方形图

Python PyQt5 - 向表中添加行

Python 3.5,单独窗口中的 pyqt5 进度条 gui

将python函数作为arg传递给PyQt5中的函数