其他分享
首页 > 其他分享> > Pyqt学习——day1

Pyqt学习——day1

作者:互联网

布局

QVBoxLayout、QHBoxLayout、QFormLayout、QGridLayout四种,比较简单。对于比较简单的应用(不维护的)用Qt Designer可以很快完成设计。但是如果是需要维护的,我建议自己手写。
注意:组件加入到某布局,会自动变更父节点

paintEvent

当要自定义组件时,可以继承QWidget,然后重写PainEvent。

# clock.py
from PyQt5.QtCore import QPoint, QSize, Qt, QTime, QTimer
from PyQt5.QtGui import QColor, QPainter, QPolygon, QRegion
from PyQt5.QtWidgets import QAction, QApplication, QWidget
from pyqt5_plugins.examplebuttonplugin import QtGui


class MyClock(QWidget):
    minuteColor = QColor(0, 127, 127, 191)

    def __init__(self, parent=None, R=200):
        super().__init__(parent)

        timer = QTimer()
        timer.timeout.connect(self.paintEvent)
        timer.start(1000)

        self.resize(R * 2, R * 2)

        self.dragPosition = None

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        # 逻辑窗口
        W, H = self.width(), self.height()
        miniSize = min(H, W)
        painter.setViewport((W - miniSize) // 2, (H - miniSize) // 2, miniSize, miniSize)
        painter.setWindow(-100, -100, 200, 200)

        # 画刻度
        painter.save()
        pen = painter.pen()
        pen.setWidth(2)
        pen.setColor(MyClock.minuteColor)
        painter.setPen(pen)

        for i in range(12):
            painter.drawLine(0, -82 if i % 3 == 0 else -90, 0, -95)
            painter.rotate(30)

        # 画指针
        painter.restore()
        painter.save()
        time = QTime.currentTime()
        hour, minute, second = time.hour(), time.minute(), time.second()

        # 时针
        pen = painter.pen()
        pen.setWidth(1)
        brush = painter.brush()
        brush.setColor(Qt.black)
        brush.setStyle(Qt.SolidPattern)
        painter.setBrush(brush)

        painter.rotate(int((minute / 60 + hour % 12) * 30))
        painter.drawPolygon(QPolygon([QPoint(3, 4), QPoint(-3, 4), QPoint(0, -35)]))

        # 分针
        painter.restore()
        pen.setWidth(1)
        painter.rotate(minute * 6 + second // 10)
        painter.drawPolygon(QPolygon([QPoint(1, 8), QPoint(-1, 8), QPoint(0, -88)]))

        maskedRegion = QRegion((self.width() - miniSize) // 2, (self.height() - miniSize) // 2, miniSize, miniSize,
                               QRegion.Ellipse)
        self.setMask(maskedRegion)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
        if event.buttons() == Qt.LeftButton:
            self.move(event.globalPos() - self.dragPosition)
            event.accept()

    def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
        if event.button() == Qt.LeftButton:
            self.dragPosition = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()


if __name__ == '__main__':
    app = QApplication([])
    c = MyClock()

    c.show()

    app.exec_()

# painter.py
from PyQt5.QtCore import QPoint, QRect, QSize, Qt
from PyQt5.QtGui import QImage, QPainter, QPen, qRgb, QMouseEvent
from PyQt5.QtWidgets import QApplication, QWidget
from pyqt5_plugins.examplebuttonplugin import QtGui

"""
1. paintEvent 一次性渲染画面
2. 如果需要做局部更新,可以采用用QImage存储画面,再由paintEven渲染的作法
"""


class Drawer(QWidget):
    def __init__(self, parent=None):
        super(Drawer, self).__init__(parent)
        self.resize(640, 480)

        self.screen = QImage(self.width(), self.height(), QImage.Format_RGB888)
        self.screen.fill(Qt.black)
        self.lastPos = None
        self.drawing = False

    def paintEvent(self, event: QtGui.QPaintEvent) -> None:
        painter = QPainter(self)
        painter.drawImage(event.rect(), self.screen, event.rect())

    def mousePressEvent(self, e: QMouseEvent) -> None:
        if e.button() == Qt.LeftButton:
            self.drawing = True
            self.lastPos = e.pos()
            self.drawLineTo(e.pos())

    def mouseMoveEvent(self, e: QtGui.QMouseEvent) -> None:
        if self.drawing:
            self.drawLineTo(e.pos())

    def mouseReleaseEvent(self, e: QtGui.QMouseEvent) -> None:
        if e.button() == Qt.LeftButton:
            self.drawing = False
            self.drawLineTo(e.pos())

    def drawLineTo(self, newPos):
        painter = QPainter(self.screen)
        painter.setRenderHint(QPainter.Antialiasing)
        pen = painter.pen()
        pen.setColor(Qt.white)
        painter.setPen(pen)
        painter.drawLine(self.lastPos, newPos)

        radius = pen.width() // 2 + 2
        self.update(QRect(self.lastPos, newPos).normalized().adjusted(-radius, -radius, +radius, +radius))
        self.lastPos = newPos

    def resizeEvent(self, e: QtGui.QResizeEvent) -> None:
        if self.width() > self.screen.width() or self.height() > self.screen.height():
            newH = max(self.height() + 128, self.screen.height())
            newW = max(self.width() + 128, self.screen.width())

            if self.screen.height() == newH and self.screen.width() == newW:
                return
            newScreen = QImage(newW, newH, QImage.Format_RGB888)
            newScreen.fill(Qt.black)
            painter = QPainter(newScreen)
            painter.drawImage(QPoint(0, 0), self.screen)
            self.screen = newScreen


if __name__ == '__main__':
    app = QApplication([])
    w = Drawer()
    w.show()
    app.exec_()

两个案例都是重写了paintEvent来实现绘制画面的。而不同的是clock.py在绘制时需要的只是当前的时间;而painter.py则不同,需要有先前的画面信息,所以借助了QImage来存储,各种划线操作也是直接对QImage进行,而paintEvent只是QImage中的内容贴上去。

多线程

将耗时比较长的处理都扔到子线程,否者在运行期间会使得界面无响应。
QMutex是一个互斥变量,当只能单个线程访问时,可以使用;
而QMutexLocker则时对QMutex做了一层封装:开始运行某个函数,在创建QMutexLocker时,申请构造函数传入的QMutex;而该函数运行结束,则将局部变量QMutexLocker销毁,在QMutexLocker的析构函数里释放QMutex,使用简单,但会减低并发性。
QSemaphore时QMutex的升级版, 可以完成大部分操作;
创建方式:继承QThread + 重写run、继承Object + moveToThread(还没有研究差别)
同步方式QMutex、QMutexLocker、QSemaphore、QWaitCondition
退出:可以使用self.thread.quit() self.thread.wait()来等待子线程退出后继续执行

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout
from PyQt5.QtCore import QThread, pyqtSignal, QObject


class Worker(QObject):
    resultReady = pyqtSignal(int)

    def __init__(self):
        super(Worker, self).__init__()
        print("初始化时(子)", QThread.currentThread())
        self.res = 0

    def doWork(self):
        QThread.sleep(3)
        self.res += 1
        print(f"生产了: {self.res}")
        self.resultReady.emit(self.res)
        print(f"运行时(子): {QThread.currentThread()}")

    def do_printChild(self):
        print(f"运行时(子): {QThread.currentThread()}")


class Controller(QWidget):
    parentCall = pyqtSignal(bool)

    def __init__(self):
        super(Controller, self).__init__()

        self.__initUI()
        self.__initThread()
        self.__connectSignalAndSlots()

    def __initUI(self):
        # 界面
        vbox = QVBoxLayout()
        self.setLayout(vbox)

        self.btnRun = QPushButton("run")
        self.btnPrintMain = QPushButton("printMain")
        self.btnPrintChild = QPushButton("printChild")
        self.btnCloseChild = QPushButton("closeChild")
        self.btnCallDirect = QPushButton("callDirect")

        vbox.addWidget(self.btnRun)
        vbox.addWidget(self.btnPrintMain)
        vbox.addWidget(self.btnPrintChild)
        vbox.addWidget(self.btnCloseChild)
        vbox.addWidget(self.btnCallDirect)

    def __initThread(self):
        print("初始化时(主)", QThread.currentThread())
        self.worker = Worker()
        self.thread = QThread()
        self.worker.moveToThread(self.thread)
        self.thread.start()

    def __connectSignalAndSlots(self):
        self.btnRun.clicked.connect(self.worker.doWork)
        self.btnPrintMain.clicked.connect(self.do_printMain)
        self.btnPrintChild.clicked.connect(self.worker.do_printChild)
        self.worker.resultReady.connect(self.doReady)
        self.btnCloseChild.clicked.connect(self.do_closeChild)
        self.btnCallDirect.clicked.connect(self.do_btnCallDirect)

    def do_btnCallDirect(self):
        print("直接调用")
        self.worker.do_printChild()

    def do_closeChild(self):
        self.thread.quit()
        self.thread.wait() # 阻塞主线程,直到停止
        print("阻塞完了")

    def doReady(self, val):
        print(f"响应(主):{QThread.currentThread()}")

    def do_printMain(self):
        print(f"运行时(主):{QThread.currentThread()}")



if __name__ == '__main__':
    app = QApplication([])
    w = Controller()
    w.show()
    app.exec_()


ProducerAndConsumer.py
import random
from PyQt5.QtCore import QCoreApplication, QSemaphore, QThread, QObject, QMutex, pyqtSignal

TOTAL_NEED = 20  # 总共需要的数量
MAX_STOCK = 3  # 最大的存货量
curStock = []  # 当前存货物
canProduceSemaphore = QSemaphore(MAX_STOCK)
canConsumeSemaphore = QSemaphore(0)
mutex = QMutex()


# 继承QThread实现
class Producer(QThread):
    def __init__(self):
        super(Producer, self).__init__()

    def run(self):
        for i in range(TOTAL_NEED):
            # 申请生产指标
            canProduceSemaphore.acquire()

            # 模拟生产
            QThread.msleep(int(random.random() * 1000))

            # 申请独占仓库后加入
            mutex.lock()
            curStock.append(i)
            print(f"生产了: 包子-{i}", flush=True)
            mutex.unlock()

            # 释放一个消耗指标
            canConsumeSemaphore.release()


# 继承QObject实现
class Consumer(QObject):
    stopSing = pyqtSignal()

    def __init__(self):
        super(Consumer, self).__init__()

    # 可以用其他名字
    def work(self):
        for i in range(TOTAL_NEED):
            # 申请
            canConsumeSemaphore.acquire()

            # 申请独占
            mutex.lock()
            index = curStock.pop(0)
            mutex.unlock()

            # 可以继续生产
            canProduceSemaphore.release()

            # 模拟消耗
            print(f"吃了: 包子-{index}", flush=True)
            QThread.msleep(int(random.random())*1000 + 500)

        self.stopSing.emit()


if __name__ == '__main__':
    app = QCoreApplication([])
    producer = Producer()
    consumer = Consumer()

    # 继承QObject用moveToThread实现
    thread = QThread()
    consumer.moveToThread(thread)
    thread.started.connect(consumer.work)
    consumer.stopSing.connect(thread.finished)
    thread.start()

    producer.start()
    app.exec_()

当使用继承QObject+moveToThread的方法,移动到子线程后的对象,一旦通过信号量调用,就能在子线程中运行;而如果直接用函数调用,则会在使用该函数的线程(一般是主线程)执行。

标签:__,self,Pyqt,day1,学习,pen,def,QThread,painter
来源: https://blog.csdn.net/qq_43641752/article/details/121875329