我试图用PyQt中的棋子画一个棋盘。因此,我使用的是QtGui.QMainWindow。在QMainWindow中,我放置了一个QtGui.QGridLayout,并在网格中添加了一个QtGui.QGraphicsView作为中心窗口小部件。 QGraphicsView应该将棋盘与64个瓦片和棋子保持为QtGui.QGraphicsItem。
我实现了这个,但现在我的定位有问题。似乎所有东西的位置都被移动或偏移一定量,看起来这个数量取决于QGraphicsItem的位置。瓷砖彼此不相邻,但它们之间有相同大小的瓷砖空间。当我拖放一块时(我已经覆盖了这些功能,因为我想对移动部件进行一些调整:即使鼠标离开电路板也要将它们保留在电路板上,即使抓住它也要将光标置于光标下方在角落里,看起来这件作品移动的距离加倍了。
我制作了gui的截图来说明问题。在第二张图片中,我开始在左上角拖动并标记鼠标和棋子的位置。光标和工件之间的距离随光标位置而变化。 http://i.imgur.com/Y8aJAQK.png http://i.imgur.com/EQsWioa.png
所以这是源代码,请你看看并帮助我:
chessboard.py
import sys
from board import *
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
grid = QtGui.QGridLayout()
grid.setColumnStretch(1,1)
grid.setRowStretch(1,1)
self.board = Board()
grid.addWidget(self.board)
mainWidget = QtGui.QWidget()
mainWidget.setLayout(grid)
self.setCentralWidget(mainWidget)
self.setGeometry(200, 200, 800, 600)
self.setWindowTitle('ChessBoard')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
m = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
board.py
from PyQt4 import QtGui, QtCore, QtSvg
SQHT = 60
WHITE, BLACK = COLORS = range(2)
LIGHT_COL = QtGui.QColor(220,225,235)
DARK_COL = QtGui.QColor(130,150,180)
class Board(QtGui.QGraphicsView):
def __init__(self):
super(Board, self).__init__(None)
self.scene = QtGui.QGraphicsScene(self)
self.setScene(self.scene)
self.svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Creator: CorelDRAW --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xml:space="preserve" width="50mm" height="50mm" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 50 50" id="svg2" sodipodi:version="0.32" inkscape:version="0.45" sodipodi:docname="bn.svg" sodipodi:docbase="/home/fkling/2/v0.1.2reduced/PieceThemes/Merida" inkscape:output_extension="org.inkscape.output.svg.inkscape" sodipodi:modified="true"><metadata id="metadata13"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs id="defs11"><linearGradient inkscape:collect="always" id="linearGradient2165"><stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop2167" /><stop style="stop-color:#ffffff;stop-opacity:0;" offset="1" id="stop2169" /></linearGradient><linearGradient inkscape:collect="always" xlink:href="#linearGradient2165" id="linearGradient2171" x1="21.253071" y1="37.59214" x2="77.641281" y2="37.469292" gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview inkscape:window-height="625" inkscape:window-width="944" inkscape:pageshadow="2" inkscape:pageopacity="0.0" guidetolerance="10.0" gridtolerance="10.0" objecttolerance="10.0" borderopacity="1.0" bordercolor="#666666" pagecolor="#ffffff" id="base" inkscape:zoom="2.2972888" inkscape:cx="119.13229" inkscape:cy="84.28695" inkscape:window-x="259" inkscape:window-y="109" inkscape:current-layer="svg2" showgrid="true" /> <g id="Layer_x0020_1"> <metadata id="CorelCorpID_0Corel-Layer" /> <path fill="#1F1A17" d="M26.178 9.3952c2.5993,0.1694 5.0038,0.8382 7.2221,2.0151 2.2098,1.1684 4.0979,2.6755 5.6557,4.5127 1.0922,1.287 2.1167,2.8448 3.0819,4.6652 0.9737,1.8118 1.7441,3.7422 2.3199,5.7742 0.6604,2.3707 1.0837,4.8514 1.253,7.4592 0.1778,2.5992 0.2625,5.0122 0.2625,7.2305l0 5.4017c0,0 -1.2869,0 -3.8608,0 -2.5654,0 -5.9013,0 -10.0076,0l-16.637 0c-0.1524,0 -0.2201,-0.4064 -0.2117,-1.2107 0.0085,-0.8128 0.0593,-1.4647 0.1609,-1.9643 0.0593,-0.3979 0.2201,-0.9567 0.4657,-1.6848 0.254,-0.7282 0.6604,-1.6087 1.2446,-2.6501 0.2624,-0.5334 0.889,-1.3039 1.8796,-2.3199 0.999,-1.016 2.1336,-2.2013 3.429,-3.539 0.745,-0.762 1.3208,-1.7188 1.7441,-2.8787 0.4233,-1.1515 0.6011,-2.2013 0.5334,-3.1496 -0.6096,0.4995 -1.2785,0.9059 -2.0066,1.2192 -3.5052,1.2531 -6.0452,3.0734 -7.6115,5.4525 -0.1186,0.1524 -0.4911,0.8213 -1.1176,2.0151 -0.3302,0.6265 -0.6181,1.0583 -0.8467,1.2869 -0.3133,0.3133 -0.7705,0.4911 -1.3631,0.525 -0.9229,0.0423 -1.6426,-0.398 -2.159,-1.3462 -0.6943,0.2032 -1.3124,0.2878 -1.8627,0.254 -0.9229,-0.3472 -1.5917,-0.7197 -2.0066,-1.1176 -0.8467,-0.8467 -1.3885,-1.6849 -1.651,-2.5316 -0.254,-0.8466 -0.381,-1.7526 -0.381,-2.7262 0,-1.3886 0.8551,-3.2258 2.5823,-5.5118 2.0151,-2.6247 3.0904,-4.6313 3.2174,-6.0029 0,-0.5927 0.0592,-1.2615 0.1778,-2.0066 0.1016,-0.5165 0.3048,-1.0075 0.618,-1.4901 0.2202,-0.3302 0.3641,-0.5588 0.4318,-0.6774 0.0762,-0.127 0.2117,-0.3132 0.4149,-0.5588 0.1439,-0.2032 0.2709,-0.3556 0.3725,-0.4572 0.0932,-0.11 0.2202,-0.254 0.3726,-0.4402 0.1778,-0.2117 0.4064,-0.4572 0.6942,-0.7451 -0.8805,-2.413 -1.2361,-4.9022 -1.0668,-7.4591 3.2851,1.1684 6.0537,3.0141 8.2804,5.5287 0.5504,-1.8711 1.6256,-3.3867 3.2258,-4.5381 1.3208,0.9228 2.3707,2.1505 3.1496,3.666z" id="path6" /> <path fill="white" d="M 15.6878,17.7857 C 16.0519,17.5994 16.2297,17.5063 16.2297,17.5063 C 16.7292,17.3116 16.8816,16.9475 16.7038,16.4141 C 16.5091,15.923 16.1281,15.7537 15.5608,15.923 C 13.6135,16.6342 12.2673,17.9381 11.5222,19.8431 C 11.4037,20.385 11.5984,20.7575 12.1149,20.9607 C 12.6314,21.1216 12.9785,20.9438 13.1563,20.4104 C 13.2918,20.131 13.3849,19.9447 13.4526,19.8685 C 13.6389,20.0124 13.876,20.114 14.1723,20.1648 C 15.1798,20.3257 15.7725,19.8854 15.9334,18.8271 C 15.9842,18.463 15.8995,18.1159 15.6878,17.7857 L 15.6878,17.7857 z M 11.573,34.5497 C 11.6323,34.3973 11.7424,34.1772 11.8948,33.8808 C 12.1742,33.1866 12.3096,32.7717 12.3096,32.6362 C 12.2842,32.179 12.0387,31.942 11.59,31.942 C 11.2598,31.942 10.8788,32.4161 10.43,33.3559 C 10.3623,33.4914 10.2607,33.6099 10.1337,33.703 C 9.685,34.1687 9.7527,34.5582 10.3284,34.8714 L 10.3284,34.8714 C 10.8618,35.1847 11.2682,35.0831 11.573,34.5497 L 11.573,34.5497 z M 26.2034,25.3464 C 27.3634,23.8224 27.9306,22.1291 27.9137,20.2664 C 27.846,19.7161 27.5327,19.4452 26.9739,19.4452 C 26.2119,19.4452 25.9156,19.7246 26.0764,20.2834 C 26.1272,21.1978 26.0426,21.9513 25.8055,22.544 C 25.4245,23.4838 25.0012,24.1865 24.544,24.6522 C 24.29,25.1517 24.4424,25.5158 24.9927,25.7444 L 24.9927,25.7444 C 25.5176,25.9899 25.924,25.8629 26.2034,25.3464 L 26.2034,25.3464 z M 19.7264,13.2391 C 19.6502,12.6464 19.6672,12.003 19.7772,11.3087 C 18.7866,11.5034 17.8553,11.9691 16.9748,12.6972 C 16.4498,12.9766 16.3228,13.3661 16.6022,13.8656 C 16.8816,14.3736 17.2711,14.4583 17.7706,14.1112 C 18.1178,13.9249 18.4395,13.7556 18.7274,13.6032 C 19.0152,13.4423 19.3454,13.3238 19.7264,13.2391 L 19.7264,13.2391 z M 42.9759,44.6928 C 42.959,44.6928 42.9759,44.244 43.0182,43.3466 C 43.149425,40.239133 43.11367,37.125892 43.0944,34.0163 C 43.0775,31.8065 42.7812,29.6052 42.2054,27.4038 C 41.365664,24.093859 40.08136,20.919205 38.133,18.1074 C 35.498872,14.261975 31.31864,12.073755 26.8469,11.1309 C 26.973151,11.896697 26.87985,12.670913 26.9231,13.4423 C 28.5233,13.9842 30.0388,14.6615 31.4612,15.4743 C 35.702058,18.028667 37.875209,22.749659 38.6579,27.4038 C 39.929701,33.557961 39.110533,38.961296 39.4707,44.6928 L 42.9759,44.6928 L 42.9759,44.6928 z M 9.4394,30.1386 C 9.9136,29.7999 9.9644,29.4104 9.5834,28.9448 C 9.1854,28.5638 8.7536,28.5299 8.271,28.8432 C 7.2635,29.5036 6.7216,30.3756 6.6539,31.4509 C 6.6708,31.9928 7.001,32.2552 7.6276,32.2214 C 8.2202,32.1706 8.5081,31.8658 8.4912,31.2985 C 8.6266,30.7736 8.9399,30.3841 9.4394,30.1386 z " id="path8" style="fill-opacity:1.0;fill:url(#linearGradient2171)" sodipodi:nodetypes="cccccccccccccccsccccccccscccccccccccccccccccccccccccccccccc" /> </g> </svg>'
self.pieces = []
self.bRect = QtCore.QRectF(0,0,8*SQHT,8*SQHT)
self.RANKS = 8
self.FILES = 8
self.setRenderHints(
QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing | QtGui.QPainter.SmoothPixmapTransform)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setDragMode(self.NoDrag)
self.setInteractive(True)
self.setTransformationAnchor(self.NoAnchor)
self.scene.setItemIndexMethod(self.scene.NoIndex)
self.setMouseTracking(True)
self.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
self.drawBoard()
self._drawPiece(0, 0, WHITE, self.svg)
for piece in self.pieces:
piece.setMovable(True)
def isLight(self, row, col):
return (row % 2) == (col % 2)
def drawBoard(self):
for col in range(self.FILES):
for row in range(self.RANKS):
BTile(self, row, col, LIGHT_COL if self.isLight(row, col) else DARK_COL)
def _drawPiece(self, row, col, color, piece):
BPiece(self, row, col, color, piece)
def getBRect(self):
return self.bRect
class BoardElement(QtGui.QGraphicsItem):
#TODO make abstract - how can I do this and keep inheritance from QGraphicsItem?
def __init__(self, board, row, col):
super(BoardElement, self).__init__(None)
self.board = board
x, y = self._rowCol2Pos(row, col)
self.setPos(x,y)
board.scene.addItem(self)
def _rowCol2Pos(self, row, col):
x = col * SQHT
y = row * SQHT
return x, y
def _updatePos(self, x, y):
self.setPos(x,y)
self.board.scene.update()
def _getXYOnBoardCentered(self, mx, my):
rect = self.board.getBRect()
dh = int(SQHT/2)
left = mx - dh
right = mx + dh
top = my - dh
bottom = my + dh
if left < rect.left():
x = rect.left()
elif right > rect.right():
x = rect.right() - SQHT
else:
x = mx - dh
if top < rect.top():
y = rect.top()
elif bottom > rect.bottom():
y = rect.bottom() - SQHT
else:
y = my - dh
return x,y
def _getXYOnBoard(self, mx, my):
rect = self.board.getBRect()
dh = int(SQHT/2)
left = mx - dh
right = mx + dh
top = my - dh
bottom = my + dh
if left < rect.left():
x = rect.left()
elif right > rect.right():
x = rect.right() - SQHT
else:
x = mx
if top < rect.top():
y = rect.top()
elif bottom > rect.bottom():
y = rect.bottom() - SQHT
else:
y = my
return x,y
class BTile(BoardElement):
def __init__(self, board, row, col, sqColor):
super(BTile, self).__init__(board, row, col)
self.sqColor = sqColor
x, y = self._rowCol2Pos(row, col)
self.bRect = QtCore.QRectF(x, y, SQHT, SQHT)
def boundingRect(self):
return self.bRect
def paint(self, painter, option, widget):
painter.setPen(self.sqColor)
painter.setBrush(self.sqColor)
painter.drawRect(self.bRect)
class BPiece(BoardElement):
def __init__(self, board, row, col, color, piece):
super(BPiece, self).__init__(board, row, col)
self.color = color
self.piece = piece
self.svg = piece
self.pixmap = QtSvg.QSvgRenderer(QtCore.QByteArray(self.svg))
self._updateBRect()
self.movable = False
self.setAcceptHoverEvents(True)
self.setZValue(2)
x, y = self._rowCol2Pos(row, col)
self.setPos(x,y)
board.pieces.append(self)
def _updateBRect(self):
self.bRect = QtCore.QRectF(self.pos().x(), self.pos().y(), SQHT, SQHT)
def boundingRect(self):
self._updateBRect()
return self.bRect
def paint(self, painter, option, widget):
self._updateBRect()
self.pixmap.render(painter, self.bRect)
def setMovable(self, movable):
self.movable = movable
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, self.movable)
def hoverMoveEvent(self, event):
if self.movable:
self.setCursor(QtCore.Qt.OpenHandCursor)
self.setFocus()
def mousePressEvent(self, event):
QtGui.QGraphicsItem.mousePressEvent(self, event)
self.setCursor(QtCore.Qt.ClosedHandCursor)
self.setZValue(3)
def mouseMoveEvent(self, event):
pos = event.scenePos()
x,y = self._getXYOnBoardCentered(pos.x(), pos.y())
self._updatePos(x,y)
def mouseReleaseEvent(self, event):
self.setZValue(2)
p = event.pos()
bx,by = self._getXYOnBoard(p.x(), p.y())
x = (bx // SQHT) * SQHT
y = (by // SQHT) * SQHT
self._updatePos(x,y)