PyQt GUI编程——猜数字

本文通过PyQt GUI实现一个简单的小游戏,猜数字。

QTDesinger 和PyUIC的安装和使用参见前文《从零开始 使用PyQt5

一、  在QTDesigner中完成界面设计

0A0B 4个Label 显示当前猜测结果中,有几个数字和位置都正确(A),几个仅数字正确(B);

0 Times 显示目前为止本局已经猜了多少次;

中间四个 LineEdit  是玩家输入数字区;

输入区下面 Label 显示对用户的提示;

Guess按钮  判决猜数字结果。

 

二、 使用PyUIC将上面的界面转换为py文件

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'GuessNumberMainWindow11.ui'
#
# Created by: PyQt5 UI code generator 5.11.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(519, 343)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(120, 130, 291, 71))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.lineEdit = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth())
        self.lineEdit.setSizePolicy(sizePolicy)
        self.lineEdit.setFocusPolicy(QtCore.Qt.ClickFocus)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout.addWidget(self.lineEdit)
        self.lineEdit_2 = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth())
        self.lineEdit_2.setSizePolicy(sizePolicy)
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.horizontalLayout.addWidget(self.lineEdit_2)
        self.lineEdit_3 = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.lineEdit_3.sizePolicy().hasHeightForWidth())
        self.lineEdit_3.setSizePolicy(sizePolicy)
        self.lineEdit_3.setObjectName("lineEdit_3")
        self.horizontalLayout.addWidget(self.lineEdit_3)
        self.lineEdit_4 = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.lineEdit_4.sizePolicy().hasHeightForWidth())
        self.lineEdit_4.setSizePolicy(sizePolicy)
        self.lineEdit_4.setObjectName("lineEdit_4")
        self.horizontalLayout.addWidget(self.lineEdit_4)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(220, 250, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
        self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(210, 80, 121, 31))
        self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
        self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.label_2 = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
        self.label_2.setSizePolicy(sizePolicy)
        self.label_2.setAlignment(QtCore.Qt.AlignCenter)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout_2.addWidget(self.label_2)
        self.label = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.horizontalLayout_2.addWidget(self.label)
        self.label_3 = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
        self.label_3.setSizePolicy(sizePolicy)
        self.label_3.setAlignment(QtCore.Qt.AlignCenter)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_2.addWidget(self.label_3)
        self.label_4 = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
        self.label_4.setSizePolicy(sizePolicy)
        self.label_4.setAlignment(QtCore.Qt.AlignCenter)
        self.label_4.setObjectName("label_4")
        self.horizontalLayout_2.addWidget(self.label_4)
        self.label_5 = QtWidgets.QLabel(self.centralwidget)
        self.label_5.setGeometry(QtCore.QRect(-300, 400, 54, 12))
        self.label_5.setObjectName("label_5")
        self.label_hints = QtWidgets.QLabel(self.centralwidget)
        self.label_hints.setGeometry(QtCore.QRect(160, 220, 211, 16))
        self.label_hints.setAlignment(QtCore.Qt.AlignCenter)
        self.label_hints.setObjectName("label_hints")
        self.horizontalLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget)
        self.horizontalLayoutWidget_3.setGeometry(QtCore.QRect(420, 150, 131, 31))
        self.horizontalLayoutWidget_3.setObjectName("horizontalLayoutWidget_3")
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_3)
        self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.label_guesstimes = QtWidgets.QLabel(self.horizontalLayoutWidget_3)
        self.label_guesstimes.setAlignment(QtCore.Qt.AlignCenter)
        self.label_guesstimes.setObjectName("label_guesstimes")
        self.horizontalLayout_3.addWidget(self.label_guesstimes)
        self.label_7 = QtWidgets.QLabel(self.horizontalLayoutWidget_3)
        self.label_7.setObjectName("label_7")
        self.horizontalLayout_3.addWidget(self.label_7)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 519, 23))
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionshuoming = QtWidgets.QAction(MainWindow)
        self.actionshuoming.setObjectName("actionshuoming")
        self.actionhistroy = QtWidgets.QAction(MainWindow)
        self.actionhistroy.setObjectName("actionhistroy")
        self.menu.addSeparator()
        self.menu.addAction(self.actionshuoming)
        self.menu.addSeparator()
        self.menu.addAction(self.actionhistroy)
        self.menu.addSeparator()
        self.menubar.addAction(self.menu.menuAction())

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "GuessNumber"))
        self.pushButton.setText(_translate("MainWindow", "Guess"))
        self.label_2.setText(_translate("MainWindow", "0"))
        self.label.setText(_translate("MainWindow", "A"))
        self.label_3.setText(_translate("MainWindow", "0"))
        self.label_4.setText(_translate("MainWindow", "B"))
        self.label_5.setText(_translate("MainWindow", "TextLabel"))
        self.label_hints.setText(_translate("MainWindow", "The number is waiting for you :)"))
        self.label_guesstimes.setText(_translate("MainWindow", "0"))
        self.label_7.setText(_translate("MainWindow", "Times"))
        self.menu.setTitle(_translate("MainWindow", "Menu"))
        self.actionshuoming.setText(_translate("MainWindow", "Rules"))
        self.actionhistroy.setText(_translate("MainWindow", "Histroy"))

