python图形化实例分享--pyqt5与pyqtgraph嵌入绘图

序言

之前也写过一些图形化界面的程序,基本上都是用wxPython写的,确实简单粗暴易上手。这次的任务是要写一个绘图的程序,wx模块就显得不太友好了,我就去网上找了一些资料,发现PyQtGraph画这种K线图、波形图等图形真是太简单了,更多的关于wx、qt等模块的细节学习可以看我后面的参考资料,我这里就分享一下我本程序的心得,和对有些方法使用上自己的理解

项目开始

引用头文件

pyqt5_draw_1 这是主程序文件,负责主窗口图形化界面

import sys  # 与PyQt5配合使用
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout,
                             QHBoxLayout, QVBoxLayout,QLabel, QComboBox,QPushButton,
                             QDateEdit, QSpacerItem,QFrame, QSizePolicy, QSplitter,
                             QRadioButton, QGroupBox,QCheckBox,QLineEdit, QAction)
                             # 上面是QT图形化要引用的所有包
from PyQt5.QtCore import Qt, QDate, QRect  # 对齐、时间等
from PyQt5.QtCore import QThread, pyqtSignal  # 多线程管理
import pyqtgraph as pg  # 绘图包
from Tmp_Data import *  # 自定义文件,下面有介绍
from Mythreading import *  # 自定义文件,下面有介绍
from pyqt5_graph import *  # 自定义文件,下面有介绍

如果PyQt5、pyqtgraph未安装的,最简单的安装方式就用python自带的pip工具安装,如果没有pip的或不会安装可直接百度

c:\> pip install PyQt5 pyqtgraph

图形化主界面搭建

# pyqt5_draw_1.py 文件名
import sys
import cgitb
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout,
                             QHBoxLayout, QVBoxLayout,QLabel, QComboBox,QPushButton,
                             QDateEdit, QSpacerItem,QFrame, QSizePolicy, QSplitter,
                             QRadioButton, QGroupBox,QCheckBox,QLineEdit, QAction)
from PyQt5.QtCore import Qt, QDate, QRect
from TmpData import *
from Mythreading import *
from pyqt5_graph import *


