PyQt5 实时获取屏幕界面图像,python3使用matplotlib

  最近,帮一个同学的忙制作一个GUI程序。由于他使用到了Python的 matplotlib第三方库,用于显示波形,而使用Duilib内嵌这个界面存在较大的困难,需要加上python的支持,还要内嵌界面,而外界用户与之交互的按钮又涉及到了一定的麻烦,所以最终决定还是使用python来做这个GUI应用程序。

  由于之前有过C++Qt的基础,加上简单比较python的几个GUI库,最终决定采用PyQt5库。

  本程序采用了Python3进行编写,用了PyQt5、matplotlib、pandas等第三方库,其中有一个小功能是实现实时获取放大当前波形图。

  话不多说,首先需要安装第三方库的支持,由于使用pip安装像matplotlib库之类的会比较麻烦,安装后将发现使用的时候会报错,网上也找了很多办法,但大部分都是比较麻烦的,哈哈,博主比较nan,就直接装了anaconda3,里面包含了大部分需要使用到的,具体可以百度这个软件,下载安装,安装后大概会占用1个g的内容。

  对于PyQt5,就可以简单地使用pip install PyQt5命令直接安装啦!

  安装完后,开始入正题,编写程序:

  首先编写matplotlib相关的基础父类

class BaseOscillograph(FigureCanvas):
    # 定义信号
    PosChangeSignal = QtCore.pyqtSignal(int,int)
    WheelChangSignal = QtCore.pyqtSignal(int)
    def __init__(self, parent=None, width=5, height=4, dpi=100):

        self.WheelValue = 0 #Wheel Default Value

        #Matble实现
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        self.axes.hold(False) 
        FigureCanvas.__init__(self, fig)
        self.setParent(parent) # 设置父窗口,添加入窗口容器
        FigureCanvas.setSizePolicy(self,QSizePolicy.Expanding,QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        # 改变样式
        self.pixmap = QPixmap("./img/border.png")
        self.scaledPixmap = self.pixmap.scaled(QSize(100, 100), Qt.KeepAspectRatio)  # 定义大小 按比例缩放图片
        newCursor = QCursor(self.scaledPixmap, -1, -1)
        self.setCursor(newCursor)

    def wheelEvent(self, event):
        delta = event.angleDelta()
        oriention = delta.y() / 8
    
        if oriention > 0:
            self.WheelValue -=10
        else:
            self.WheelValue +=10
        #Fix Value
        if self.WheelValue > 100:
            self.WheelValue =100
        if self.WheelValue <-40:
            self.WheelValue = -40
        self.scaledPixmap = self.pixmap.scaled(QSize(120+self.WheelValue, 120+self.WheelValue), Qt.KeepAspectRatio)
        newCursor = QCursor(self.scaledPixmap, -1, -1)
        self.setCursor(newCursor)

        self.WheelChangSignal.emit(self.WheelValue) # Send Signal To Provide Current WhellValue

    def mouseMoveEvent(self, event):
        pos =event.globalPos()

        self.curX = pos.x()
        self.curY = pos.y()
        self.PosChangeSignal.emit(self.curX,self.curY)
        super().mouseMoveEvent(event)

大部分本文讨论的核心功能将在父类实现,重载wheelEvent方法,用于获取鼠标滚动事件,采用self.WheelValue属性用于记录当前鼠标滚轮的值,用于发送到主窗体,主窗体获取后动态改变显示大小的倍率,实现放大缩小的效果。每次滚动增减值为10,读者也可以自行添加个全局变量,或后期使用读取配置文件的方式,动态改变这一值。

  每次改变后,使用emit将这个值发送出去,供主窗口程序获取并作出改变。

  同样的方式,采用复写mouseMoveEvent的方法,将获取的当前鼠标位置保存起来,并发送给主界面

接下来就需要编写子类功能实现窗口了,继承自BaseOscillograph

#全局变量
i=0
j=200
data = pd.read_excel(r'./data/signalData.xlsx')
data = np.array(data)

class MatplotOscillograph(BaseOscillograph):
    def __init__(self, *args, **kwargs):
        BaseOscillograph.__init__(self, *args, **kwargs)  #调用父类的构造器
        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.update_figure) # FIXME : 改用开启线程的方式
        self.timer.start(100)
    def update_figure(self):
        # TODO : 此代码需要放在线程上 涉及线程通信
        global i,j
        self.axes.plot(data[i,j-200:j], 'r')
        self.axes.set_ylim([data[i,:].min(),data[i,:].max()])
        j = j + 5
        if j > 4096:
            j = 0
            i = i + 1
        self.draw()
    def stopRun(self):
        self.timer.stop()
    def beginRun(self):
        self.timer.start(100)