三、 启动游戏界面

新建Guess.py如下,from GuessNumberMainWindow import Ui_MainWindow导入刚才生成的游戏界面。

# -*- coding: utf-8 -*-
"""猜数字"""

from PyQt5 import QtWidgets   # 导入PyQt5部件
import sys
from GuessNumberMainWindow import Ui_MainWindow

app = QtWidgets.QApplication(sys.argv)  # 建立application对象

first_window = Ui_MainWindow()  # 建立窗体对象

first_window.show()  # 显示窗体

sys.exit(app.exec())  # 运行程序

直接运行Guess.py,提示错误 AttributeError: 'Ui_MainWindow' object has no attribute 'show'  

这是因为 第二步生成的GuessNumberMainWindow.py只定义了界面元素,没有定义自己为主界面,也没有初始化自己。

解决方法,修改GuessNumberMainWindow.py 如下,

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Ui_MainWindow(QMainWindow):

    def __init__(self, parent=None):
        super(Ui_MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.retranslateUi(self)

    def setupUi(self, MainWindow):

……

再次运行Guess.py,成功显示游戏界面

Good Job!

 

四、约束用户输入

猜数字游戏中,用户输入的是0-9的整数,且每个LineEdit中只能输入一个数字。

然后我们要把输入的数字显示的大一点并且在LineEdit中间位置这样看起来舒服一些。这项工作本来应该在QTDesigner中完成,不过在代码中也很简单。

1)约束用户只能输入0-9的整数中的一个,使用QtGui.QIntValidator

int_validator = QtGui.QIntValidator() # validator for lineEdit

int_validator.setRange(0, 9)

self.lineEdit.setValidator(int_validator)

self.lineEdit.setMaxLength(1)  # 只能输入一个数字

2)输入的数字显示的大一点(QtGui.QFont),并显示在LineEdit中间位置

lineEdit_font = QtGui.QFont()
lineEdit_font.setPixelSize(24)
self.lineEdit.setFont(lineEdit_font)
self.lineEdit.setAlignment(QtCore.Qt.AlignCenter) #数字显示在LineEdit中间位置

以上代码添加到GuessNumberMainWindow.py的retranslateUi方法中。

四个LineEdit全部如此处理。运行Guess.py 输入数字,界面如下

五、 Guess按钮被点击后,首先进一步校验用户输入

猜数字游戏中,用户输入的四个数字中不能出现重复,这种情况下判为无效,要求用户重新输入。

修改GuessNumberMainWindow.py文件

1)定义校验函数

def judge_guess(self):
    # 数字依次加入数组(链表)
    print('judge_guess')
    array = [0, 0, 0, 0]
    array[0] = int(self.lineEdit.text())
    array[1] = int(self.lineEdit_2.text())
    array[2] = int(self.lineEdit_3.text())
    array[3] = int(self.lineEdit_4.text())

    # 去除数组中的相同元素,根据数据长度判断是否存在相同数字
    duplicated_array = list(set(array))
    # print(duplicated_array, duplicated_array.__len__())
    if duplicated_array.__len__() != array.__len__():
        # 重复数字的输入不判断猜测结果,提示用户重新输入
        self.label_hints.setText('Do not input duplicated numbers')
        self.label_hints.setStyleSheet("color:red")

2)绑定Guess按钮的clicked信号到judge_guess方法

def signal_slot(self, MainWindow):
        self.pushButton.clicked.connect(self.judge_guess)

     初始化函数中进行信号槽绑定

def __init__(self, parent=None):
    super(Ui_MainWindow, self).__init__(parent)
    self.setupUi(self)
    self.retranslateUi(self)
    generate_aim_array()  # 生成目标数据
    self.signal_slot(self)

