本篇代码由PtQt4官方example代码升级而来。其实现的功能是将图片分割后随机打散,以供用户拖放拼图。
源代码如下:
#!/usr/bin/env python
## Copyright (C) 2010 Riverbank Computing Limited
import random
from PyQt5 import QtCore, QtGui, QtWidgets
import puzzle_qrc
class PuzzleWidget(QtWidgets.QWidget):
puzzleCompleted = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.piecePixmaps = []
self.pieceRects = []
self.pieceLocations = []
self.highlightedRect = QtCore.QRect()
self.inPlace = 0
self.setAcceptDrops(True)
self.setMinimumSize(400, 400)
self.setMaximumSize(400, 400)
def clear(self):
self.pieceLocations = []
self.piecePixmaps = []
self.pieceRects = []
self.highlightedRect = QtCore.QRect()
self.inPlace = 0
self.update()
def dragEnterEvent(self, event):
if event.mimeData().hasFormat('image/x-puzzle-piece'):
event.accept()
else:
event.ignore()
#ok
def dragLeaveEvent(self, event):
print("leave0")
updateRect = self.highlightedRect
self.highlightedRect = QtCore.QRect()
self.update(updateRect)
event.accept()
print("leave1")
def dragMoveEvent(self, event):
updateRect = self.highlightedRect.united(self.targetSquare(event.pos()))
if event.mimeData().hasFormat('image/x-puzzle-piece') and self.findPiece(self.targetSquare(event.pos())) == -1:
self.highlightedRect = self.targetSquare(event.pos())
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
else:
self.highlightedRect = QtCore.QRect()
event.ignore()
self.update(updateRect)
def dropEvent(self, event):
if event.mimeData().hasFormat('image/x-puzzle-piece') and self.findPiece(self.targetSquare(event.pos())) == -1:
pieceData = event.mimeData().data('image/x-puzzle-piece')
dataStream = QtCore.QDataStream(pieceData, QtCore.QIODevice.ReadOnly)
square = self.targetSquare(event.pos())
pixmap = QtGui.QPixmap()
location = QtCore.QPoint()
dataStream >> pixmap >> location
self.pieceLocations.append(location)
self.piecePixmaps.append(pixmap)
self.pieceRects.append(square)
self.hightlightedRect = QtCore.QRect()
self.update(square)
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
if location == QtCore.QPoint(square.x() / 80, square.y() / 80):
self.inPlace += 1
if self.inPlace == 25:
self.puzzleCompleted.emit()
else:
self.highlightedRect = QtCore.QRect()
event.ignore()
def findPiece(self, pieceRect):
try:
return self.pieceRects.index(pieceRect)
except ValueError:
return -1
def mousePressEvent(self, event):
square = self.targetSquare(event.pos())
found = self.findPiece(square)
if found == -1:
return
location = self.pieceLocations[found]
pixmap = self.piecePixmaps[found]
del self.pieceLocations[found]
del self.piecePixmaps[found]
del self.pieceRects[found]
if location == QtCore.QPoint(square.x() / 80, square.y() / 80):
self.inPlace -= 1
self.update(square)
itemData = QtCore.QByteArray()
dataStream = QtCore.QDataStream(itemData, QtCore.QIODevice.WriteOnly)
dataStream << pixmap << location
mimeData = QtCore.QMimeData()
mimeData.setData('image/x-puzzle-piece', itemData)
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - square.topLeft())
drag.setPixmap(pixmap)
if drag.exec_(QtCore.Qt.MoveAction) != QtCore.Qt.MoveAction:
self.pieceLocations.insert(found, location)
self.piecePixmaps.insert(found, pixmap)
self.pieceRects.insert(found, square)
self.update(self.targetSquare(event.pos()))
if location == QtCore.QPoint(square.x() / 80, square.y() / 80):
self.inPlace += 1
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
painter.fillRect(event.rect(), QtCore.Qt.white)
if self.highlightedRect.isValid():
painter.setBrush(QtGui.QColor("#ffcccc"))
painter.setPen(QtCore.Qt.NoPen)
painter.drawRect(self.highlightedRect.adjusted(0, 0, -1, -1))
for rect, pixmap in zip(self.pieceRects, self.piecePixmaps):
painter.drawPixmap(rect, pixmap)
painter.end()
def targetSquare(self, position):
return QtCore.QRect(position.x() // 80 * 80, position.y() // 80 * 80, 80, 80)
class PiecesList(QtWidgets.QListWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setDragEnabled(True)
self.setViewMode(QtWidgets.QListView.IconMode)
self.setIconSize(QtCore.QSize(60, 60))
self.setSpacing(10)
self.setAcceptDrops(True)
self.setDropIndicatorShown(True)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat('image/x-puzzle-piece'):
event.accept()
else:
event.ignore()
#ok
def dragMoveEvent(self, event):
if event.mimeData().hasFormat('image/x-puzzle-piece'):
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
else:
event.ignore()
print('dragmove')
#ok
def dropEvent(self, event):
if event.mimeData().hasFormat('image/x-puzzle-piece'):
pieceData = event.mimeData().data('image/x-puzzle-piece')
dataStream = QtCore.QDataStream(pieceData, QtCore.QIODevice.ReadOnly)
pixmap = QtGui.QPixmap()
location = QtCore.QPoint()
dataStream >> pixmap >> location
self.addPiece(pixmap, location)
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
else:
event.ignore()
#ok
def addPiece(self, pixmap, location):
pieceItem = QtWidgets.QListWidgetItem(self)
pieceItem.setIcon(QtGui.QIcon(pixmap))
pieceItem.setData(QtCore.Qt.UserRole, pixmap)
pieceItem.setData(QtCore.Qt.UserRole+1, location)
pieceItem.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled)
#ok
def startDrag(self, supportedActions):
item = self.currentItem()
itemData = QtCore.QByteArray()
dataStream = QtCore.QDataStream(itemData, QtCore.QIODevice.WriteOnly)
pixmap = QtGui.QPixmap(item.data(QtCore.Qt.UserRole))
location = item.data(QtCore.Qt.UserRole+1)
dataStream << pixmap << location
mimeData = QtCore.QMimeData()
mimeData.setData('image/x-puzzle-piece', itemData)
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(QtCore.QPoint(pixmap.width()/2, pixmap.height()/2))
drag.setPixmap(pixmap)
print("startDrag1")
if drag.exec_(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction:
print("move action")
self.takeItem(self.row(item))
else:
print("else")
print("startDrag99")
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.puzzleImage = QtGui.QPixmap()
self.setupMenus()
self.setupWidgets()
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed))
self.setWindowTitle("Puzzle")
def openImage(self, path=None):
if not path:
path, filetype = QtWidgets.QFileDialog.getOpenFileName(self, "Open Image", '',
"Image Files (*.png *.jpg *.bmp)")
if path:
newImage = QtGui.QPixmap()
if not newImage.load(path):
QtWidgets.QMessageBox.warning(self, "Open Image", "The image file could not be loaded.", QtWidgets.QMessageBox.Cancel)
return
self.puzzleImage = newImage
self.setupPuzzle()
def setCompleted(self):
QtWidgets.QMessageBox.information(self, "Puzzle Completed",
"Congratulations! You have completed the puzzle!\nClick OK "
"to start again.",
QtWidgets.QMessageBox.Ok)
self.setupPuzzle()
def setupPuzzle(self):
size = min(self.puzzleImage.width(), self.puzzleImage.height())
self.puzzleImage = self.puzzleImage.copy(
(self.puzzleImage.width() - size)/2,
(self.puzzleImage.height() - size)/2, size, size).scaled(400, 400, QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation)
self.piecesList.clear()
for y in range(5):
for x in range(5):
pieceImage = self.puzzleImage.copy(x*80, y*80, 80, 80)
self.piecesList.addPiece(pieceImage, QtCore.QPoint(x,y))
random.seed(QtGui.QCursor.pos().x() ^ QtGui.QCursor.pos().y())
for i in range(self.piecesList.count()):
if random.random() < 0.5:
item = self.piecesList.takeItem(i)
self.piecesList.insertItem(0, item)
self.puzzleWidget.clear()
def setupMenus(self):
fileMenu = self.menuBar().addMenu("&File")
openAction = fileMenu.addAction("&Open...")
openAction.setShortcut("Ctrl+O")
exitAction = fileMenu.addAction("E&xit")
exitAction.setShortcut("Ctrl+Q")
gameMenu = self.menuBar().addMenu("&Game")
restartAction = gameMenu.addAction("&Restart")
openAction.triggered.connect(self.openImage)
exitAction.triggered.connect(QtWidgets.qApp.quit)
restartAction.triggered.connect(self.setupPuzzle)
def setupWidgets(self):
frame = QtWidgets.QFrame()
frameLayout = QtWidgets.QHBoxLayout(frame)
self.piecesList = PiecesList()
self.puzzleWidget = PuzzleWidget()
self.puzzleWidget.puzzleCompleted.connect(self.setCompleted, QtCore.Qt.QueuedConnection)
frameLayout.addWidget(self.piecesList)
frameLayout.addWidget(self.puzzleWidget)
self.setCentralWidget(frame)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.openImage(':/images/example.jpg')
window.show()
sys.exit(app.exec_())
其用到的金丝猴图片保存在资源文件puzzle_qrc.py 中 。也可在程序文件菜单另打开其它任意图片用于拼图游戏。
本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!