这里首先采用简单的测试方法,将excel文件里面的内容当做仪器发送过来的信号,进行读取(这里的读取只是测试用,后期需要采用线程的方式进行动态获取,动态改变波形图,不在本文章详细介绍),子类使用简单的定时器功能,定时获取数据,并将数据读取显示成为波形图,当然,这里是需要修改的,只做测试用。


excel的数据内容很简单,其实就是仿到时候仪器发送过来的数据。由于没有开启线程动态获取,开启程序的时候会因为读取过长,显示界面比较缓慢。后续采用动态获取仪器发送过来的数据将不会有这种现象。



主程序类实现:

class ApplicationWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.Init()

    def Init(self):
        # InitWindowAttribute
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setWindowFlags(Qt.FramelessWindowHint)  #去掉标题栏
        # 添加状态栏  重载,参数二为状态持续时间
        self.statusBar().showMessage(u"心电信号波形图")
        #
        self.SetStyle()
        self.InitData()
        self.InitProperty()
        self.InitLayout()

    def SetStyle(self):
        qssFile = open("qss\style.qss").read()
        self.setStyleSheet(qssFile)
        self.setWindowIcon(QIcon("img/15d.ico"))

    def InitData(self):
        # Param
        self.WheelValue = 0 #默认为0
        self.FitstMove = 1
        self.bImgRun = 1
        self.curX = -1
        self.curY = -1

        # Control
        self.image = QPixmap()
        self.picture = QLabel()
        self.minBtn = QPushButton()
        self.exitBtn = QPushButton()
        self.aboutBtn = QPushButton()
        self.analyResText = QTextEdit()

        # Others
        self.timer = QtCore.QTimer(self)  # 定时器

    def InitProperty(self):
        self.image.scaled(QSize(300, 300), Qt.KeepAspectRatio)
        #
        self.picture.setFixedSize(300, 300)
        self.picture.setScaledContents(1)
        #
        self.analyResText.setAttribute(Qt.WA_TranslucentBackground, 1)
        # Updata
        self.timer.timeout.connect(self.updateCurImg)
        #
        ####
        # Init ToolBar
        self.minBtn.setIcon(QIcon("img/btn_mini_normal.png"))
        self.minBtn.setMaximumSize(20, 20)
        self.minBtn.setIconSize(QSize(30, 30))
        self.minBtn.clicked.connect(self.windowMin)
        #
        self.exitBtn.setIcon(QIcon("img/btn_close_normal.png"))
        self.exitBtn.setMaximumSize(20, 20)
        self.exitBtn.setIconSize(QSize(30, 30))
        self.exitBtn.clicked.connect(self.fileQuit)
        #
        self.aboutBtn.setIcon(QIcon("img/list_icon_b.png"))
        self.aboutBtn.setMaximumSize(20, 20)
        self.aboutBtn.setIconSize(QSize(30, 30))
        self.aboutBtn.clicked.connect(self.about)
        #
        ####
        #
    def InitLayout(self):
        ###
        # Main Frame And Layout
        frame = QWidget()
        frameLayout = QVBoxLayout(frame)
        #
        ###
        #  TitleLayout
        titleLayout = QHBoxLayout()
        toolBarLayout = QHBoxLayout()
        #
        ico = QToolButton()
        ico.setIcon(QIcon("img/15d.ico"))
        title = QLabel(u"心电信号波形界面")
        #
        toolBarLayout.addWidget(QLabel())
        toolBarLayout.addWidget(self.aboutBtn, 1)
        toolBarLayout.addWidget(self.minBtn, 1)
        toolBarLayout.addWidget(self.exitBtn, 1)
        #
        titleLayout.addWidget(ico)
        titleLayout.addWidget(title)
        titleLayout.addLayout(toolBarLayout)
        #
        ###
        # CenterLayout
        centerLayout = QHBoxLayout()
        rightLayout = QVBoxLayout()
        # MatbalWindow
        matplotWindow = MatplotOscillograph(frame, width=5, height=4, dpi=100)
        matplotWindow.PosChangeSignal.connect(self.showCurImg)
        matplotWindow.WheelChangSignal.connect(self.wheelChange)
        #
        rightLayout.addWidget(self.picture)
        rightLayout.addWidget(QLabel(u"分析结果:"))
        rightLayout.addWidget(self.analyResText)
        #
        centerLayout.addWidget(matplotWindow, 2)
        centerLayout.addLayout(rightLayout, 1)
        #
        ###
        # ButtonLayout
        bottonLayout = QHBoxLayout()
        # Buttons
        stopBtn = QPushButton(u"暂停波形图")
        beginBtn = QPushButton(u"波形图运动")
        self.beginImgBtn = QPushButton(u"观察动图")
        self.analyCom = QComboBox()
        #
        # TODO : 改模式名称
        self.analyCom.addItem(u"分析1")
        self.analyCom.addItem(u"分析2")
        self.analyCom.addItem(u"分析3")
        self.analyCom.addItem(u"分析4")
        self.analyCom.addItem(u"分析5")


        # Signals and slots
        stopBtn.clicked.connect(matplotWindow.stopRun)
        beginBtn.clicked.connect(matplotWindow.beginRun)
        self.beginImgBtn.clicked.connect(self.beginImgRun)
        self.analyCom.currentIndexChanged.connect(self.analyFunction)
        #
        bottonLayout.addWidget(stopBtn)
        bottonLayout.addWidget(beginBtn)
        bottonLayout.addWidget(self.beginImgBtn)
        bottonLayout.addWidget(self.analyCom)
        #
        ###
        # MainLayout
        frameLayout.addLayout(titleLayout)
        frameLayout.addWidget(QLabel().setMaximumHeight(20))
        frameLayout.addLayout(centerLayout)
        frameLayout.addLayout(bottonLayout)
        #
        ### Others
        # setFocus And CentralWindow
        frame.setFocus()
        self.setCentralWidget(frame)
        #
        ###

    # # Slot Functions # #
    def fileQuit(self):
        self.close()

    def windowMin(self):
        self.showMinimized( )

    def closeEvent(self, ce):
        self.fileQuit()

    def about(self):
        QMessageBox.about(self, u"心电信号波形图",
        """心电信号波形图使用程序
        旨为医生提供更便捷的服务
        """
        )

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragPosition = event.globalPos() - self.frameGeometry().topLeft()
            QApplication.postEvent(self, QEvent(174))
            event.accept()

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

    def showCurImg(self,x,y):
        self.curX = x
        self.curY = y
        screen = QGuiApplication.primaryScreen()
        distance = self.WheelValue + 80
        self.image = QPixmap(screen.grabWindow(0, x-distance,y-distance,distance*2, distance*2))
        self.picture.setPixmap(self.image)
        self.picture.update()

    def wheelChange(self,wheelValue):
        self.WheelValue = wheelValue

    def updateCurImg(self):
        self.showCurImg(self.curX,self.curY)

    def beginImgRun(self):
        if(self.bImgRun):
            if self.curX == -1:
                self.statusBar().showMessage(u"请先用鼠标获取波形图再尝试",2000)
                return
            self.bImgRun = 0
            self.beginImgBtn.setText(u"停止动图")
            self.timer.start(500) # Don't Too Quick. It is important for CPU
        else:
            self.bImgRun =1
            self.beginImgBtn.setText(u"观察动图")
            self.timer.stop()

