在 QLayout PyQT 5 中的 QLabel 类型棋盘上绘制棋子
Posted
技术标签:
【中文标题】在 QLayout PyQT 5 中的 QLabel 类型棋盘上绘制棋子【英文标题】:Drawing chess pieces on a QLabel type chessboard in a QLayout PyQT 5 【发布时间】:2021-08-04 17:43:48 【问题描述】:对于一个学校项目,我正在编写一个国际象棋游戏。我用以下代码制作了第一个 GUI:
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_())
这会呈现以下 GUI::
但是,我的代码存在一个主要缺陷:它无法调整大小。我查看了文档,发现您需要使用布局才能调整大小。这让我尝试了以下代码:
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_())
呈现以下内容 ::
但是当我想添加我的棋子的图像时,我不知道如何将它添加到棋盘上。我继续将它们添加到布局中,在棋盘和表格小部件之间。
有人能给点建议吗?
【问题讨论】:
我建议把棋盘分成几块,每一块都是正方形。在 GridLayout 中正确添加它们,它们之间没有水平或垂直空间。然后使用 addWidget(widget, row, column, alignment) 将棋子添加到所需位置。 【参考方案1】:处理具有固定纵横比的小部件并非易事,必须采取一些预防措施以确保“不兼容”的父尺寸不会妨碍正确显示。
在这种情况下,一种可能的解决方案是为棋盘使用一个小部件,该小部件对所有方块和块使用网格布局。 请注意,QLabel 不是棋盘的好选择,因为它不允许尺寸小于 QPixmap,因此应该将 QWidget 子类化。
诀窍是覆盖resizeEvent()
,忽略基本实现(默认情况下会适应布局的几何形状)并根据宽度和高度之间的最小范围手动设置几何形状。
为了确保布局在行或列为空时具有适当的等间距,必须为整个网格大小调用setRowStretch()
和setColumnStretch()
。
然后,您将片段直接添加到布局中,当您需要移动它们时,您只需创建一个辅助函数,该函数使用 addWidget()
和正确的行/列(它会自动将小部件“移动”到新职位)。
这是一个可能的实现。
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_())
请注意,您还应该考虑使用Graphics View Framework,它提供了更多的控制和功能,对于此类界面非常有用。请注意,它既强大又难以习惯,并且需要很长时间才能理解它的所有方面。
【讨论】:
你好,首先我想原谅自己这么晚才回答。我刚刚测试了你的建议,效果很好!非常感谢! @ArthurCassou 不客气!请记住,如果某个答案解决了您的问题,您应该通过单击其左侧的灰色勾号将其标记为已接受。以上是关于在 QLayout PyQT 5 中的 QLabel 类型棋盘上绘制棋子的主要内容,如果未能解决你的问题,请参考以下文章
试图将 QLayout "" 添加到 QWidget "",它已经有一个布局