class Qt_Test_Frame(QMainWindow):

    Items = []

    def __init__(self):
        #super(Qt_Test_Frame, self).__init__(*args, **kw)
        super().__init__()

        # 初始化界面
        self._initUI()

        self.show()

    def _initUI(self):
        self.setWindowTitle("QT图形界面测试")
        self.resize(800, 600)

        wwg = QWidget()

        # 全局布局
        wlayout = QVBoxLayout()
        h1_wlayout = QHBoxLayout()
        h2_wlayout = QHBoxLayout()
        h3_wlayout = QHBoxLayout()
        v4_wlayout = QVBoxLayout()
        v5_wlayout = QVBoxLayout()

        self.statusBar().showMessage("状态栏")

        # 第一层
        self._frist_story(h1_wlayout)

        # 第二层
        self._second_story(h2_wlayout)

        # 第三层 左
        self._third_left(v4_wlayout, v5_wlayout)

        # 第三层 右
        self._fouth_right(v5_wlayout)

        # 加载
        splt = self._my_line()
        splt2 = self._my_line(False)
        wlayout.addSpacing(10)  # 增加布局间距
        wlayout.addLayout(h1_wlayout)
        wlayout.addSpacing(10)  # 增加布局间距
        wlayout.addLayout(h2_wlayout)
        wlayout.addSpacing(10)  # 增加布局间距
        wlayout.addWidget(splt)
        wlayout.addLayout(h3_wlayout)
        wlayout.addWidget(self.statusBar())
        h3_wlayout.addLayout(v4_wlayout, 0)
        h3_wlayout.addWidget(splt2)
        h3_wlayout.addLayout(v5_wlayout, 2)

        #wlayout.setAlignment(Qt.AlignTop)

        wwg.setLayout(wlayout)
        self.setCentralWidget(wwg)

    def _frist_story(self, h1_wlayout):
        # 第一层布局
        self.h1_combox1 = QComboBox(minimumWidth=100)
        self.h1_combox1.addItems(wind_field)
        self.h1_combox2 = QComboBox(minimumWidth=100)
        self.h1_combox2.addItems(wind_mach_chooice(self.h1_combox1.currentText()))
        self.h1_combox3 = QComboBox(minimumWidth=100)
        self.h1_combox3.addItems(wind_blade)
        self.h1_combox4 = QComboBox(minimumWidth=100)
        self.h1_combox4.addItems(signal_type)

        # 行为测试 暂时无法使用
        h1_cb1_action = QAction("风场选择", self)
        h1_cb1_action.setStatusTip("请选择风场")
        self.h1_combox1.addAction(h1_cb1_action)

        h1_wlayout.addItem(QSpacerItem(20, 20))
        h1_wlayout.addWidget(QLabel("风场"),0)
        h1_wlayout.addWidget(self.h1_combox1,0)
        h1_wlayout.addItem(QSpacerItem(40, 20))
        h1_wlayout.addWidget(QLabel("风机"), 0)
        h1_wlayout.addWidget(self.h1_combox2, 0)
        h1_wlayout.addItem(QSpacerItem(40, 20))
        h1_wlayout.addWidget(QLabel("叶片ID"), 0)
        h1_wlayout.addWidget(self.h1_combox3, 0)
        h1_wlayout.addItem(QSpacerItem(40, 20))
        h1_wlayout.addWidget(QLabel("信号类型"), 0)
        h1_wlayout.addWidget(self.h1_combox4, 0)

        h1_wlayout.setAlignment(Qt.AlignLeft)

        # 事件绑定
        self.h1_combox1.currentIndexChanged.connect(self._wind_chooice)

    def _second_story(self, h2_wlayout):
        # 第二层布局
        self.h2_date1 = QDateEdit(QDate.currentDate())
        self.h2_date1.setCalendarPopup(True)
        self.h2_date2 = QDateEdit(QDate.currentDate())
        self.h2_date2.setCalendarPopup(True)
        self.h2_button = QPushButton("运行")
        self.h2_button2 = QPushButton("停止")

        h2_wlayout.addItem(QSpacerItem(20, 20))
        h2_wlayout.addWidget(QLabel("起始"),0)
        h2_wlayout.addWidget(self.h2_date1)
        h2_wlayout.addItem(QSpacerItem(50, 20))
        h2_wlayout.addWidget(QLabel("结束"), 0)
        h2_wlayout.addWidget(self.h2_date2)
        h2_wlayout.addItem(QSpacerItem(70, 20))
        h2_wlayout.addWidget(self.h2_button)
        h2_wlayout.addWidget(self.h2_button2)

        h2_wlayout.setAlignment(Qt.AlignLeft)

        # 事件绑定
        self.h2_button.clicked.connect(lambda: self._start_func())
        self.h2_button2.clicked.connect(lambda: self._stop_func())

    def _third_left(self, v4_wlayout, v5_wlayout):
        # 第三层布局
        # 分量布局
        v4_group_imf = QGridLayout()
        vbox1 = QGroupBox("分量值")
        self.radio_1 = QRadioButton("分量1")
        self.radio_2 = QRadioButton("分量2")
        self.radio_3 = QRadioButton("分量3")
        self.radio_4 = QRadioButton("分量4")
        self.radio_5 = QRadioButton("分量5")
        self.radio_6 = QRadioButton("分量6")
        self.radio_7 = QRadioButton("分量7")
        self.radio_8 = QRadioButton("分量8")
        self.radio_9 = QRadioButton("分量9")
        self.radio_1.setChecked(True)
        self.radio_val = self.radio_1.text()

        # 优先级布局
        v4_group_prior = QGridLayout()
        vbox2 = QGroupBox("优先级")
        cb1 = QCheckBox("叶片1")
        cb2 = QCheckBox("叶片2")
        cb3 = QCheckBox("叶片3")
        self.v4_lineEdit = QLineEdit()

        # 时间布局
        v4_group_time = QGridLayout()
        vbox3 = QGroupBox("时间选择")
        self.v4_combox1 = QComboBox(minimumWidth=100)
        self.v4_combox1.addItem("空")

        # 按键
        v4_button = QPushButton("显示图形")

        # 写入网格格布局
        v4_group_imf.addWidget(self.radio_1, 0, 0)
        v4_group_imf.addWidget(self.radio_2, 0, 1)
        v4_group_imf.addWidget(self.radio_3, 1, 0)
        v4_group_imf.addWidget(self.radio_4, 1, 1)
        v4_group_imf.addWidget(self.radio_5, 2, 0)
        v4_group_imf.addWidget(self.radio_6, 2, 1)
        v4_group_imf.addWidget(self.radio_7, 3, 0)
        v4_group_imf.addWidget(self.radio_8, 3, 1)
        v4_group_imf.addWidget(self.radio_9, 4, 0)

        v4_group_prior.addWidget(cb1, 1, 0)
        v4_group_prior.addWidget(cb2, 2, 0)
        v4_group_prior.addWidget(cb3, 3, 0)
        v4_group_prior.addWidget(QLabel("选择是:"),4,0)
        v4_group_prior.addWidget(self.v4_lineEdit, 5, 0)

        v4_group_time.addWidget(self.v4_combox1)

        # 写入左侧布局
        vbox1.setLayout(v4_group_imf)
        vbox2.setLayout(v4_group_prior)
        vbox3.setLayout(v4_group_time)
        v4_wlayout.addItem(QSpacerItem(50, 20))
        v4_wlayout.addWidget(vbox1)
        v4_wlayout.addItem(QSpacerItem(50, 20))
        v4_wlayout.addWidget(vbox2)
        v4_wlayout.addItem(QSpacerItem(50, 20))
        v4_wlayout.addWidget(vbox3)
        v4_wlayout.addItem(QSpacerItem(50, 20))
        v4_wlayout.addWidget(v4_button)
        v4_wlayout.addItem(QSpacerItem(50, 20))

        # 事件绑定
        self.radio_1.toggled.connect(lambda: self._changestyle(self.radio_1))
        self.radio_2.toggled.connect(lambda: self._changestyle(self.radio_2))
        self.radio_3.toggled.connect(lambda: self._changestyle(self.radio_3))
        self.radio_4.toggled.connect(lambda: self._changestyle(self.radio_4))
        self.radio_5.toggled.connect(lambda: self._changestyle(self.radio_5))
        self.radio_6.toggled.connect(lambda: self._changestyle(self.radio_6))
        self.radio_7.toggled.connect(lambda: self._changestyle(self.radio_7))
        self.radio_8.toggled.connect(lambda: self._changestyle(self.radio_8))
        self.radio_9.toggled.connect(lambda: self._changestyle(self.radio_9))

        cb1.stateChanged.connect(lambda: self._prior_func(cb1))
        cb2.stateChanged.connect(lambda: self._prior_func(cb2))
        cb3.stateChanged.connect(lambda: self._prior_func(cb3))

        v4_button.clicked.connect(lambda: self._show_func(v5_wlayout))

    def _fouth_right(self, v5_wlayout):
        # 加载波形图
        self.tmp_plt = plt_init()
        v5_wlayout.addWidget(self.tmp_plt)

    def _my_line(self, var=True):
        # var 为True时,为横线,否则为竖线
        line = QFrame(self)
        line_var = QFrame.HLine
        sp_var = Qt.Horizontal
        if not var:
            line_var = QFrame.VLine
            sp_var = Qt.Vertical
        line.setFrameShape(line_var)
        line.setFrameShadow(QFrame.Sunken)
        splitter = QSplitter(sp_var)
        splitter.addWidget(line)
        return splitter

    def _wind_chooice(self):
        tmp_list = wind_mach_chooice(self.h1_combox1.currentText())
        self.h1_combox2.clear()
        self.h1_combox2.addItems(tmp_list)

    def _start_func(self):
        a = self.h1_combox1.currentText()
        b = self.h1_combox2.currentText()
        c = self.h1_combox3.currentText()
        d = self.h1_combox4.currentText()
        e = self.h2_date1.dateTime().toString("yy-MM-dd")
        f = self.h2_date2.dateTime().toString("yy-MM-dd")
       	# 多线程的引用
        self.start_func = RunThread(target=self._start_thread, args=(a, b, c, d, e, f))
        # 多线程启动
        self.start_func.start()

    def _stop_func(self):
    	# 线程停止
        self.start_func.stop()
        print("运行结束")

    def _start_thread(self, a, b, c, d, e, f):
        print("*****运行打印*****")
        print(wind_mach_chooice(a))
        print(a,b,c,d)
        print(e)
        print(f)
        print("%s" % (time.strftime('<%H:%M:%S>', time.localtime())))
        self.v4_combox1.clear()
        self.v4_combox1.addItems(tmp_time_list)
        print("*****运行打印*****")

    def _changestyle(self, btn):
        # 单选项的判断函数
        if btn.isChecked():
            self.radio_val = btn.text()
        #print("%s"%(time.strftime('<%H:%M:%S>', time.localtime())))

    def _prior_func(self, cb):
        # 复选框内容添加
        if cb.isChecked():
            if cb.text()[-1] not in self.Items:
                self.Items.append(cb.text()[-1])
            shop_cart= ",".join(self.Items)
            self.v4_lineEdit.setText(shop_cart)
        else:
            if cb.text()[-1] in self.Items:
                self.Items.remove(cb.text()[-1])
            shop_cart = ",".join(self.Items)
            self.v4_lineEdit.setText(shop_cart)

    def _show_func(self, v5_wlayout):
        print("*****显示打印*****")
        print(self.radio_val)
        num = self.v4_lineEdit.text()
        print(self.v4_combox1.currentText())
        v5_wlayout.removeWidget(self.tmp_plt)
        self.tmp_plt = plt_show(num)
        v5_wlayout.addWidget(self.tmp_plt)
        print("*****显示打印*****")


