ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Pyqt学习——day1

2021-12-11 17:02:12  阅读:209  来源: 互联网

标签:__ self Pyqt day1 学习 pen def QThread painter


布局

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有