используя QPainter для рисования палача - PullRequest
0 голосов
/ 05 апреля 2020

Я использую python и я новичок в PyQt (и немного новичок в python). Я хочу создать основную игру c палач, и у меня есть все элементы управления, за исключением того, что я не знаю, как нарисовать голову, тело и т. Д. c, когда нажата неправильная буква. Я пытался как-то использовать 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 отдельно, чтобы увидеть, как он работает, и я заставил его нарисовать круг, линию и т. Д. c, но не один за другим, как я хочу. Спасибо за чтение, и я буду благодарен за любые ответы. Извините, если мой макет для моего вопроса был плохим, я начал использовать этот сайт только вчера, поэтому пока не знаю, как все работает.

1 Ответ

0 голосов
/ 05 апреля 2020

paintEvent должен быть частью вашего QMainWindow экземпляра, а не объекта, который вы используете для настройки пользовательского интерфейса. Фактически, большинство методов в 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. Например, 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()

Наконец, так как вы проверяете только равенство wrong_count в paintEvent, в любой момент времени может быть нарисована не более одной конечности. Это можно исправить, проверив, больше ли 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()
...