使用QPainter画刽子手

using QPainter to draw a hangman

我正在使用 python 并且是 PyQt 的新手(对 python 也有点陌生)。我想创建一个基本的刽子手游戏,我已经弄清楚了所有的控件,除了我不知道如何在按下错误的字母时绘制头部、body 等。我尝试以某种方式使用 QPainter,但它不起作用,经过研究,有人说我必须使用我以前从未听说过的 QPix 贴图,所以我有点困惑。这是我的代码的主要部分(我跳过了为字母配置按钮):

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QInputDialog, QWidget
from PyQt5.QtGui import QPainter, QBrush, QPen
from PyQt5.QtCore import Qt
import re

allowed = re.compile('[a-zA-Z ]')


class Ui_MainWindow(object):
    answer = [] #list of correct charcters they have inputted
    required = '' #string containing the solution
    indicies = {} #indicies mapping the index of each correct character in solution to the actual character
    wrong_count = 0

    def setupUi(self, MainWindow):
        ### sets up all the buttons for letters of the alphabet, lines, etc. I have this figured out.
        self.newgame = QtWidgets.QPushButton(self.centralwidget)
        self.newgame.setGeometry(QtCore.QRect(800, 290, 231, 51))
        font = QtGui.QFont()
        font.setPointSize(20)
        self.newgame.setFont(font)
        self.newgame.setObjectName("newgame")
        self.newgame.setText('New game')
        self.newgame.clicked.connect(self.restart)
        self.newgame.hide()

        self.start = QtWidgets.QPushButton(self.centralwidget)
        self.start.setGeometry(QtCore.QRect(480, 380, 231, 51))
        font = QtGui.QFont()
        font.setPointSize(20)
        self.start.setFont(font)
        self.start.setObjectName("start")
        self.start.setText("Begin")
        self.start.clicked.connect(self.get_name)

        self.answerbox = QtWidgets.QLabel(self.centralwidget)
        self.answerbox.setGeometry(QtCore.QRect(340, 480, 601, 91))
        font = QtGui.QFont()
        font.setPointSize(28)
        self.answerbox.setFont(font)
        self.answerbox.setText("")
        self.answerbox.setObjectName("answerbox")

        self.win = QtWidgets.QLabel(self.centralwidget)
        self.win.setGeometry(QtCore.QRect(340, 400, 601, 91))
        font = QtGui.QFont()
        font.setPointSize(28)
        self.win.setFont(font)
        self.win.setText("")
        self.win.setObjectName("win")

        self.wrongtext = QtWidgets.QLabel(self.centralwidget)
        self.wrongtext.setGeometry(QtCore.QRect(340, 500, 601, 91))
        font = QtGui.QFont()
        font.setPointSize(28)
        self.wrongtext.setFont(font)
        self.wrongtext.setText("Sorry, please type a valid string, without any special characters")
        self.wrongtext.adjustSize()
        self.wrongtext.setObjectName("wrongtext")
        self.wrongtext.hide()

        self.lost = QtWidgets.QLabel(self.centralwidget)
        self.lost.setGeometry(QtCore.QRect(340, 500, 601, 91))
        font = QtGui.QFont()
        font.setPointSize(28)
        self.lost.setFont(font)
        self.lost.setText("Sorry, you lost")
        self.lost.adjustSize()
        self.lost.setObjectName("lost")
        self.lost.hide()

    def get_name(self):
        text = App().text ##prompts user input for word
        if text == '':
            return
        elif allowed.match(text):
            Ui_MainWindow.required = text
            self.answerbox.setText('_ '*len(text))
            self.wrongtext.hide()
            self.check_space(text)
            self.answerbox.adjustSize()
            self.start.hide()
        else:
            self.wrongtext.show()


    def check_space(self, text):
        list1 = list(text)
        for ch in list1:
            if ch == ' ':
                self.revealer(ch)
                Ui_MainWindow.answer.append(ch)

    def clicked(self, ans): ### connected to letter buttons
        if ans.lower() in Ui_MainWindow.required:
            for _ in range(Ui_MainWindow.required.count(ans.lower())):
                Ui_MainWindow.answer.append(ans.lower())
            self.revealer(ans.lower())
            self.checker()
        else:
            Ui_MainWindow.wrong_count += 1
            self.paintEvent()

    def paintEvent(self): ##### this is what doesn't work but I want it to be something like this
        if Ui_MainWindow.wrong_count == 1: ### face
            painter = QPainter(self)
            painter.setPen(QPen(Qt.black, 10, Qt.SolidLine))
            painter.drawEllipse(250, 320, 20, 20)
            return
        if Ui_MainWindow.wrong_count == 2: ### body
            painter = QPainter(self)
            painter.begin(self)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(QtCore.Qt.black)
            painter.setBrush(QtCore.Qt.black)
            painter.drawLine(250, 340, 250, 360)
            return
        if Ui_MainWindow.wrong_count == 3: ###legs
            painter = QPainter(self)
            painter.begin(self)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(QtCore.Qt.black)
            painter.setBrush(QtCore.Qt.black)
            painter.drawLine(250, 360, 260, 370)
            return
        if Ui_MainWindow.wrong_count == 4:
            painter = QPainter(self)
            painter.begin(self)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(QtCore.Qt.black)
            painter.setBrush(QtCore.Qt.black)
            painter.drawLine(250, 360, 240, 370)
            return
        if Ui_MainWindow.wrong_count == 5: ####arms
            painter = QPainter(self)
            painter.begin(self)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(QtCore.Qt.black)
            painter.setBrush(QtCore.Qt.black)
            painter.drawLine(250, 350, 260, 340)
            return
        if Ui_MainWindow.wrong_count == 6:
            painter = QPainter(self)
            painter.begin(self)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(QtCore.Qt.black)
            painter.setBrush(QtCore.Qt.black)
            painter.drawLine(250, 350, 240, 340)
            self.lost.show()
            self.newgame.show()


    def revealer(self, ans):
        index = Ui_MainWindow.required.index(ans) #find the index of a character from answer list in required list
        Ui_MainWindow.indicies[index*2] = ans #assigns index to the character, to be used for displayed
        try:
            while True: ##checks if clicked character appears multiple times in the answer string and appends all of them
                index = Ui_MainWindow.required.index(ans, index+1)
                Ui_MainWindow.indicies[index*2] = ans
        except:
            pass
        displayed = '_ '*len(Ui_MainWindow.required)
        temp_list = list(displayed)
        for index, ch in Ui_MainWindow.indicies.items():
            temp_list[index] = ch
        str2 = ''
        displayed = str2.join(temp_list)
        self.answerbox.setText(displayed)

    def checker(self):
        if sorted(Ui_MainWindow.answer) == sorted(list(Ui_MainWindow.required)):
            self.win.setText("You won!")
            self.newgame.show()
    def restart(self):
        Ui_MainWindow.answer = []
        Ui_MainWindow.required = ''
        Ui_MainWindow.indicies = {}
        Ui_MainWindow.wrong_count = 1
        self.win.setText("")
        self.answerbox.setText("")
        self.newgame.hide()
        self.lost.hide()
        self.start.show()

