在 QLayout PyQT 5 中的 QLabel 类型棋盘上绘制棋子 [英] Drawing chess pieces on a QLabel type chessboard in a QLayout PyQT 5

查看:132
本文介绍了在 QLayout PyQT 5 中的 QLabel 类型棋盘上绘制棋子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于一个学校项目,我正在编写一个国际象棋游戏.我用以下代码制作了第一个 GUI:

class chessUI(QtWidgets.QMainWindow):def __init__(self, 当事人):"""super().__init__() ===>倒 QtWidgets.QWidget"""# 初始化 de la fenêtreQtWidgets.QMainWindow.__init__(self)self.setGeometry(0, 0, 1100, 1100)#self.setFixedSize(1100, 1100)self.setWindowTitle(IHM")"""self.layout1 = QtWidgets.QHBoxLayout()self.setLayout(self.layout1)self.layout1.addStretch(1)self.layout2 = QtWidgets.QHBoxLayout()self.layout2.addStretch(1)bout1 = QtWidgets.QPushButton('Nouvelle party', self)bout2 = QtWidgets.QPushButton('Custom', self)self.layout2.addWidget(bout1)self.layout2.addWidget(bout2)self.layout3 = QtWidgets.QHBoxLayout()im = QtGui.QPixmap("echiquier.png")标签 = QtWidgets.QLabel()label.setPixmap(im)self.layout3.addWidget(label)""";""#Définition des différents boutons de l'interfacebout1 = QtWidgets.QPushButton('Nouvelle party', self)bout2 = QtWidgets.QPushButton('Custom', self)bout1.setToolTip('Nouvelle Party')bout2.setToolTip('自定义')回合1.move(1350, 100)bout2.move(1500, 100)bout1.clicked.connect(self.nvl_partie)bout2.clicked.connect(self.custom)""""""self.layout3.addLayout(self.layout3)self.layout1.addLayout(self.layout2)""";self.coups = QTableWidget(1, 3, self)self.coups.setItem(0, 0, QTableWidgetItem(0))self.coups.setItem(0, 1, QTableWidgetItem(0))self.coups.setItem(0, 2, QTableWidgetItem(0))self.coups.setHorizo​​ntalHeaderItem(0, QTableWidgetItem(str(Tour")))self.coups.setHorizo​​ntalHeaderItem(1, QTableWidgetItem(str(Joueur blanc")))self.coups.setHorizo​​ntalHeaderItem(2, QTableWidgetItem(str(Jueur noir")))self.coups.move(1000, 100)self.coups.setFixedSize(500, 840)# Definition des paramètres pour le jeuself.taille = 105 #taille en pixel d'une case de l'échiquierself.partie = partie # partie d'échec associée à la fenêtre自我展示()defpaintEvent(self, event = None):":return: les différentes actions selon les conditions satisfaites"qp = QtGui.QPainter()qp.begin(self)如果 self.partie 不是 None: # Si une partie d'échecs a été associée :self.drawRectangles(qp) # 在 dessine l'échiquierj_blanc = self.partie.joueur_1 # 在 sélectionne les deux joueursj_noir = self.partie.joueur_2如果 self.partie.type 为 None : # Si la partie est un match et qu'on est au débutif self.partie.debut: # si on est au début, on place les pièces de chaque joueur# sur l'échiquierself.partie.debut = 假self.set_pieces(qp, j_blanc)self.set_pieces(qp, j_noir)其他:#Sinon,在 juste les pièces et cela fait la mise# à jour du plateau durant la partieself.set_pieces(qp, j_blanc)self.set_pieces(qp, j_noir)其他:# Le joueur veut étudier différentes strategies selon# des Positions de pièces précises : on le laisse placer# les pièces sur l'échiquier ==>非最终如果 self.partie.debut:self.partie.debut = 假self.choix_pieces(qp)#self.placement_pieces()别的 :self.set_pieces(qp, j_blanc)self.set_pieces(qp, j_noir)qp.end()def drawRectangles(self, qp):":param qp: 文件模块 QtGui.QPainter():return: dessine dans l'IHM l'image du jeu d'échec"taille = self.tailleqp.drawPixmap(100, 100, taille * 8, taille * 8, QtGui.QPixmap("echiquier.png"))def set_pieces(self, qp, joueur):":param qp: 文件模块 QtGui.QPainter():param joueur: le joueur 不要在 veut placer les pièces:返回:"taille = self.taille对于 joueur.pieces_restantes 中的 pi:#Pour chaque pièce restante au joueur,在 trouve l'image associée# et on la place sur l'échiquiery, x = pi.coordsvar = pi.car()c = joueur.couleurqp.drawPixmap(100 + x* taille, (7-y+1) * taille, 0.9*taille,0.9*taille, QtGui.QPixmap("impieces/" + var + "_" + c + ".png"))def disp_cases(self, qp, case):":param case: la liste des cases accesss par la pièce:return: affiche les case accesss par la pièce"# parcourir les case dans la liste cases# sur chaque case, faire un qp.drawPixmap pour y mettre le CERCLE !!!!!!j'insiste sur le CERCLE :)def mousePressEvent(self, event):":param event: paramètre 类型 QMouseEvent:return: permet de générer les déplacements des pièces"y, x = event.x(), event.y() # recupération des coordonnées de la sourie如果 self.partie 不是 None 并且 self.partie.type 是 None 并且 100 <= x <= 1055 和 0 