运行Guess.py 输入相同数字并点击Guess按钮,界面如下

六、 完成数字比较

新建Rules.py文件实现如下功能

1)自动生成一组四个不同的数字作为目标。

2)比较用户输入的数字和目标数字,得出xAyB的匹配结果,有几个数字和位置都正确(A),几个仅数字正确(B);

# -*- coding: utf-8 -*-
import random
global aim_array, a_num, b_num
aim_array = [0, 0, 0, 0]
a_num = 0
b_num = 0


def generate_aim_array():
   global aim_array, a_num, b_num
   aim_array = random.sample(range(0, 9), 4)  # 0-9之间随机生成四个不相同的数
   a_num = 0
   b_num = 0
   print(aim_array)


def compare_and_judge(array):
   # print('compare_and_judge')
   global a_num
   global b_num
   # print('range(len(aim_array):', range(len(aim_array)))
   # print('range(len(array):', range(len(array)))
   for aim_array_index in range(len(aim_array)):
      for array_index in range(len(array)):
         # print('aim_array:', aim_array_index, aim_array[aim_array_index])
         # print('array:', array_index, array[array_index])
         if aim_array[aim_array_index] == array[array_index]:
            if aim_array_index == array_index:
               a_num = a_num + 1
            else:
               b_num = b_num + 1
            break
   # print('a_num = ', a_num, 'b_num = ', b_num)


def getA():
   global a_num
   return a_num


def setA(a):
   global a_num
   a_num = a


def getB():
   global b_num
   return b_num


def setB(b):
   global b_num
   b_num = b

七、 完成游戏

修改GuessNumberMainWindow.py文件

1)第一次进入游戏时自动生成一组四个不同的数字作为目标。

def __init__(self, parent=None):
……
    self.retranslateUi(self)
    generate_aim_array()  # 生成目标数字
    self.signal_slot(self)

2)点击Guess按钮时判断结果,当比较结果为4A0B时用户输入数字完全正确,本局游戏结束,Guess按钮上的文字变为New Game,玩家点击按钮可以开始下一局游戏。如果比较结果不为4A0B,玩家继续猜数字,直到结果正确。

def judge_guess(self):
……
    if duplicated_array.__len__() != array.__len__():
……
    else:
        # 统计本局猜测的次数
        self.label_guesstimes.setText(str(int(self.label_guesstimes.text()) + 1))
        self.label_hints.setStyleSheet("color:black")

        # 判断数字是否与结果相同,并给出对应提示
        compare_and_judge(array)
        self.label_2.setText(str(getA()))
        self.label_3.setText(str(getB()))
        if getA() == 4:  # 所有数字及位置都正确
            self.label_hints.setText('Good Job!')
            self.pushButton.setText('New Game')
            self.pushButton.disconnect()
            self.pushButton.clicked.connect(self.new_guess)
        else:
            self.label_hints.setText('Come on,you can make it!')
        setA(0)
        setB(0)

def new_guess(self):
    generate_aim_array()
    self.pushButton.setText('Guess')
    self.pushButton.disconnect()
    self.pushButton.clicked.connect(self.judge_guess)
    self.label_guesstimes.setText('0')
    self.label_2.setText('0')
    self.label_3.setText('0')
    self.lineEdit.clear()
    self.lineEdit_2.clear()
    self.lineEdit_3.clear()
    self.lineEdit_4.clear()

3)显示玩家目前为止猜测的次数。代码包含在第二步。

到此游戏就实现完毕了,运行Guess.py,玩一下吧。

八、一点点小优化

玩游戏的时候发现,每次输入数字的时候都要鼠标去点一下对应方框才行,不大舒适。我们把它改成输入一个数字后,光标自动跳转到下一个EditLine,这样玩家就可以连续输入数字了。

修改GuessNumberMainWindow.py文件

1)定义光标跳转方法

def move_cursor(self):
    if self.sender() == self.lineEdit:
        self.lineEdit_2.setFocus()
    elif self.sender() == self.lineEdit_2:
        self.lineEdit_3.setFocus()
    elif self.sender() == self.lineEdit_3:
        self.lineEdit_4.setFocus()

2)绑定editLine输入内容改变(信号)与move_cursor(槽)

def signal_slot(self, MainWindow):
……
        self.lineEdit.textEdited.connect(self.move_cursor)
        self.lineEdit_2.textEdited.connect(self.move_cursor)
        self.lineEdit_3.textEdited.connect(self.move_cursor)

-------------------------------------------------------------------------------------------------------------------------

That's all.Thank you!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值