前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PyQt5 多线程绘制曼德勃罗集分形图

PyQt5 多线程绘制曼德勃罗集分形图

作者头像
用户6021899
发布2019-11-23 08:55:24
9530
发布2019-11-23 08:55:24
举报

本篇的代码来自于PyQt4官方demo,其功能是使用多线程,计算每一像素的的RGB,生成一张曼德勃罗集分形图,支持平移与缩放。

代码如下(我已将其改为PyQt5版本):

代码语言:javascript
复制
#!/usr/bin/env python## Copyright (C) 2010 Riverbank Computing Limited.
from PyQt5 import QtCore, QtGui, QtWidgets
DefaultCenterX = -0.647011DefaultCenterY = -0.0395159DefaultScale = 0.00403897ZoomInFactor = 0.8ZoomOutFactor = 1 / ZoomInFactorScrollStep = 20
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* class RenderThread(QtCore.QThread):#创建一个线程类    ColormapSize = 500       #renderedImage = QtCore.pyqtSignal(QtWidgets.QWidget, float)#only for PyQt4    renderedImage = QtCore.pyqtSignal(QtGui.QImage, float)#for PyQt5       def __init__(self, parent=None):        super().__init__(parent)        self.mutex = QtCore.QMutex() #用于线程锁,使同一时间只有一个线程能访问待保护的对象        self.condition = QtCore.QWaitCondition()        self.centerX = 0.0        self.centerY = 0.0        self.scaleFactor = 0.0        self.resultSize = QtCore.QSize()        self.restart = False        self.abort = False        self.colormap = []        for i in range(RenderThread.ColormapSize):            self.colormap.append(self.rgbFromWaveLength(450 + (i * 400.0 / RenderThread.ColormapSize)))       def __del__(self):        self.mutex.lock()        self.abort = True        self.condition.wakeOne()        self.mutex.unlock()        self.wait()            def render(self, centerX, centerY, scaleFactor, resultSize):        locker = QtCore.QMutexLocker(self.mutex)        self.centerX = centerX        self.centerY = centerY        self.scaleFactor = scaleFactor        self.resultSize = resultSize        if not self.isRunning():            self.start(QtCore.QThread.LowPriority) #以低优先级启动线程,调用run()        else:            self.restart = True                       #用于多线程的同步,一个线程调用QWaitCondition.wait() 阻塞等待,直到另一个线程调用QWaitCondition.wake() 唤醒才继续往下执行            #wakeOne会随机唤醒等待的线程中的一个            self.condition.wakeOne()                def run(self):        while True:            self.mutex.lock() #线程加锁            resultSize = self.resultSize            scaleFactor = self.scaleFactor            centerX = self.centerX            centerY = self.centerY            self.mutex.unlock()#线程解锁            halfWidth = resultSize.width() // 2            halfHeight = resultSize.height() // 2            image = QtGui.QImage(resultSize, QtGui.QImage.Format_RGB32)            NumPasses = 8            curpass = 0            while curpass < NumPasses:                MaxIterations = (1 << (2 * curpass + 6)) + 40                Limit = 4                allBlack = True                for y in range(-halfHeight, halfHeight):                    if self.restart:                        break                    if self.abort:                        return                    ay = 1j * (centerY + (y * scaleFactor))                    for x in range(-halfWidth, halfWidth):                        c0 = centerX + (x * scaleFactor) + ay                        c = c0                        numIterations = 0                        while numIterations < MaxIterations:                            numIterations += 1                            c = c*c + c0                            if abs(c) >= Limit:                                break                            numIterations += 1                            c = c*c + c0                            if abs(c) >= Limit:                                break                            numIterations += 1                            c = c*c + c0                            if abs(c) >= Limit:                                break                            numIterations += 1                            c = c*c + c0                            if abs(c) >= Limit:                                break                        if numIterations < MaxIterations:                            #image.setPixel()设定像素,设定指定坐标处的qRgb                            image.setPixel(x + halfWidth, y + halfHeight,                                           self.colormap[numIterations % RenderThread.ColormapSize])                            allBlack = False                        else:                            #image.setPixel()设定像素,设定指定坐标处的qRgb                            image.setPixel(x + halfWidth, y + halfHeight, QtGui.qRgb(0, 0, 0))                if allBlack and curpass == 0:                    curpass = 4                else:                    if not self.restart:                        self.renderedImage.emit(image, scaleFactor)                    curpass += 1            self.mutex.lock()            if not self.restart:                #用于多线程的同步,一个线程调用QWaitCondition.wait() 阻塞等待,直到另一个线程调用QWaitCondition.wake() 唤醒才继续往下执行                self.condition.wait(self.mutex)            self.restart = False            self.mutex.unlock()                     def rgbFromWaveLength(self, wave):        #根据波长返回一个RGB颜色 对象        r = 0.0        g = 0.0        b = 0.0        if wave >= 380.0 and wave <= 440.0:            r = -1.0 * (wave - 440.0) / (440.0 - 380.0)            b = 1.0        elif wave >= 440.0 and wave <= 490.0:            g = (wave - 440.0) / (490.0 - 440.0)            b = 1.0        elif wave >= 490.0 and wave <= 510.0:            g = 1.0            b = -1.0 * (wave - 510.0) / (510.0 - 490.0)        elif wave >= 510.0 and wave <= 580.0:            r = (wave - 510.0) / (580.0 - 510.0)            g = 1.0        elif wave >= 580.0 and wave <= 645.0:            r = 1.0            g = -1.0 * (wave - 645.0) / (645.0 - 580.0)        elif wave >= 645.0 and wave <= 780.0:            r = 1.0        s = 1.0        if wave > 700.0:            s = 0.3 + 0.7 * (780.0 - wave) / (780.0 - 700.0)        elif wave < 420.0:            s = 0.3 + 0.7 * (wave - 380.0) / (420.0 - 380.0)        r = pow(r * s, 0.8)        g = pow(g * s, 0.8)        b = pow(b * s, 0.8)        return QtGui.qRgb(r*255, g*255, b*255)
*/
代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* class MandelbrotWidget(QtWidgets.QWidget):    def __init__(self, parent=None):        super().__init__(parent)        self.thread = RenderThread()        self.pixmap = QtGui.QPixmap()        self.pixmapOffset = QtCore.QPoint()        self.lastDragPos = QtCore.QPoint()        self.centerX = DefaultCenterX        self.centerY = DefaultCenterY        self.pixmapScale = DefaultScale        self.curScale = DefaultScale        self.thread.renderedImage.connect(self.updatePixmap)        self.setWindowTitle("Mandelbrot")        self.setCursor(QtCore.Qt.CrossCursor) #光标形状设为十字型        self.resize(550, 400)    def paintEvent(self, event): #屏幕绘制事件 ,self.update()调用时被调用        painter = QtGui.QPainter(self)        painter.fillRect(self.rect(), QtCore.Qt.black)        if self.pixmap.isNull():            painter.setPen(QtCore.Qt.white)            painter.drawText(self.rect(), QtCore.Qt.AlignCenter,                    "Rendering initial image, please wait...")            return               if self.curScale == self.pixmapScale:            painter.drawPixmap(self.pixmapOffset, self.pixmap)        else:            scaleFactor = self.pixmapScale / self.curScale            newWidth = int(self.pixmap.width() * scaleFactor)            newHeight = int(self.pixmap.height() * scaleFactor)            newX = self.pixmapOffset.x() + (self.pixmap.width() - newWidth) / 2            newY = self.pixmapOffset.y() + (self.pixmap.height() - newHeight) / 2            painter.save()            painter.translate(newX, newY)            painter.scale(scaleFactor, scaleFactor)            #exposed, _ = painter.matrix().inverted() #only for PyQt4            exposed, _ = painter.transform().inverted() #变换矩阵的逆矩阵,for PyQt5             exposed = exposed.mapRect(self.rect()).adjusted(-1, -1, 1, 1)            painter.drawPixmap(exposed, self.pixmap, exposed)            painter.restore()        text = "Use mouse wheel or the '+' and '-' keys to zoom. Press and " \                "hold left mouse button to scroll."        metrics = painter.fontMetrics()        textWidth = metrics.width(text)        painter.setPen(QtCore.Qt.NoPen)        painter.setBrush(QtGui.QColor(0, 0, 0, 127))        painter.drawRect((self.width() - textWidth) / 2 - 5, 0, textWidth + 10,                metrics.lineSpacing() + 5)        painter.setPen(QtCore.Qt.white)        painter.drawText((self.width() - textWidth) / 2,                metrics.leading() + metrics.ascent(), text)       def resizeEvent(self, event):        self.thread.render(self.centerX, self.centerY, self.curScale, self.size())    def keyPressEvent(self, event):# 键盘事件的响应        if event.key() == QtCore.Qt.Key_Plus:            self.zoom(ZoomInFactor)        elif event.key() == QtCore.Qt.Key_Minus:            self.zoom(ZoomOutFactor)        elif event.key() == QtCore.Qt.Key_Left:            self.scroll(-ScrollStep, 0)        elif event.key() == QtCore.Qt.Key_Right:            self.scroll(+ScrollStep, 0)        elif event.key() == QtCore.Qt.Key_Down:            self.scroll(0, -ScrollStep)        elif event.key() == QtCore.Qt.Key_Up:            self.scroll(0, +ScrollStep)        else:            super().keyPressEvent(event)                def wheelEvent(self, event):# 鼠标滚轮事件的响应        #numDegrees = event.delta() / 8 # only for PyQt4        numDegrees = event.angleDelta().y() / 8  #for PyQt5        numSteps = numDegrees / 15.0        self.zoom(pow(ZoomInFactor, numSteps))            def mousePressEvent(self, event): #鼠标按下事件的响应        if event.buttons() == QtCore.Qt.LeftButton:            self.lastDragPos = QtCore.QPoint(event.pos())                def mouseMoveEvent(self, event): # 鼠标移动事件的响应        if event.buttons() & QtCore.Qt.LeftButton:            self.pixmapOffset += event.pos() - self.lastDragPos            self.lastDragPos = QtCore.QPoint(event.pos())            self.update()                def mouseReleaseEvent(self, event):# 鼠标释放事件的响应        if event.button() == QtCore.Qt.LeftButton:            self.pixmapOffset += event.pos() - self.lastDragPos            self.lastDragPos = QtCore.QPoint()            deltaX = (self.width() - self.pixmap.width()) / 2 - self.pixmapOffset.x()            deltaY = (self.height() - self.pixmap.height()) / 2 - self.pixmapOffset.y()            self.scroll(deltaX, deltaY)                def updatePixmap(self, image, scaleFactor):        if not self.lastDragPos.isNull():            return        self.pixmap = QtGui.QPixmap.fromImage(image)        self.pixmapOffset = QtCore.QPoint()        self.lastDragPosition = QtCore.QPoint()        self.pixmapScale = scaleFactor        self.update()            def zoom(self, zoomFactor):        self.curScale *= zoomFactor        self.update()        self.thread.render(self.centerX, self.centerY, self.curScale,                self.size())                    def scroll(self, deltaX, deltaY):        self.centerX += deltaX * self.curScale        self.centerY += deltaY * self.curScale        self.update()        self.thread.render(self.centerX, self.centerY, self.curScale, self.size())
*/
代码语言:javascript
复制
if __name__ == '__main__':    import sys    app = QtWidgets.QApplication(sys.argv)    widget = MandelbrotWidget()    widget.show()    sys.exit(app.exec_())
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档