这将呈现以下 GUI ::

但是,我的代码存在一个主要缺陷:无法调整大小.我在文档中环顾四周,发现您需要使用布局才能使其可调整大小.这让我尝试了以下代码:

导入系统作为进口方从 PyQt5 导入 QtGui、QtCore、QtWidgets从 PyQt5.QtWidgets 导入 *类 random_ui(QtWidgets.QWidget):def __init__(self):super().__init__()self.partie = pa.partie()self.setWindowTitle(QVBoxLayout 示例")外层布局 = QHBoxLayout()左标签 = QLabel()leftlabel.setGeometry(100, 100, 840, 840)leftlabel.setPixmap(QtGui.QPixmap("echiquier.png"))externallayout.addWidget(leftlabel)self.coups = QTableWidget(1, 3, self)self.coups.setItem(0, 0, QTableWidgetItem(0))self.coups.setItem(0, 1, QTableWidgetItem(0))self.coups.setItem(0, 2, QTableWidgetItem(0))self.coups.setHorizo​​ntalHeaderItem(0, QTableWidgetItem(str(Tour")))self.coups.setHorizo​​ntalHeaderItem(1, QTableWidgetItem(str(Joueur blanc")))self.coups.setHorizo​​ntalHeaderItem(2, QTableWidgetItem(str(Jueur noir")))self.coups.move(1000, 100)self.coups.setFixedSize(500, 840)externallayout.addWidget(self.coups)self.setLayout(outerlayout)自我展示()如果 __name__ == __main__":app = QtWidgets.QApplication(sys.argv)窗口 = random_ui()sys.exit(app.exec_())

呈现以下内容::

但是当我想添加我的棋子的图像时,我不知道如何将它添加到棋盘上.我不断将它们添加到布局中,位于棋盘和表格小部件之间.

有人能给点建议吗?

解决方案

处理具有固定纵横比的小部件并不是一件容易的事,必须采取一些预防措施以确保具有不兼容"的部件.父尺寸不会妨碍正确显示.

在这种情况下,一个可能的解决方案是为棋盘使用一个小部件,该小部件对所有的方块使用网格布局.
请注意,QLabel 不是棋盘的好选择,因为它不允许小于 QPixmap 的尺寸,因此应将 QWidget 子类化.

诀窍是覆盖

这是一个可能的实现.

from PyQt5 import QtCore, QtGui, QtWidgets类 Pawn(QtWidgets.QWidget):def __init__(self):super().__init__()self.image = QtGui.QPixmap('whitepawn.png')self.setMinimumSize(32, 32)defpaintEvent(self, event):qp = QtGui.QPainter(self)大小 = min(self.width(), self.height())qp.drawPixmap(0, 0, self.image.scaled(大小,大小,QtCore.Qt.KeepAspectRatio,QtCore.Qt.SmoothTransformation))类板(QtWidgets.QWidget):def __init__(self):super().__init__()布局 = QtWidgets.QGridLayout(self)layout.setSpacing(0)layout.setContentsMargins(0, 0, 0, 0)self.background = QtGui.QPixmap('chessboard.png')对于范围内的 i (8):layout.setRowStretch(i, 1)layout.setColumnStretch(i, 1)对于范围(8)中的col:layout.addWidget(Pawn(), 1, col)def minimumSizeHint(self):返回 QtCore.QSize(256, 256)定义尺寸提示(自我):返回 QtCore.QSize(768, 768)def resizeEvent(self, event):大小 = min(self.width(), self.height())rect = QtCore.QRect(0, 0, size, size)rect.moveCenter(self.rect().center())self.layout().setGeometry(rect)defpaintEvent(self, event):qp = QtGui.QPainter(self)rect = self.layout().geometry()qp.drawPixmap(rect, self.background.scaled(rect.size(),QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))类国际象棋游戏(QtWidgets.QMainWindow):def __init__(self):super().__init__()中央 = QtWidgets.QWidget()self.setCentralWidget(central)布局 = QtWidgets.QHBoxLayout(central)self.board = Board()layout.addWidget(self.board)self.table = QtWidgets.QTableWidget(1, 3)layout.addWidget(self.table)导入系统app = QtWidgets.QApplication(sys.argv)游戏 = ChessGame()游戏节目()sys.exit(app.exec_())

