使用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()
最后,由于只检查paintEvent
中wrong_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()
我正在使用 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()
最后,由于只检查paintEvent
中wrong_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()