返回顶部

PyQt5 Demo

PyQt 模拟时钟 AnalogClock:

from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication,QWidget,QLabel

class AnalogClock(QWidget):
    hourHand = QtGui.QPolygon([
        QtCore.QPoint(10, 8),
        QtCore.QPoint(-10, 8),
        QtCore.QPoint(0, -60)
    ])
    minuteHand = QtGui.QPolygon([
        QtCore.QPoint(8, 8),
        QtCore.QPoint(-8, 8),
        QtCore.QPoint(0, -70)
    ])
    secondHand = QtGui.QPolygon([
        QtCore.QPoint(4, 8),
        QtCore.QPoint(-4, 8),
        QtCore.QPoint(0, -90)
    ])
    hourColor = QtGui.QColor(255, 0, 0)
    minuteColor = QtGui.QColor(0, 255, 0)
    secondColor = QtGui.QColor(0, 0, 255)

    def __init__(self):
        super().__init__()
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.update)
        timer.start(1000)
        self.setWindowTitle("Analog Clock")
        self.resize(200, 200)

    def paintEvent(self, event):
        side = min(self.width(), self.height()) # 最小边
        time = QtCore.QTime.currentTime()  # 获取系统当前时间

        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)  # 抗锯齿
        painter.translate(self.width() / 2, self.height() / 2) # 坐标位置
        painter.scale(side / 300.0, side / 300.0) # 缩放时 的比例

        painter.setPen(QtGui.QColor(0, 0, 0))
        painter.drawEllipse(-100, -100, 200, 200)  # 画圆。参数是外接矩形左上点和长宽
        # painter.drawEllipse(-10,-10,20,20)

        painter.setPen(AnalogClock.hourColor)
        for i in range(12):  # 整点刻度
            painter.drawLine(88, 0, 96, 0)
            painter.rotate(30.0)
        painter.setPen(AnalogClock.minuteColor)
        for j in range(60):  # 小刻度
            if (j % 5) != 0:
                painter.drawLine(92, 0, 96, 0)
            painter.rotate(6.0)

        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(AnalogClock.hourColor)

        painter.save()
        painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)))
        painter.drawConvexPolygon(AnalogClock.hourHand) # 画三角形
        painter.restore()

        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(AnalogClock.minuteColor)


        painter.save()
        painter.rotate(6.0 * (time.minute() + time.second() / 60.0))
        painter.drawConvexPolygon(AnalogClock.minuteHand)
        painter.restore()

        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(AnalogClock.secondColor)

        painter.save()
        painter.rotate(6.0 * time.second())
        painter.drawConvexPolygon(AnalogClock.secondHand)
        painter.restore()

        painter.setPen(QtGui.QColor(0, 0, 0))
        painter.drawEllipse(-5, -5, 10, 10)  # 画圆。参数是外接矩形左上点和长宽


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    # widget = QWidget()
    # widget.setWindowTitle("Hello ZCB")
    # widget.setWindowIcon(QIcon("d:/0.jpg"))
    # widget.show()

    clock = AnalogClock()
    clock.show()

    sys.exit(app.exec())
View Code

通过继承QWidget,并重写父类方法 printEvent() 来实现,1s 更新一次!!! 

https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtwidgets/qwidget.html#custom-widgets-and-painting   

效果图:

 

 

 

 

Qt 中的拖拽 QDrag

PyQt Draggable Icons Example

import sys

from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QByteArray, QDataStream, QIODevice, QPoint, QMimeData, QObject
from PyQt5.QtGui import QPixmap, QDrag, QPainter, QColor
from PyQt5.QtWidgets import QFrame, QLabel, QApplication, QWidget, QHBoxLayout