哈哈,由于受到了Duilib的影响,现在做界面什么的都喜欢采用自绘的方式,同样的思想被我应用到了PyQt中,采用

        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setWindowFlags(Qt.FramelessWindowHint)  #去掉标题栏
去掉标题栏,然后重写下面两个方法以实现窗口的拖动,

    def mousePressEvent(self, event):
    def mouseMoveEvent(self, event):
简单地实现了布局后,我们进入到本文重点,就是动态获取屏幕信息

由于MATLAB窗口那边发送了信号,我们在主窗口类实现上,将信号和槽连接起来,一旦信号发生了,槽方法将被调用,槽函数的实现我们详细再看一遍

def showCurImg(self,x,y):
    self.curX = x
    self.curY = y
    screen = QGuiApplication.primaryScreen()
    distance = self.WheelValue + 80
    self.image = QPixmap(screen.grabWindow(0, x-distance,y-distance,distance*2, distance*2))
    self.picture.setPixmap(self.image)
    self.picture.update()

def wheelChange(self,wheelValue):
    self.WheelValue = wheelValue
由于MATLAB窗口发送信号的同时,我们将鼠标当前的位置也发送过来了,所以,在主界面类中,我们先保存当前的鼠标坐标,供后面使用,然后调用Qt封装好的API,获取屏幕,根据滚轮的值,动态获取这个QPixmap,然后,只需要简答地setPixmap,update后就可以了。