if __name__ == '__main__':
	cgitb.enable(format="text")
    app = QApplication(sys.argv)
    win = Qt_Test_Frame()
    sys.exit(app.exec_())

处理把列表文件转成字典与绘图

# pyqt5_graph.py 文件名 
import pyqtgraph as pg
from TmpData import _read_data, wind_mach_chooice

colour = ["r", "g", "b"]
yp_list = ["叶片1", "叶片2", "叶片3"]

def _data_to_dict():
    mydict = {}
    for my_vars, i in zip(_read_data(), range(len(_read_data()))):
        tmp_dict = {}
        for var, j in zip(my_vars, range(len(my_vars))):
            tmp_dict[var[0]] =var[1]
        mydict[i] = tmp_dict
    return mydict

def plt_init():
	# 绘图初始化
    pg.setConfigOption("background", "w")
    plt = pg.PlotWidget()
    plt.addLegend(size=(150, 80))
    plt.showGrid(x=True, y=True, alpha=0.5)
    return plt

def plt_show(num):
	# 传绘制的新图
    mydict = _data_to_dict()
    pg.setConfigOption("background", "w")
    plt = pg.PlotWidget()
    plt.addLegend(size=(150, 80))
    plt.showGrid(x=True, y=True, alpha=0.5)
    for i in num.split(","):
        i = int(i)-1
        plt.plot(x=list(mydict[i].keys()), y=list(mydict[i].values()), pen=colour[i],
                 name=yp_list[i])
  
    return plt

