#!/usr/bin/env python ############################################################################ # # Copyright (C) 2004-2005 Trolltech AS. All rights reserved. # # This file is part of the example classes of the Qt Toolkit. # # This file may be used under the terms of the GNU General Public # License version 2.0 as published by the Free Software Foundation # and appearing in the file LICENSE.GPL included in the packaging of # self file. Please review the following information to ensure GNU # General Public Licensing requirements will be met: # http://www.trolltech.com/products/qt/opensource.html # # If you are unsure which license is appropriate for your use, please # review the following information: # http://www.trolltech.com/products/qt/licensing.html or contact the # sales department at sales@trolltech.com. # # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # ############################################################################ import sys import random from PyQt4 import QtCore, QtGui import puzzle_rc class PuzzleWidget(QtGui.QWidget): def __init__(self, parent=None): QtGui.QWidget.__init__(self, 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() def dragLeaveEvent(self, event): updateRect = self.highlightedRect self.highlightedRect = QtCore.QRect() self.update(updateRect) event.accept() def dragMoveEvent(self, event): updateRect = self.highlightedRect.unite(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.emit(QtCore.SIGNAL("puzzleCompleted()")) else: self.highlightedRect = QtCore.QRect() event.ignore() def findPiece(self, pieceRect): for i in range(len(self.pieceRects)): if pieceRect == self.pieceRects[i]: return i 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 not (drag.start(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 i in range(len(self.pieceRects)): painter.drawPixmap(self.pieceRects[i], self.piecePixmaps[i]) painter.end() def targetSquare(self, position): return QtCore.QRect(position.x() / 80 * 80, position.y() / 80 * 80, 80, 80) class PiecesList(QtGui.QListWidget): def __init__(self, parent=None): QtGui.QListWidget.__init__(self, parent) self.setDragEnabled(True) self.setViewMode(QtGui.QListView.IconMode) self.setIconSize(QtCore.QSize(60, 60)) self.setSpacing(10) self.setAcceptDrops(True) self.setDropIndicatorShown(True) def addPiece(self, pixmap, location): pieceItem = QtGui.QListWidgetItem(self) pieceItem.setIcon(QtGui.QIcon(pixmap)) pieceItem.setData(QtCore.Qt.UserRole, QtCore.QVariant(pixmap)) pieceItem.setData(QtCore.Qt.UserRole+1, QtCore.QVariant(location)) pieceItem.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled) def dragEnterEvent(self, event): if event.mimeData().hasFormat("image/x-puzzle-piece"): event.accept() else: event.ignore() def dragMoveEvent(self, event): if event.mimeData().hasFormat("image/x-puzzle-piece"): event.setDropAction(QtCore.Qt.MoveAction) event.accept() else: event.ignore() 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() 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).toPoint() 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) if drag.start(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction: self.takeItem(self.row(item)) class MainWindow(QtGui.QMainWindow): def __init__(self, parent=None): QtGui.QMainWindow.__init__(self, parent) self.puzzleImage = QtGui.QPixmap() self.setupMenus() self.setupWidgets() self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)) self.setWindowTitle(self.tr("Puzzle")) def openImage(self, path=QtCore.QString()): fileName = path if fileName.isNull(): fileName = QtGui.QFileDialog.getOpenFileName(self, self.tr("Open Image"), "", "Image Files (*.png *.jpg *.bmp)") if not fileName.isEmpty(): newImage = QtGui.QPixmap() if not newImage.load(fileName): QtGui.QMessageBox.warning(self, self.tr("Open Image"), self.tr("The image file could not be loaded."), QtGui.QMessageBox.Cancel, QtGui.QMessageBox.NoButton) return self.puzzleImage = newImage self.setupPuzzle() def setCompleted(self): QtGui.QMessageBox.information(self, self.tr("Puzzle Completed"), self.tr("Congratulations! You have completed the puzzle!\n" "Click OK to start again."), QtGui.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(self.tr("&File")) openAction = fileMenu.addAction(self.tr("&Open...")) openAction.setShortcut(QtGui.QKeySequence(self.tr("Ctrl+O"))) exitAction = fileMenu.addAction(self.tr("E&xit")) exitAction.setShortcut(QtGui.QKeySequence(self.tr("Ctrl+Q"))) gameMenu = self.menuBar().addMenu(self.tr("&Game")) restartAction = gameMenu.addAction(self.tr("&Restart")) self.connect(openAction, QtCore.SIGNAL("triggered()"), self.openImage) self.connect(exitAction, QtCore.SIGNAL("triggered()"), QtGui.qApp, QtCore.SLOT("quit()")) self.connect(restartAction, QtCore.SIGNAL("triggered()"), self.setupPuzzle) def setupWidgets(self): frame = QtGui.QFrame() frameLayout = QtGui.QHBoxLayout(frame) self.piecesList = PiecesList() self.puzzleWidget = PuzzleWidget() self.connect(self.puzzleWidget, QtCore.SIGNAL("puzzleCompleted()"), self.setCompleted, QtCore.Qt.QueuedConnection) frameLayout.addWidget(self.piecesList) frameLayout.addWidget(self.puzzleWidget) self.setCentralWidget(frame) if __name__ == "__main__": app = QtGui.QApplication(sys.argv) window = MainWindow() window.openImage(QtCore.QString(":/images/example.jpg")) window.show() sys.exit(app.exec_())