class DragWidget(QFrame):
    def __init__(self,parent=None):
        super().__init__(parent)
        self.setMinimumSize(200,200)
        self.setFrameStyle(QFrame.Sunken|QFrame.StyledPanel)
        self.setAcceptDrops(True)

        boatIcon = QLabel(self)
        boatIcon.setPixmap(QPixmap("images/boat.png"))
        boatIcon.move(10,10)
        boatIcon.show()
        boatIcon.setAttribute(Qt.WA_DeleteOnClose)


        carIcon = QLabel(self)
        carIcon.setPixmap(QPixmap("images/car.png"))
        carIcon.move(100,10)
        carIcon.show()
        carIcon.setAttribute(Qt.WA_DeleteOnClose)

        houseIcon = QLabel(self)
        houseIcon.setPixmap(QPixmap("images/house.png"))
        houseIcon.move(10,80)
        houseIcon.show()
        houseIcon.setAttribute(Qt.WA_DeleteOnClose)

    def dragEnterEvent(self, evt: QtGui.QDragEnterEvent) -> None:
        print("drag enter")
        if evt.mimeData().hasFormat("application/x-zcb"):
            if evt.source() == self:
                evt.setDropAction(Qt.MoveAction)
                evt.accept()
            else:
                evt.acceptProposedAction()
        else:
            evt.ignore()

    def dragMoveEvent(self, evt: QtGui.QDragMoveEvent) -> None:
        print("drag move")

        if evt.mimeData().hasFormat("application/x-zcb"):
            if evt.source() == self:
                evt.setDropAction(Qt.MoveAction)
                evt.accept()
            else:
                evt.acceptProposedAction()
        else:
            evt.ignore()


    def dropEvent(self, evt: QtGui.QDropEvent) -> None:
        print("drop event")

        if evt.mimeData().hasFormat("application/x-zcb"):
            itemData = evt.mimeData().data("application/x-zcb")
            dataStream = QDataStream(itemData, QIODevice.ReadOnly)

            pixmap = QPixmap()
            offset = QPoint()
            dataStream >> pixmap >> offset

            newIcon = QLabel(self)
            newIcon.setPixmap(pixmap)
            newIcon.move(evt.pos() - offset)
            newIcon.show()
            newIcon.setAttribute(Qt.WA_DeleteOnClose)

            if evt.source() == self:
                evt.setDropAction(Qt.MoveAction)
                evt.accept()
            else:
                evt.acceptProposedAction()
        else:
            evt.ignore()

    def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:

        child:QLabel = self.childAt(evt.pos())
        if not child:
            return
        pixmap = QPixmap(child.pixmap())

        itemData = QByteArray()
        dataStream = QDataStream(itemData,QIODevice.WriteOnly)
        dataStream << pixmap <<QPoint(evt.pos() - child.pos())

        mimeData = QMimeData()
        mimeData.setData("application/x-zcb",itemData)

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setPixmap(pixmap)
        drag.setHotSpot(evt.pos() - child.pos())

        tempPixmap = pixmap
        painter = QPainter()
        painter.begin(tempPixmap)
        painter.fillRect(pixmap.rect(),QColor(127,127,127,255//3))
        painter.end()

        child.setPixmap(tempPixmap)

        if drag.exec(Qt.CopyAction | Qt.MoveAction, Qt.CopyAction) == Qt.MoveAction:
            child.close()
        else:
            child.show()
            child.setPixmap(pixmap)

if __name__ == '__main__':
    app = QApplication(sys.argv)

    mainWidget = QWidget()
    horLayout = QHBoxLayout()
    horLayout.addWidget(DragWidget())
    horLayout.addWidget(DragWidget())

    mainWidget.setLayout(horLayout)
    mainWidget.setWindowTitle("Draggable Icons")
    mainWidget.show()

    app.exec()
View Code

https://doc.qt.io/archives/qt-5.5/qtwidgets-draganddrop-draggableicons-example.html

效果图:

 

PyQt Draggable Text Example

import re
import sys

from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QByteArray, QIODevice, QPoint, QMimeData, QObject, QFile, QTextStream, QRegularExpression
from PyQt5.QtGui import QPixmap, QDrag, QPainter, QColor
from PyQt5.QtWidgets import QFrame, QLabel, QApplication, QWidget, QHBoxLayout


def createDragLabel(text, parent):
    label = QLabel(text, parent)
    label.setAutoFillBackground(True)
    label.setFrameShape(QFrame.Panel)
    label.setFrameShadow(QFrame.Raised)

    return label


class DragWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        dicFile = QFile("d:/words.txt")
        dicFile.open(QIODevice.ReadOnly)
        inputStream = QTextStream(dicFile)

        x, y = 5, 5
        while not inputStream.atEnd():

            word = inputStream.readLine()
            if word:
                wordLabel: QLabel = createDragLabel(word, self)
                wordLabel.move(x, y)
                wordLabel.show()
                wordLabel.setAttribute(Qt.WA_DeleteOnClose)
                x += wordLabel.width() + 2
                if x >= 245:
                    x = 5
                    y += wordLabel.height() + 2

        self.setAcceptDrops(True)
        self.setMinimumSize(400, max(200, y))
        self.setWindowTitle("Draggable Text")

    def dragEnterEvent(self, evt: QtGui.QDragEnterEvent) -> None:
        if evt.mimeData().hasText():
            if evt.source() == self:
                evt.setDropAction(Qt.MoveAction)
                evt.accept()
            else:
                evt.acceptProposedAction()
        else:
            evt.ignore()

    def dropEvent(self, evt: QtGui.QDragMoveEvent) -> None:
        if evt.mimeData().hasText():
            mime = evt.mimeData()
            pieces = re.split("\s+", mime.text())

            position: QPoint = evt.pos()
            hotSpot = QPoint()

            hotSpotPos = mime.data("application/x-zcb").split(" ")
            if len(hotSpotPos) == 2:
                hotSpot.setX(int(hotSpotPos[0]))
                hotSpot.setY(int(hotSpotPos[1]))

            for piece in pieces:
                newLabel = createDragLabel(piece, self)
                newLabel.move(position - hotSpot)
                newLabel.show()
                newLabel.setAttribute(Qt.WA_DeleteOnClose)

                position += QPoint(newLabel.width(), 0)

            if evt.source() == self:
                evt.setDropAction(Qt.MoveAction)
                evt.accept()
            else:
                evt.acceptProposedAction()
        else:
            evt.ignore()

    def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:
        child: QLabel = self.childAt(evt.pos())
        if not child:
            return

        hotSpot: QPoint = evt.pos() - child.pos()

        mimeData = QMimeData()
        mimeData.setText(child.text())
        mimeData.setData("application/x-zcb", f"{hotSpot.x()} {hotSpot.y()}".encode())


        dpr = self.windowHandle().devicePixelRatio()
        pixmap = QPixmap(child.size() * dpr)
        pixmap.setDevicePixelRatio(dpr)
        child.render(pixmap)

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setPixmap(pixmap)
        drag.setHotSpot(hotSpot)

        dropAction: Qt.DropAction = drag.exec(Qt.CopyAction | Qt.MoveAction, Qt.CopyAction)
        if dropAction == Qt.MoveAction:
            child.close()

if __name__ == '__main__':
    app = QApplication(sys.argv)

    window = DragWidget()
    window.show()

    app.exec()
View Code

https://doc.qt.io/qt-5/qtwidgets-draganddrop-draggabletext-example.html

效果图:

 

PyQt Draggable Drop Site Example

https://doc.qt.io/qt-5/qtwidgets-draganddrop-dropsite-example.html

import re
import sys

from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QByteArray, QIODevice, QPoint, QMimeData, QObject, QFile, QTextStream, QRegularExpression, \
    pyqtSignal
from PyQt5.QtGui import QPixmap, QDrag, QPainter, QColor, QPalette, QGuiApplication
from PyQt5.QtWidgets import QFrame, QLabel, QApplication, QWidget, QHBoxLayout, QTableWidget, QAbstractItemView, \
    QPushButton, QDialogButtonBox, QVBoxLayout, QTableWidgetItem


class DropArea(QLabel):
    changed = pyqtSignal(QMimeData)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMinimumSize(200, 200)
        self.setFrameStyle(QFrame.Sunken | QFrame.StyledPanel)
        self.setAlignment(Qt.AlignCenter)
        self.setAcceptDrops(True)
        self.setAutoFillBackground(True)

        self.clear()

    def clear(self) -> None:
        self.setText("<drop content>")
        self.setBackgroundRole(QPalette.Dark)
        self.changed.emit(None)

    def dragEnterEvent(self, evt: QtGui.QDragEnterEvent) -> None:
        self.setText("<drop content>")
        self.setBackgroundRole(QPalette.Highlight)

        evt.acceptProposedAction()
        self.changed.emit(evt.mimeData())

    def dragMoveEvent(self, evt: QtGui.QDragMoveEvent) -> None:
        evt.acceptProposedAction()

    def dropEvent(self, evt: QtGui.QDropEvent) -> None:
        mime = evt.mimeData()
        if mime.hasImage():
            print("image")
            self.setPixmap(mime.imageData())
        elif mime.hasHtml():
            self.setText(mime.html())
            self.setTextFormat(Qt.RichText)
        elif mime.hasText():
            print("text")
            self.setText(mime.text())
            self.setTextFormat(Qt.PlainText)
        elif mime.hasUrls():
            print("url")
            urls = mime.urls()
            text = ""
            for url in urls:
                text += url.path() + "\n"
            self.setText(text)
        else:
            self.setText("Cannot display data")

        self.setBackgroundRole(QPalette.Dark)
        evt.acceptProposedAction()

    def dragLeaveEvent(self, evt: QtGui.QDragLeaveEvent) -> None:
        self.clear()
        evt.accept()


class DropSiteWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        abstractLabel = QLabel(
            "This example accepts drags from other applications and displays the MIME types provided by the drag object.")

        abstractLabel.setWordWrap(True)
        abstractLabel.adjustSize()

        dropArea = DropArea()
        dropArea.changed.connect(self.updateFormatsTable)

        self.formatsTable = QTableWidget()
        formatsTable = self.formatsTable
        formatsTable.setColumnCount(2)
        formatsTable.setEditTriggers(QAbstractItemView.NoEditTriggers)
        formatsTable.setHorizontalHeaderLabels(["Format", "Content"])
        formatsTable.horizontalHeader().setStretchLastSection(True)

        self.clearButton = QPushButton("Clear")
        self.copyButton = QPushButton("Copy")
        self.quitButton = QPushButton("Quit")
        clearButton = self.clearButton
        copyButton = self.copyButton
        quitButton = self.quitButton

        buttonBox = QDialogButtonBox()
        buttonBox.addButton(clearButton, QDialogButtonBox.ActionRole)
        buttonBox.addButton(copyButton, QDialogButtonBox.ActionRole)

        copyButton.setVisible(False)

        buttonBox.addButton(quitButton, QDialogButtonBox.RejectRole)

        quitButton.clicked.connect(self.close)
        clearButton.clicked.connect(dropArea.clear)
        quitButton.clicked.connect(self.copy)

        mainLayout = QVBoxLayout(self)
        mainLayout.addWidget(abstractLabel)
        mainLayout.addWidget(dropArea)
        mainLayout.addWidget(formatsTable)
        mainLayout.addWidget(buttonBox)

        self.setWindowTitle("Drop Site")
        self.setMinimumSize(350, 500)

    def updateFormatsTable(self, mimeData: QMimeData):

        formatsTable = self.formatsTable
        formatsTable.setRowCount(0)

        copyButton = self.copyButton
        copyButton.setEnabled(False)
        if not mimeData:
            return

        formats = mimeData.formats()
        for format in formats:
            formatItem = QTableWidgetItem()
            formatItem.setFlags(Qt.ItemIsEnabled)
            formatItem.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
            text = ""
            if format == "text/plain":
                text = mimeData.text()
            elif format == "text/html":
                text = mimeData.html()
            elif format == "text/uri-list":
                urls = mimeData.urls()
                for url in urls:
                    text += url.toString() + " "
            else:
                data: QByteArray = mimeData.data(format)
                for item in data:
                    hex = str(item).strip("b'\\x")
                    text += f' {hex}'.upper()

            row = formatsTable.rowCount()
            formatsTable.insertRow(row)
            formatsTable.setItem(row, 0, QTableWidgetItem(format))
            formatsTable.setItem(row, 1, QTableWidgetItem(text))

            formatsTable.resizeColumnToContents(0)
            copyButton.setEnabled(formatsTable.rowCount() > 0)

    def copy(self):
        text = ""
        formatsTable = self.formatsTable
        for row in range(formatsTable.rowCount()):
            text += formatsTable.item(row, 0) + ": " + formatsTable.item(row, 1).text() + "\n"

        QGuiApplication.clipboard().setText(text)


if __name__ == '__main__':
    app = QApplication(sys.argv)

    window = DropSiteWindow()
    window.show()

    app.exec()
View Code

 

PyQt Drag and Drop Puzzle Example

https://doc.qt.io/qt-5/qtwidgets-draganddrop-puzzle-example.html

import sys

from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QByteArray, QDataStream, QIODevice, QPoint, QMimeData, QObject, QStandardPaths, QDir, \
    QSize, QVariant, QRect, pyqtSignal, QCoreApplication, QRandomGenerator
from PyQt5.QtGui import QPixmap, QDrag, QPainter, QColor, QImageReader, QIcon, QKeySequence
from PyQt5.QtWidgets import QFrame, QLabel, QApplication, QWidget, QHBoxLayout, QMainWindow, QFileDialog, QDialog, \
    QMessageBox, QListWidget, QListView, QListWidgetItem, QSizePolicy, QAction, qApp, QMenu


class PiecesList(QListWidget):
    def __init__(self, pieceSize, parent=None):
        super().__init__(parent)
        self.setDragEnabled(True)
        self.setViewMode(QListView.IconMode)
        self.setIconSize(QSize(pieceSize, pieceSize))
        self.setSpacing(10)
        self.setAcceptDrops(True)
        self.setDropIndicatorShown(True)

    @staticmethod
    def puzzleMimeType():
        return "image/x-puzzle-piece"

    def dragEnterEvent(self, evt: QtGui.QDragEnterEvent) -> None:
        # print("piecelist drag enter")
        if evt.mimeData().hasFormat(PiecesList.puzzleMimeType()):
            evt.accept()
        else:
            evt.ignore()

    def dragMoveEvent(self, evt: QtGui.QDragMoveEvent) -> None:
        # print("piecelist drag move")
        if evt.mimeData().hasFormat(PiecesList.puzzleMimeType()):
            evt.setDropAction(Qt.MoveAction)
            evt.accept()
        else:
            evt.ignore()

    def dropEvent(self, evt: QtGui.QDropEvent) -> None:
        # print("piecelist drop event")

        if evt.mimeData().hasFormat(PiecesList.puzzleMimeType()):
            pieceData: QByteArray = evt.mimeData().data(PiecesList.puzzleMimeType())
            dataStream = QDataStream(pieceData, QIODevice.ReadOnly)

            pixmap = QPixmap()
            location = QPoint()
            dataStream >> pixmap >> location

            self.addPiece(pixmap, location)

            evt.setDropAction(Qt.MoveAction)
            evt.accept()
        else:
            evt.ignore()

    def addPiece(self, pixmap: QPixmap, location: QPoint):
        pieceItem: QListWidgetItem = QListWidgetItem(self)
        pieceItem.setIcon(QIcon(pixmap))
        pieceItem.setData(Qt.UserRole, QVariant(pixmap))
        pieceItem.setData(Qt.UserRole + 1, location)

        pieceItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled)

    def startDrag(self, supportedActions: Qt.DropActions) -> None:
        # print("piecelist startDrag")
        item: QListWidgetItem = self.currentItem()

        itemData = QByteArray()
        dataStream = QDataStream(itemData, QIODevice.WriteOnly)

        pixmap: QPixmap = item.data(Qt.UserRole)
        location: QPoint = item.data(Qt.UserRole + 1)

        dataStream << pixmap << location

        mimeData = QMimeData()
        mimeData.setData(PiecesList.puzzleMimeType(), itemData)

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2))
        drag.setPixmap(pixmap)

        if drag.exec(Qt.MoveAction) == Qt.MoveAction:
            ret = self.takeItem(self.row(item))
            del ret


