• wxPython 绘图演示——模拟雷达信号图(2)



    参考此前 wxpython 实现简易画板(1),wxPython 实现绘图有直接法和缓冲法。推荐使用缓冲法来避免屏闪。

    1.1. 基本框架

    下面以“雷达信号图”为例,回顾 wxPython 的绘图的基本框架:

    class RadarGraph(wx.Window):
    
        def __init__(self, *args, **kw):
            super().__init__(*args, **kw)
    
            # 自定义的数据
            # Add your codes
            
            # 设置并重绘缓冲区
            self.buffer = None # 缓冲区
            self.InitBuffer()
    
            # 绑定事件
            self.Bind(wx.EVT_SIZE, self.OnSize)
            self.Bind(wx.EVT_PAINT, self.OnPaint)
            # add other events you need
    
        def InitBuffer(self):
            '''设置并重绘缓冲区'''
            w, h = self.GetClientSize()
            self.buffer = wx.Bitmap(w, h)
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
            
            # 你的绘制内容
            self.DrawGraph(dc)
        
        def DrawGraph(self, dc:wx.BufferedPaintDC):
            pass
    
        # ----- 事件响应函数 -----
        def OnSize(self, event):
            """响应窗口大小改变"""
    
            # 每次窗口大小变换,都需要重新设置缓冲区大小,重绘窗口
            self.InitBuffer()
        
        def OnPaint(self, event):
            """响应Paint Event"""
    
            wx.BufferedPaintDC(self, self.buffer)
    

    1.2. 运行结果

    设置了一个定时器 wx.Timer,让数据随着时间而变化,形成一个动图效果。

    运行结果

    1.3. 完整代码

    # -*- encoding: utf-8 -*-
    # Python 3.9.6 64bit
    '''
    @File        : a_sample_radar_graph.py
    @Time        : 2022/01/04 11:02
    @Author      : Wreng
    @Description : 雷达信号图,参考 Python in action / Chapter 12 / example 12.2
    @Other       : version - Python 3.9.6 64bit, wxPython 4.1.1
    '''
    
    
    import wx
    import math
    import random
    
    class RadarGraph(wx.Window):
    
        def __init__(self, parent, title, labels):
            super().__init__(parent)
    
            # 窗口标题
            self.title = title
            self.titleFont = wx.Font(14, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
    
            # 图中的标签
            self.labels = labels
            self.labelFont = wx.Font(10, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
    
            self.data = [0.0] * len(labels)
            self.xy = None
            
            self.buffer = None # 缓冲区
    
            # 设置并重绘缓冲区
            self.InitBuffer()
    
            # 绑定事件
            self.Bind(wx.EVT_SIZE, self.OnSize)
            self.Bind(wx.EVT_PAINT, self.OnPaint)
        
        def InitBuffer(self):
            '''创建缓冲区'''
            w, h = self.GetClientSize()
            self.buffer = wx.Bitmap(w, h)
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
            
            # 你的绘制内容
            self.DrawGraph(dc) 
    
        def DrawGraph(self, dc:wx.BufferedPaintDC):
            '''绘制图形'''
            spacer = 10
            scaledmax = 150.0
            
            dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
            dc.Clear()
            dw, dh = dc.GetSize()
    
            # 绘制标题
            dc.SetFont(self.titleFont)
            tw, th = dc.GetTextExtent(self.title)
            dc.DrawText(self.title, (dw-tw)/2, spacer)
    
            # 找到中心点
            th = th + 2*spacer
            cx = dw / 2
            cy = (dh - th)/2 + th
    
            # 计算比率
            mindim = min(cx, (dh - th)/2)
            scale = mindim / scaledmax
    
            # 绘制轴线,以25为刻度
            dc.SetPen(wx.Pen("black", 1))
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            dc.DrawCircle(cx, cy, 25*scale)
            dc.DrawCircle(cx, cy, 50*scale)
            dc.DrawCircle(cx, cy, 75*scale)
            dc.DrawCircle(cx, cy, 100*scale)
    
            dc.SetPen(wx.Pen("black", 2))
            dc.DrawLine(cx-110*scale, cy, cx+110*scale, cy)
            dc.DrawLine(cx, cy-110*scale, cx, cy+110*scale)
    
            # 寻找了每个点的坐标,绘制标签以及最大的数据点
            dc.SetFont(self.labelFont)
            maxval = 0
            angle = 0
            polypoints = []
            for i, label in enumerate(self.labels):
                val = self.data[i]
                point = self.PolarToCartesian(val*scale, angle, cx, cy) # 将极坐标转化为直角坐标
                polypoints.append(point)
                x, y = self.PolarToCartesian(125*scale, angle, cx, cy)
                dc.DrawText(label, x, y) # 绘制标签
                if val > maxval:
                    maxval = val
                angle = angle + 360 / len(self.labels)
        
            # 根据最大值确定brush的颜色, green is good; red is bad
            c = "forest green"
            if maxval > 70:
                c = "yellow"
            if maxval > 95:
                c = "red"
            dc.SetBrush(wx.Brush(c)) # 设置画刷的颜色
            dc.SetPen(wx.Pen("navy", 3))
            dc.DrawPolygon(polypoints) # 绘制采集的形状
    
        def PolarToCartesian(self, radius, angle, cx,cy):
            '''将极坐标转化为直角坐标'''
            x = radius * math.cos(math.radians(angle))
            y = radius * math.sin(math.radians(angle))
            return (int(cx+x+0.5), int(cy+y+0.5)) # 四舍五入转化为整数
    
        def GetData(self):
            return self.data
        
        def SetData(self, newData):
            assert len(newData) == len(self.data)
            self.data = newData[:]
    
            # 数据改变了,因此更新缓冲和窗口
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
            self.DrawGraph(dc)
    
        # ====================================================================
        # 事件响应函数
        # ====================================================================
        def OnSize(self, event):
            """响应窗口大小改变"""
    
            # 每次窗口大小变换,都需要重新设置缓冲区大小,重绘窗口
            self.InitBuffer()
        
        def OnPaint(self, event):
            """响应Paint Event"""
    
            wx.BufferedPaintDC(self, self.buffer)
    
    
    class TestFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, title="Double Buffered Drawing", size=(480, 480))
            self.plot = RadarGraph(self, "Sample Radar Plot", ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'])
    
            data = []
            for d in self.plot.GetData():
                data.append(random.randint(0, 75))
            self.plot.SetData(data)
    
            # 设置一个定时器,使数据随时间随机变化
            self.timer = wx.Timer(self)
            self.timer.Start(500) # 每 500ms 触发一次 EVT_TIMER
    
            self.Bind(wx.EVT_TIMER, self.OnTimeout)
    
        def OnTimeout(self, event):
            data = []
            for d in self.plot.GetData():
                val = d + random.uniform(-5, 5)
                if val < 0:
                    val = 0
                if val > 110:
                    val = 110
                data.append(val)
            self.plot.SetData(data)
    
    if __name__ == '__main__':
        app = wx.App()
        frm = TestFrame()
        frm.Show()
        app.MainLoop()
    

    1.4. 相关参考

  • 相关阅读:
    sync 解释
    USB枚举详细过程剖析(转)
    内核早期内存分配器:memblock
    LTE:eMBMS架构
    对linux内核中jiffies+Hz表示一秒钟的理解
    android的USB MTP && USB CDC/USBnet(ECM, NCM, ACM) && USB gardget
    Install Shield中调用devcon自动安装硬件驱动程序
    利用 devcon.exe实现自动安装驱动(转)
    linux系统IO调度算法
    ZooKeeper原理详解及常用操作
  • 原文地址:https://www.cnblogs.com/wreng/p/15761776.html
Copyright © 2020-2023  润新知