class App(QWidget):

    def __init__(self):
        super().__init__()
        self.title = 'PyQt5 input dialogs - pythonspot.com'
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(380, 280, 640, 480)
        self.get_name()

    def get_name(self):
        text, ok = QInputDialog.getText(self, 'Text Input Dialog', 'Enter your word:')
        self.text = text
        if text and ok:
            return str(text)

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

抱歉,太长了。基本上,当我 运行 时,一切都按照我想要的方式运行,除非我按错了字母,否则什么也不会发生。它不会抛出错误或任何东西。我试着单独使用 QPainter 来查看它是如何工作的,我让它画了一个圆、一条线等,但不是我想要的一个一个地画。感谢阅读,如有任何答案,我将不胜感激。对不起,如果我的问题布局不好,我昨天才开始使用这个网站,所以还不熟悉它是如何工作的。

paintEvent 应该是您的 QMainWindow 实例的一部分,而不是您用于设置 ui 的对象的一部分。事实上,Ui_MainWindow 中的大多数方法确实属于您的 MainWindow 实例,因此解决此问题的最简单方法可能就是使 Ui_MainWindow 成为 QMainWindow 的子类,即

class Ui_MainWindow(QtWidgets.QMainWindow):
    # You could consider making these instance variables instead of class variables
    answer = [] #list of correct charcters they have inputted
    required = '' #string containing the solution
    indicies = {} #indicies mapping the index of each correct character in solution to the actual character
    wrong_count = 0

    def __init__(self):
        super().__init__()
        self.centralwidget = QWidget(self)
        self.setCentralWidget(self.centralwidget)

        self.setupUi(self)

    def paintEvent(self, event):    # <-- paintEvent receives the paint event as an input parameter
        ....

    # all other methods as before


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    MainWindow = Ui_MainWindow()
    MainWindow.show()
    MainWindow.resize(800,600)
    app.exec()

这至少会调用 paintEvent,但是 paintEvent 的实现仍然存在一些问题。一方面,每次 window 需要重绘时都会自动调用 painEvent。您不应该尝试自己调用此函数。如果您认为您的小部件需要重绘,您可以使用 update 代替。

此外,paintEvent 仅用于绘制小部件,仅用于绘制小部件。这意味着所有与绘制无关的东西,比如显示或隐藏小部件,都应该移到别处。在你的例子中,行

self.lost.show()
self.newgame.show()

应该从 paintEvent 中删除并移动到例如 clicked,例如

def clicked(self, ans): ### connected to letter buttons
    if ans.lower() in Ui_MainWindow.required:
        for _ in range(Ui_MainWindow.required.count(ans.lower())):
            Ui_MainWindow.answer.append(ans.lower())
        self.revealer(ans.lower())
        self.checker()
    else:
        Ui_MainWindow.wrong_count += 1
        if self.wrong_count >= 6:
            self.lost.show()
            self.newgame.show()

最后,由于只检查paintEventwrong_count的相等性,任何时候最多只能画出一个肢体。这可以通过检查 wrong_count 是否大于某个值而不是等于来解决,例如

    def paintEvent(self, event): 
        painter = QPainter(self)
        painter.begin(self)
        if Ui_MainWindow.wrong_count >= 1: ### face
            painter.setPen(QPen(Qt.black, 10, Qt.SolidLine))
            painter.drawEllipse(250, 320, 20, 20)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setPen(QtCore.Qt.black)
        painter.setBrush(QtCore.Qt.black)
        if Ui_MainWindow.wrong_count >= 2: ### body
            painter.drawLine(250, 340, 250, 360)
        if Ui_MainWindow.wrong_count >= 3: ###legs
            painter.drawLine(250, 360, 260, 370)
        if Ui_MainWindow.wrong_count >= 4:
            painter.drawLine(250, 360, 240, 370)
        if Ui_MainWindow.wrong_count >= 5: ####arms
            painter.drawLine(250, 350, 260, 340)
        if Ui_MainWindow.wrong_count >= 6:
            painter.drawLine(250, 350, 240, 340)
        painter.end()