class Piece:
    def __init__(self):
        self.pixmap = QPixmap()
        self.rect = QRect()
        self.location = QPoint()


class PuzzleWidget(QWidget):
    puzzleCompleted = pyqtSignal()

    def __init__(self, imageSize, parent=None):
        super().__init__(parent)
        self.m_ImageSize = imageSize
        self.pieces: [Piece] = []  # 存放 Piece() 对象
        self.highlightedRect = QRect()
        self.inPlace = int()

        self.setAcceptDrops(True)
        self.setMinimumSize(imageSize, imageSize)
        self.setMaximumSize(imageSize, imageSize)

    def clear(self):
        self.pieces.clear()
        self.highlightedRect = QRect()
        self.inPlace = 0
        self.update()

    def dragEnterEvent(self, evt: QtGui.QDragEnterEvent) -> None:
        # print("puzzlewidget drag enter")
        if evt.mimeData().hasFormat(PiecesList.puzzleMimeType()):
            evt.accept()
        else:
            evt.ignore()

    def dragLeaveEvent(self, evt: QtGui.QDragLeaveEvent) -> None:
        # print("puzzlewidget drag leave")

        updateRect = self.highlightedRect
        self.highlightedRect = QRect()
        self.update(updateRect)
        evt.accept()

    def dragMoveEvent(self, evt: QtGui.QDragMoveEvent) -> None:
        # print("puzzlewidget drag move")

        updateRect = self.highlightedRect.united(self.targetSquare(evt.pos()))
        if evt.mimeData().hasFormat(PiecesList.puzzleMimeType()) and self.findPiece(self.targetSquare(evt.pos())) == -1:

            self.highlightedRect = self.targetSquare(evt.pos())
            evt.setDropAction(Qt.MoveAction)
            evt.accept()
        else:
            self.highlightedRect = QRect()
            evt.ignore()

        self.update(updateRect)

    def dropEvent(self, evt: QtGui.QDropEvent) -> None:
        # print("puzzlewidget drop event")
        if evt.mimeData().hasFormat(PiecesList.puzzleMimeType()) and self.findPiece(self.targetSquare(evt.pos()) == -1):
            pieceData = evt.mimeData().data(PiecesList.puzzleMimeType())
            dataStream = QDataStream(pieceData, QIODevice.ReadOnly)
            piece = Piece()
            piece.rect = self.targetSquare(evt.pos())
            dataStream >> piece.pixmap >> piece.location

            self.pieces.append(piece)
            self.update(piece.rect)

            evt.setDropAction(Qt.MoveAction)
            evt.accept()

            if piece.location == piece.rect.topLeft() / self.pieceSize():
                self.inPlace += 1
                if self.inPlace == 25:
                    self.puzzleCompleted.emit()
        else:
            self.highlightedRect = QRect()
            evt.ignore()

    def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:
        square: QRect = self.targetSquare(evt.pos())

        found = self.findPiece(square)
        if found == -1:
            return

        piece: Piece = self.pieces[found]

        if piece.location == square.topLeft() / self.pieceSize():
            self.inPlace -= 1

        self.update(square)

        itemData = QByteArray()
        dataStream = QDataStream(itemData, QIODevice.WriteOnly)

        dataStream << piece.pixmap << piece.location

        mimeData = QMimeData()
        mimeData.setData(PiecesList.puzzleMimeType(), itemData)

        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(evt.pos() - square.topLeft())
        drag.setPixmap(piece.pixmap)

        if drag.exec(Qt.MoveAction) != Qt.MoveAction:
            # self.pieces.insert(found, piece)
            # self.update(self.targetSquare(evt.pos()))

            if piece.location == square.topLeft() / self.pieceSize():
                self.inPlace += 1

        else:
            del self.pieces[found]
            self.update()

    def paintEvent(self, evt: QtGui.QPaintEvent) -> None:
        painter = QPainter(self)
        painter.fillRect(evt.rect(), Qt.white)

        if self.highlightedRect.isValid():
            painter.setBrush(QColor("#ffcccc"))
            painter.setPen(Qt.NoPen)
            painter.drawRect(self.highlightedRect.adjusted(0, 0, -1, -1))

        for piece in self.pieces:
            painter.drawPixmap(piece.rect, piece.pixmap)

    def findPiece(self, pieceRect: QRect):
        for i in range(len(self.pieces)):
            piece: Piece = self.pieces[i]
            if piece.rect == pieceRect:
                return i
        return -1

    def targetSquare(self, position: QPoint):
        k1 = int(position.x() / self.pieceSize())
        k2 = int(position.y() / self.pieceSize())
        ret = QRect(QPoint(k1*self.pieceSize(),k2*self.pieceSize()), QSize(self.pieceSize(), self.pieceSize()))
        return ret





    def pieceSize(self):
        return self.m_ImageSize / 5


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupMenus()
        self.setupWidgets()

        self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.setWindowTitle("Puzzle")

    def setupMenus(self):
        # print("setupMenus")
        fileMenu = self.menuBar().addMenu("&File")
        openAction: QAction = fileMenu.addAction("&Open...", self.openImage)
        openAction.setShortcuts(QKeySequence.Open)

        exitAction = fileMenu.addAction("E&xit", QCoreApplication.quit)
        exitAction.setShortcuts(QKeySequence.Quit)

        gameMenu: QMenu = self.menuBar().addMenu("&Game")
        gameMenu.addAction("&Restart", self.setupPuzzle)

    def setupWidgets(self):
        frame = QFrame()
        frameLayout = QHBoxLayout(frame)
        self.puzzleWidget = PuzzleWidget(800)

        self.piecesList = PiecesList(self.puzzleWidget.pieceSize(), self)
        self.puzzleWidget.puzzleCompleted.connect(self.setCompleted, Qt.QueuedConnection)

        frameLayout.addWidget(self.piecesList)
        frameLayout.addWidget(self.puzzleWidget)

        self.setCentralWidget(frame)

    def openImage(self):
        try:
            directory = QStandardPaths.standardLocations(QStandardPaths.PicturesLocation)[0]
        except Exception as e:
            directory = QDir.homePath()

        dialog = QFileDialog(self, "Open Image", directory)
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setFileMode(QFileDialog.ExistingFile)

        mimeTypeFilters = []
        for mimeTypeName in QImageReader.supportedMimeTypes():
            mimeTypeFilters.append(mimeTypeName.data().decode())
        mimeTypeFilters.sort()

        dialog.setMimeTypeFilters(mimeTypeFilters)
        dialog.selectMimeTypeFilter("image/jpeg")

        if dialog.exec() == QDialog.Accepted:
            self.loadImage(dialog.selectedFiles()[0])

    def loadImage(self, fileName):
        newImage = QPixmap()
        if not newImage.load(fileName):
            QMessageBox.warning(self, "Open Image", "The image file could not be loaded.", QMessageBox.Close)
            return

        self.puzzleImage = newImage  # goto1
        self.setupPuzzle()

    def setCompleted(self):
        QMessageBox.information(self, "Puzzle Completed",
                                "Congratulations! You have completed the puzzle! Click OK to start again.",
                                QMessageBox.Ok)
        self.setupPuzzle()

    def setupPuzzle(self):
        puzzleImage = self.puzzleImage
        size = min(puzzleImage.width(), puzzleImage.height())

        puzzleImage = puzzleImage.copy((puzzleImage.width() - size) / 2, (puzzleImage.height() - size) / 2, size,
                                       size).scaled(self.puzzleWidget.width(), self.puzzleWidget.height(),
                                                    Qt.IgnoreAspectRatio, Qt.SmoothTransformation)

        self.piecesList.clear()

        for x in range(5):
            for y in range(5):
                pieceSize = self.puzzleWidget.pieceSize()
                pieceImage: QPixmap = puzzleImage.copy(x * pieceSize, y * pieceSize, pieceSize, pieceSize)

                self.piecesList.addPiece(pieceImage, QPoint(x, y))

        for i in range(len(self.piecesList)):
            if QRandomGenerator.global_().bounded(2) == 1:
                item: QListWidgetItem = self.piecesList.takeItem(i)
                self.piecesList.insertItem(0, item)

        self.puzzleWidget.clear()


if __name__ == '__main__':
    app = QApplication(sys.argv)

    window = MainWindow()
    window.loadImage("./images/0.jpg")

    window.show()

    app.exec()
View Code

 

 

posted @ 2020-06-13 19:43  Zcb0812  阅读(455)  评论(0编辑  收藏  举报