if __name__ == '__main__':
    _data_to_dict()
    pass

模拟给其它文件传指定数据

# TmpData.py 文件名
import os
import numpy as np

file_path = os.path.join(os.getcwd(), "风机采集信号数据\\")

wind_field = ["风场1", "风场2", "风场3"]
wind_machine = {"风场1":["大别山", "天目山"],
                "风场2":["昆仑山", "三清山"],
                "风场3":["五指山", "火焰山"]}
wind_blade = ["X-20Hz", "X-1K", "Y-20Hz", "Y-1K"]
signal_type = ["包络", "振动"]

tmp_time_list = ["20190501", "20190502", "20190504", "20190508", "20190515"]

def wind_mach_chooice(val):
    return wind_machine[val]

def _read_data():
    file_list = os.listdir(file_path)
    file_list = [var for var in file_list if var.split(".")[1] == "csv"]
    a = []
    for var in file_list:
        tmp = os.path.join(file_path, var)
        rd_file = np.loadtxt(tmp, delimiter=",", usecols=(0, 1))
        a.append(rd_file)
    return a[0], a[1], a[2]

多线程管理

因为程序运行时间久,主界面就会出现假死的状态,要引用多线程

# Mythreading.py 文件名
from PyQt5.QtCore import QThread, pyqtSignal

class RunThread(QThread):

    counter_value = pyqtSignal(int)

    def __init__(self, target, args, name=""):
        QThread.__init__(self)
        self.target = target
        self.args = args
        self.is_running = True

    def run(self):
        #print("starting",self.name, "at:",ctime())
        self.res = self.target(*self.args)

    def stop(self):
    	# 负责停止线程
        self.terminate()

关于QT异常直接退出没有报错的情况,查bug比较麻烦

import cgitb

# 这句放在所有程序开始前,这样就可以正常打印异常了
cgitb.enable(format="text")

这些天本人身体不舒服,但还是把做完的这个分享出来,有些细节没有具体说明,下次身体好点,再单独拿出来分享,累了,晚安!

参考资料

本程序的源代码分享 https://github.com/zhouf00/pyqt5-my/tree/master
wxpython 大多数的功能介绍都有 https://www.yiibai.com/wxpython/wx_dialog_class.html
wxpython 内容很全,英文要好 https://wxpython.org/Phoenix/docs/html/index.html
QT、PyQtGraph这个网址写的不错,可以简单入门 https://zmister.com/archives/187.html


本人的经验分享,希望可以帮助到你们,如何不对的地方,可以评论留言,帮我指正一下,如果帮助了你,请给我点个赞吧

  • 13
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值