请注意,您还应该考虑使用 Graphics View Framework,它提供了更多的控制和功能,对这种界面非常有用.请注意,它既强大又难以适应,并且需要很长时间才能了解它的所有方面.

For a school project I am programming a chess game. I've made a first GUI with the following code:

class chessUI(QtWidgets.QMainWindow):

    def __init__(self, partie):

        """super().__init__() ===> pour QtWidgets.QWidget"""
        # Initialisation de la fenêtre
        QtWidgets.QMainWindow.__init__(self)
        self.setGeometry(0, 0, 1100, 1100)
        #self.setFixedSize(1100, 1100)
        self.setWindowTitle("IHM")

        """self.layout1 = QtWidgets.QHBoxLayout()
        self.setLayout(self.layout1)
        self.layout1.addStretch(1)

        self.layout2 = QtWidgets.QHBoxLayout()
        self.layout2.addStretch(1)
        bout1 = QtWidgets.QPushButton('Nouvelle partie', self)
        bout2 = QtWidgets.QPushButton('Custom', self)
        self.layout2.addWidget(bout1)
        self.layout2.addWidget(bout2)

        self.layout3 = QtWidgets.QHBoxLayout()
        im = QtGui.QPixmap("echiquier.png")
        label = QtWidgets.QLabel()
        label.setPixmap(im)
        self.layout3.addWidget(label)"""

        """# Définition des différents boutons de l'interface
        bout1 = QtWidgets.QPushButton('Nouvelle partie', self)
        bout2 = QtWidgets.QPushButton('Custom', self)
        bout1.setToolTip('Nouvelle partie')
        bout2.setToolTip('Custom')
        bout1.move(1350, 100)
        bout2.move(1500, 100)
        bout1.clicked.connect(self.nvl_partie)
        bout2.clicked.connect(self.custom)"""

        """self.layout3.addLayout(self.layout3)
        self.layout1.addLayout(self.layout2)"""

        self.coups = QTableWidget(1, 3, self)
        self.coups.setItem(0, 0, QTableWidgetItem(0))
        self.coups.setItem(0, 1, QTableWidgetItem(0))
        self.coups.setItem(0, 2, QTableWidgetItem(0))
        self.coups.setHorizontalHeaderItem(0, QTableWidgetItem(str("Tour")))
        self.coups.setHorizontalHeaderItem(1, QTableWidgetItem(str("Joueur blanc")))
        self.coups.setHorizontalHeaderItem(2, QTableWidgetItem(str("Joueur noir")))
        self.coups.move(1000, 100)
        self.coups.setFixedSize(500, 840)

        # Définition des paramètres pour le jeu
        self.taille = 105                                                   # taille en pixel d'une case de l'échiquier
        self.partie = partie                                                # partie d'échec associée à la fenêtre
        self.show()

    def paintEvent(self, event = None):
        """
        :return: les différentes actions selon les conditions satisfaites
        """
        qp = QtGui.QPainter()
        qp.begin(self)
        if self.partie is not None:                             # Si une partie d'échecs a été associée :
            self.drawRectangles(qp)                             # On dessine l'échiquier
            j_blanc = self.partie.joueur_1                      # On sélectionne les deux joueurs
            j_noir = self.partie.joueur_2
            if self.partie.type is None :                       # Si la partie est un match et qu'on est au début
                if self.partie.debut:                           # si on est au début, on place les pièces de chaque joueur
                                                                # sur l'échiquier
                    self.partie.debut = False
                    self.set_pieces(qp, j_blanc)
                    self.set_pieces(qp, j_noir)
                else:                                           # Sinon, on place juste les pièces et cela fait la mise
                                                                # à jour du plateau durant la partie
                    self.set_pieces(qp, j_blanc)
                    self.set_pieces(qp, j_noir)
            else :                                              # Le joueur veut étudier différentes stratégies selon
                                                                # des positions de pièces précises : on le laisse placer
                                                                # les pièces sur l'échiquier ==> non fini
                if self.partie.debut:
                    self.partie.debut = False
                    self.choix_pieces(qp)
                    #self.placement_pieces()
                else :
                    self.set_pieces(qp, j_blanc)
                    self.set_pieces(qp, j_noir)
        qp.end()

    def drawRectangles(self, qp):
        """
        :param qp: le module QtGui.QPainter()
        :return: dessine dans l'IHM l'image du jeu d'échec
        """
        taille = self.taille
        qp.drawPixmap(100, 100, taille * 8, taille * 8, QtGui.QPixmap("echiquier.png"))

    def set_pieces(self, qp, joueur):
        """
        :param qp: le module QtGui.QPainter()
        :param joueur: le joueur dont on veut placer les pièces
        :return:
        """
        taille = self.taille
        for pi in joueur.pieces_restantes:                      # Pour chaque pièce restante au joueur, on trouve l'image associée
                                                                # et on la place sur l'échiquier
            y, x = pi.coords
            var = pi.car()
            c = joueur.couleur
            qp.drawPixmap(100 + x* taille, (7-y+1) * taille, 0.9*taille,
                                                0.9*taille, QtGui.QPixmap("impieces/" + var + "_" + c + ".png"))

    def disp_cases(self, qp, cases):
        """
        :param cases: la liste des cases accessibles par la pièce
        :return: affiche les cases accessibles par la pièce
        """
        # parcourir les cases dans la liste cases
        # sur chaque case, faire un qp.drawPixmap pour y mettre le CERCLE !!!!! j'insiste sur le CERCLE :)

    def mousePressEvent(self, event):
        """
        :param event: paramètre de type QMouseEvent
        :return: permet de générer les déplacements des pièces
        """
        y, x = event.x(), event.y()      # récupération des coordonnées de la sourie
        if self.partie is not None and self.partie.type is None and 100 <= x <= 1055 and 0 < y < 945:       # si une partie a été associée et que l'on
                                                                                                            # a bien cliqué sur une case de l'échiquier
            if event.button() == QtCore.Qt.LeftButton:
                self.partie.coupencours.append((7-(x//105-1), y//105-1))
                event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, event.pos(), QtCore.Qt.LeftButton,
                                          QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
                QtWidgets.QMainWindow.mousePressEvent(self, event)                                          # appel récursif à la fonction pour avoir la case destination
                                                                                                            # la case départ est stocké dans la liste coupencours
                if len(self.partie.coupencours) == 2:
                    self.bouger_piece(self.partie.coupencours)
                    self.partie.coupencours = []
        """elif self.partie.type == 'Custom' :
            if 1100 <= x <= 1730 and 300 <= y <= 400:
                index = (x-1100)//105
                self.creer_piece()
            elif 1100 <= x <= 1730 and 400 <= y <= 500:
                index = (x - 1100) // 105
                self.creer_piece()"""

    def bouger_piece(self, coup):
        """
        :param coup: tuple regroupant la case de départ et la case d'arrivée
        :return: si le coup est valable, bouge la pièce et met à jour l'IHM
        """
        case_dep, case_arr = coup[0], coup[1]
        self.coups.setItem(self.partie.tour//2, 0, QTableWidgetItem("{}".format(self.partie.tour//2+1)))
        if case_dep != case_arr:
            pi = self.partie.plateau[case_dep[0], case_dep[1]]
            t = self.partie.tour
            if pi != 0:
                if t % 2 == 1 and pi.joueur == self.partie.joueur_2:  # on vérifie que c'est bien à nous de jouer
                    # et que la pièce nous appartient
                    if pi.move(case_arr):
                        row = self.partie.tour // 2
                        s = "{} à {}".format(case_dep, case_arr)
                        self.coups.setItem(row, 2, QTableWidgetItem(s))
                        self.partie.tour += 1
                        self.coups.insertRow(self.coups.rowCount())
                elif t % 2 == 0 and pi.joueur == self.partie.joueur_1:
                    if pi.move(case_arr):
                        s = "{} à {}".format(case_dep, case_arr)
                        row = self.partie.tour // 2
                        self.coups.setItem(row, 1, QTableWidgetItem(s))
                        self.partie.tour += 1
                self.update()                                                                   # on met à jour l'IHM
                

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = chessUI()
    sys.exit(app.exec_())

This renders the following GUI : :

However, my code presents a main flaw: it isn't resizable. I've looked around in the documentation and I've found you need to use layouts in order to have it resizable. This brought me to try out the following code:

import sys
import partie as pa
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import *


class random_ui(QtWidgets.QWidget):

    def __init__(self):
        super().__init__()
        self.partie = pa.partie()
        self.setWindowTitle("QVBoxLayout Example")
        outerlayout = QHBoxLayout()
        leftlabel = QLabel()
        leftlabel.setGeometry(100, 100, 840, 840)
        leftlabel.setPixmap(QtGui.QPixmap("echiquier.png"))
        outerlayout.addWidget(leftlabel)

        self.coups = QTableWidget(1, 3, self)
        self.coups.setItem(0, 0, QTableWidgetItem(0))
        self.coups.setItem(0, 1, QTableWidgetItem(0))
        self.coups.setItem(0, 2, QTableWidgetItem(0))
        self.coups.setHorizontalHeaderItem(0, QTableWidgetItem(str("Tour")))
        self.coups.setHorizontalHeaderItem(1, QTableWidgetItem(str("Joueur blanc")))
        self.coups.setHorizontalHeaderItem(2, QTableWidgetItem(str("Joueur noir")))
        self.coups.move(1000, 100)
        self.coups.setFixedSize(500, 840)
        outerlayout.addWidget(self.coups)
        self.setLayout(outerlayout)
        self.show()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = random_ui()
    sys.exit(app.exec_())

Which renders the following ::

But when I want to add the images of my chesspieces, I cannot find out how to add it on the chessboard. I keep on having them added to the layout, between the chessboard, and the table widget.

Would anyone have some piece of advice?

解决方案

Dealing with widgets that have a fixed aspect ratio is not an easy task, and some precautions must be taken in order to ensure that having an "incompatible" parent size doesn't prevent proper display.

In this case, a possible solution is to use a widget for the chessboard that uses a grid layout for all the squares and pieces.
Note that a QLabel isn't a good choice for the chessboard, as it doesn't allow a size smaller than the QPixmap, so a QWidget should be subclassed instead.

The trick is to override the resizeEvent(), ignore the base implementation (which by default adapts the geometry of the layout) and manually set the geometry based on the minimum extent between width and height.

In order to ensure that the layout has proper equal spacings even when a row or column is empty, setRowStretch() and setColumnStretch() must be called for the whole grid size.

Then, you add the pieces directly to the layout, and whenever you need to move them you can just create a helper function that uses addWidget() with the correct row/column (which will automatically "move" the widget to the new position).

Here is a possible implementation.

from PyQt5 import QtCore, QtGui, QtWidgets

class Pawn(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.image = QtGui.QPixmap('whitepawn.png')
        self.setMinimumSize(32, 32)

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        size = min(self.width(), self.height())
        qp.drawPixmap(0, 0, self.image.scaled(
            size, size, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))


class Board(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QGridLayout(self)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        self.background = QtGui.QPixmap('chessboard.png')

        for i in range(8):
            layout.setRowStretch(i, 1)
            layout.setColumnStretch(i, 1)

        for col in range(8):
            layout.addWidget(Pawn(), 1, col)

    def minimumSizeHint(self):
        return QtCore.QSize(256, 256)

    def sizesHint(self):
        return QtCore.QSize(768, 768)

    def resizeEvent(self, event):
        size = min(self.width(), self.height())
        rect = QtCore.QRect(0, 0, size, size)
        rect.moveCenter(self.rect().center())
        self.layout().setGeometry(rect)

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        rect = self.layout().geometry()
        qp.drawPixmap(rect, self.background.scaled(rect.size(), 
            QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))


class ChessGame(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        central = QtWidgets.QWidget()
        self.setCentralWidget(central)
        layout = QtWidgets.QHBoxLayout(central)
        self.board = Board()
        layout.addWidget(self.board)
        self.table = QtWidgets.QTableWidget(1, 3)
        layout.addWidget(self.table)


import sys
app = QtWidgets.QApplication(sys.argv)
game = ChessGame()
game.show()
sys.exit(app.exec_())

Note that you should also consider using the Graphics View Framework, which provides much more control and features that can be very useful for this kind of interfaces. Be aware that it's as powerful as it's hard to get accustomed to, and it takes a long time to understand all of its aspects.

这篇关于在 QLayout PyQT 5 中的 QLabel 类型棋盘上绘制棋子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