功能很简单,没有太强大的功能,但是可以根据同样的思路,实现截图程序,以及屏幕录屏程序等。

就介绍这么多了,对于python博主也是使用不久,主要是因为Qt有了python的实现,所以也那么幸运,可以使用到python制作一个Gui程序。

好了,功能实现的结果就如下了:还有不完善的地方后续将更改,也会分享出来,一方面可以增强自己的记忆,也可以供以后忘了的时候阅读


  • 2
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 在PyQt中使用Matplotlib库进行绘图时,如果需要刷新图表的显示,可以使用以下方法: 1. 使用canvas.draw()方法:在绘图完成后,可以调用canvas对象的draw()方法来刷新图表的显示。这个方法会重新生成并更新绘图的图像,然后显示在界面上。 2. 使用figure.canvas.draw_idle()方法:在绘图完成后,可以调用figure对象的canvas属性的draw_idle()方法来刷新图表的显示。这个方法会该方法会自动调用canvas.draw_idle()方法更新绘图的图像。 3. 使用figure.canvas.update()方法:在绘图完成后,可以调用figure对象的canvas属性的update()方法来刷新图表的显示。这个方法会强制更新绘图的图像,然后显示在界面上。 在以上方法中,一般推荐使用canvas.draw()或figure.canvas.draw_idle()来刷新图表的显示,因为它们能够自动识别需要更新的区域,而不会重复绘制整个图表。 需要注意的是,在使用以上方法进行图表刷新时,需要确保已经在PyQt的事件循环中。可以使用QTimer的singleShot()方法来确保在事件循环中进行图表的刷新,例如: ```python from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import QTimer import matplotlib.pyplot as plt # 绘图函数 def plot(): # 绘图逻辑... # 初始化PyQt应用 app = QApplication([]) # 创建定时器 timer = QTimer() # 定时器到期时调用绘图函数 timer.timeout.connect(plot) # 设置定时器时间间隔(毫秒) timer.setInterval(1000) # 启动定时器 timer.start() # 运行PyQt应用的事件循环 app.exec_() ``` 上述代码中,定时器每隔1秒(1000毫秒)会调用一次绘图函数进行图表刷新。这样可以在PyQt应用的事件循环中进行图表的刷新,保证图表能够实时更新。 ### 回答2: PyQt是Python的一个GUI(图形用户界面)工具包,Matplotlib是一个常用的绘图库。在使用PyQt和Matplotlib进行图形绘制时,我们可能需要实时刷新图形来显示实时数据。 在PyQt中,可以使用QTimer定时器来实现定时刷新。首先,我们需要导入相应的库: ```python from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget from PyQt5.QtCore import QTimer import matplotlib.pyplot as plt ``` 接下来,创建一个QMainWindow的子类,并在构造函数中进行一些初始化操作,例如创建一个QWidget和一个QVBoxLayout来放置Matplotlib绘制的图形: ```python class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Matplotlib Refresh Example") self.layout = QVBoxLayout() self.widget = QWidget() self.widget.setLayout(self.layout) self.setCentralWidget(self.widget) ``` 然后,我们可以在类中添加一个刷新函数,用于进行Matplotlib的图形绘制和更新: ```python def refresh(self): # 清除之前的图形 plt.clf() # 进行新的绘制 plt.plot([1, 2, 3, 4], [1, 4, 9, 16]) plt.xlabel('x') plt.ylabel('y') # 更新图形 plt.draw() ``` 最后,我们在类中添加一个定时器事件,用于定时调用refresh函数进行图形刷新: ```python def timerEvent(self, event): if event.timerId() == self.timer.timerId(): # 调用刷新函数 self.refresh() else: super().timerEvent(event) ``` 在我们的主程序中,我们需要初始化QApplication,创建MainWindow的实例,并启动定时器进行图形的刷新: ```python if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() # 创建定时器 timer = QTimer() timer.timeout.connect(window.refresh) timer.start(1000) # 设置刷新间隔 app.exec_() ``` 通过上述代码,我们可以实现Matplotlib图形的实时刷新,频率为每秒一次。 以上是关于如何在PyQt中使用Matplotlib实现图形的刷新的简要说明。需要注意的是,实际项目中可能会有更多细节和复杂性,具体的实现方式可能会根据具体需求有所变化。 ### 回答3: 在PyQt中使用Matplotlib时,刷新图形的一种常见方法是使用`plt.draw()`函数。该函数用于重新绘制当前活动的图形。但是,在PyQt中,我们不能直接使用`plt.draw()`函数,而是需要将它嵌入到Qt事件循环中。 例如,以下是一个使用PyQt和Matplotlib绘制动态实时图表的示例: ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure import matplotlib.pyplot as plt import numpy as np class MainWindow(QMainWindow): def __init__(self): super().__init__() # 创建一个Matplotlib Figure对象 self.figure = Figure() # 创建一个FigureCanvas对象,并将Figure对象传递给它 self.canvas = FigureCanvas(self.figure) # 创建一个垂直布局,将FigureCanvas对象放入其中 layout = QVBoxLayout() layout.addWidget(self.canvas) # 创建一个QWidget对象,并将布局设置给它 widget = QWidget(self) widget.setLayout(layout) # 设置QWidget对象为主窗口的中央部件 self.setCentralWidget(widget) # 初始化Matplotlib图形 self.ax = self.figure.add_subplot(111) self.x = np.linspace(0, 10, 100) self.line, = self.ax.plot(self.x, np.sin(self.x)) # 开始计时器,并设置每秒触发一次update函数 self.timer = self.startTimer(1000) def timerEvent(self, event): # 更新数据 self.x += 0.1 self.line.set_ydata(np.sin(self.x)) # 重新绘制图形 self.canvas.draw() if __name__ == '__main__': app = QApplication(sys.argv) mainWindow = MainWindow() mainWindow.show() sys.exit(app.exec_()) ``` 在上面的示例中,我们创建了一个继承自QMainWindow的MainWindow类,并重写了timerEvent方法来更新数据和重新绘制图形。在timerEvent方法中,我们先更新了数据(self.x)和图形(self.line),然后调用了self.canvas.draw()方法来重新绘制图形。 此外,我们还使用了startTimer方法创建了一个计时器,并设置每秒触发一次timerEvent方法。这样,我们就可以实现动态实时更新Matplotlib图表的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值