[wxpython] How to use wx.lib.plot basic & time axis

Introduction

This is the first post! I look forward to working with you!

There is an opportunity to draw a graph in real time in the GUI, and it is a summary at that time. Since the GUI used wxpython, I used wx.lib.plot in the same wx module. I had a hard time finding articles on the time axis, so I hope you find it helpful.

environment

mac OS python 3.8.5 wxpython 4.1.0

import

python


import wx
import wx.lib
import wx.lib.plot as plot

Basic

Arrange in the order of wx.Frame-> wx.Panel-> plot.Canvas.

python


import wx
import wx.lib
import wx.lib.plot as plot
#Used for graph drawing
import random

#Value to draw
x_val = list(range(10))
y_val = [random.randint(0, 10) for i in range(10)]  # 0~10 random values up to 10

# [(x1, y1), (x2, y2),...(xn, yn)]Transformed to pass to the graph in the format
xy_val = list(zip(x_val, y_val))


class MainFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(500, 500))
        #panel creation
        self.panel = wx.Panel(self, -1)
        #graph creation
        self.plotter = plot.PlotCanvas(self, -1)
        #Create a line to display&drawing
        line = plot.PolyLine(xy_val)
        gc = plot.PlotGraphics([line])
        self.plotter.Draw(gc)

        #sizer creation&Installation
        sizer = wx.GridSizer(1, 1, gap=(0, 0))
        sizer.Add(self.plotter, flag=wx.EXPAND)
        self.SetSizer(sizer)

        #Display GUI in the center of the screen
        self.Center()
        self.Show()


def main():
    app = wx.App()
    MainFrame(None, -1, 'WxLibPlot')
    app.MainLoop()


if __name__ == '__main__':
    main()

result WxLibPlot1.png

Layout

Add graph title, legend, label Enable zoom or drag function Change font size, line color and weight

Zoom and drag cannot coexist, so enabling one disables the other.

Change the code below self.plotter = plot.PlotCanvas (self, -1) in the above code.

python


self.plotter.enableLegend = True  #Set the legend display to True
self.plotter.fontSizeTitle = 18  #Set the font size of the graph title to 18.(Default=15)
self.plotter.fontSizeLegend = 18  #Set the font size of the legend to 18.(Default=7)
self.plotter.fontSizeAxis = 18  #xy label,Set the font size of the coordinates to 18.(Default=10)

#Enable the zoom or drag function. Only one can be enabled
# self.plotter.enableZoom = True
self.plotter.enableDrag = True

#Create a line to display&drawing()
line = plot.PolyLine(xy_val, legend='sample', colour='red', width=4)  #Added legend text, changed line color to red and thickness to 4
gc = plot.PlotGraphics([line], 'WxLibPlot', 'xaxis', 'yaxis')  #Graph title and xy label added

result Drag is enabled. WxLibPlot2.gif

Real-time drawing

Use wx.Timer to draw a line graph that takes a random value every second. This time, we have added a button to control the start and end of the plot.

python


import wx
import wx.lib
import wx.lib.plot as plot
#Used for graph drawing
import random


class MainFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(500, 500))
        #panel creation
        self.panel = wx.Panel(self, -1)
        #Button for start and stop measurement
        self.start_button = wx.Button(self, -1, label='start')
        self.stop_button = wx.Button(self, -1, label='stop')
        #graph creation
        self.plotter = plot.PlotCanvas(self, -1)
        #Initial value of plot point
        self.x_val = 0
        self.xy_val = []

        self.plotter.enableLegend = True  #Set the legend display to True
        self.plotter.fontSizeTitle = 18  #Set the font size of the graph title to 18.(Default=15)
        self.plotter.fontSizeLegend = 18  #Set the font size of the legend to 18.(Default=7)
        self.plotter.fontSizeAxis = 18  #xy label,Set the font size of the coordinates to 18.(Default=10)

        #Enable the zoom or drag function. Only one can be enabled
        # self.plotter.enableZoom = True
        # self.plotter.enableDrag = True

        #Create a line to display&drawing()
        line = plot.PolyLine(self.xy_val, legend='sample', colour='red', width=2)  #Added legend text, changed line color to red and thickness to 4
        gc = plot.PlotGraphics([line], 'RealTimePlot', 'xaxis', 'yaxis')  #Graph title and xy label added
        self.plotter.Draw(gc)

        #Create a frame timer
        self.timer = wx.Timer(self)

        #sizer creation&Installation
        sizer1 = wx.FlexGridSizer(2, 1, gap=(0, 0))  #Sizar for arranging 2 rows and 1 column graph and sizer2
        sizer2 = wx.GridSizer(1, 2, gap=(0, 0))  #Sizar for arranging buttons in 1 row and 2 columns

        sizer1.Add(self.plotter, flag=wx.EXPAND)  # flag=wx.EXPAND:Extend the width and height to the maximum
        sizer1.Add(sizer2, flag=wx.ALIGN_RIGHT)  # flag=wx.ALIGN_RIGHT:Installed to the right
        sizer1.AddGrowableCol(0)  #Extend the width of the first row to the maximum
        sizer1.AddGrowableRow(0)  #Extend the height of the first line to the maximum
        #Button installation
        sizer2.Add(self.start_button)
        sizer2.Add(self.stop_button)

        self.SetSizer(sizer1)

        #Events
        self.start_button.Bind(wx.EVT_BUTTON, self.graph_start)
        self.stop_button.Bind(wx.EVT_BUTTON, self.graph_stop)
        self.Bind(wx.EVT_TIMER, self.graph_plot)

        #Display GUI in the center of the screen
        self.Center()
        self.Show()

    def graph_start(self, event):
        self.plotter.Clear()  #Initialize the graph
        self.x_val, self.xy_val = 0, []  # x_val, xy_Initialize val
        self.timer.Start(1000)

    def graph_stop(self, event):
        self.timer.Stop()

    def graph_plot(self, event):
        y_val = random.randint(0, 100)
        self.xy_val.append((self.x_val, y_val))
        line = plot.PolyLine(self.xy_val, legend='sample', colour='red', width=2)
        gc = plot.PlotGraphics([line], 'RealTimePlot', 'xaxis', 'yaxis')
        self.plotter.Draw(gc)
        self.x_val += 1


def main():
    app = wx.App()
    MainFrame(None, -1, 'WxLibPlot')
    app.MainLoop()


if __name__ == '__main__':
    main()

result The reason why it turns white once after pressing the start button is because the graph is initialized. WxLibPlot3.gif

The drawing method changes by changing the attribute called xSpec

python


 self.plotter.xSpec = 'auto' # or 'min', int, (min, max), 'none'

WxLibPlot4.gif

From the top, it will be'auto','min', int, (min, max). 'auto' is the default. If you assign an integer type, the behavior is the same as'min', but the coordinates are fixed. In the above example, the graph is divided into 4 parts and the coordinates are displayed on the boundary.

'none' is omitted because it only erases the x-axis and label.

Time axis display

This is the part I wanted to write the most. Immediately, I will write the code and the result. The time interval is 1 second.

python


import datetime as dt
import random
import wx
import wx.lib
import wx.lib.plot as plot


class TimeAxisPlot(plot.PlotCanvas):
    def __init__(self, parent, id):
        plot.PlotCanvas.__init__(self, parent, id)
        #Define layout relationships
        self.enableLegend = True
        self.fontSizeLegend = 18
        self.fontSizeAxis = 18
        self.xSpec = 4

        self.startDate = dt.datetime.now()
        self.data = []
        line = plot.PolyLine(self.data, legend='sample', colour='red')
        gc = plot.PlotGraphics([line], 'TimeAxisPlot', 'time', 'yaxis')
        self.Draw(gc)

    def _xticks(self, *args):
        ticks = plot.PlotCanvas._xticks(self, *args)
        # ticks = [(Plot points,Character to display), (),...()]
        new_ticks = []
        for tick in ticks:
            t = tick[0]
            time_value = self.startDate + dt.timedelta(seconds=t)
            time_value_str = time_value.strftime('%H:%M:%S')
            new_ticks.append([t, time_value_str])
            # new_ticks = [(Plot points,Times of Day), (),...()]
            #Change the displayed characters to time
        return new_ticks


class MainFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(500, 500))
        #panel creation
        self.panel = wx.Panel(self, -1)
        #Button for start and stop measurement
        self.start_button = wx.Button(self, -1, label='start')
        self.stop_button = wx.Button(self, -1, label='stop')
        #graph creation
        self.plotter = TimeAxisPlot(self, -1)
        #Initial value of plot point
        self.x_val = 0
        self.xy_val = []

        self.plotter.enableLegend = True  #Set the legend display to True
        self.plotter.fontSizeTitle = 18  #Set the font size of the graph title to 18.(Default=15)
        self.plotter.fontSizeLegend = 18  #Set the font size of the legend to 18.(Default=7)
        self.plotter.fontSizeAxis = 18  #xy label,Set the font size of the coordinates to 18.(Default=10)

        #Create a line to display&drawing()
        line = plot.PolyLine(self.xy_val, legend='sample', colour='red', width=2)  #Added legend text, changed line color to red and thickness to 4
        gc = plot.PlotGraphics([line], 'RealTimePlot', 'xaxis', 'yaxis')  #Graph title and xy label added
        self.plotter.Draw(gc)

        #Create a frame timer
        self.timer = wx.Timer(self)

        #sizer creation&Installation
        sizer1 = wx.FlexGridSizer(2, 1, gap=(0, 0))  #Sizar for arranging 2 rows and 1 column graph and sizer2
        sizer2 = wx.GridSizer(1, 2, gap=(0, 0))  #Sizar for arranging buttons in 1 row and 2 columns

        sizer1.Add(self.plotter, flag=wx.EXPAND)  # flag=wx.EXPAND:Extend the width and height to the maximum
        sizer1.Add(sizer2, flag=wx.ALIGN_RIGHT)  # flag=wx.ALIGN_RIGHT:Installed to the right
        sizer1.AddGrowableCol(0)  #Extend the width of the first row to the maximum
        sizer1.AddGrowableRow(0)  #Extend the height of the first line to the maximum
        #Button installation
        sizer2.Add(self.start_button)
        sizer2.Add(self.stop_button)

        self.SetSizer(sizer1)

        #Events
        self.start_button.Bind(wx.EVT_BUTTON, self.graph_start)
        self.stop_button.Bind(wx.EVT_BUTTON, self.graph_stop)
        self.Bind(wx.EVT_TIMER, self.graph_plot)

        #Display GUI in the center of the screen
        self.Center()
        self.Show()

    def graph_start(self, event):
        self.plotter.Clear()  #Initialize the graph
        self.x_val, self.xy_val = 0, []  # x_val, xy_Initialize val
        self.timer.Start(1000)

    def graph_stop(self, event):
        self.timer.Stop()

    def graph_plot(self, event):
        y_val = random.randint(0, 100)
        self.xy_val.append((self.x_val, y_val))
        line = plot.PolyLine(self.xy_val, legend='sample', colour='red', width=2)
        gc = plot.PlotGraphics([line], 'RealTimePlot', 'xaxis', 'yaxis')
        self.plotter.Draw(gc)
        self.x_val += 1


def main():
    app = wx.App()
    MainFrame(None, -1, 'TimeAxisPlot')
    app.MainLoop()


if __name__ == '__main__':
    main()

result WxLibPlot5.gif

Inherit plot.Canvas to create a new class, overriding the x-axis method. here,

python


    def _xticks(self, *args):
        ticks = plot.PlotCanvas._xticks(self, *args)
        # ex)ticks = [(0, '0.0'), (0.5, '0.5'), (1.0, '1.0'), (1.5, '1.5'), (2.0, '2.0')]
        # [(x coordinate(float)), (x coordinateに表示する文字(str))...]

ticks returns [(x coordinate (float)), (character to display in x coordinate (str)) ...]. The time axis is created by changing the displayed characters to the time.

Method Create a tuple of coordinates and time and append to the empty list new_ticks ex) Start time: 0:00, ticks = [(0, '0.0'), (0.5, '0.5'), (1.0, '1.0'), (1.5, '1.5'), (2.0, '2.0' )]time

for sentence 1st

  1. tick = (0, '0.0')
  2. t = 0
  3. Create a date t seconds off the start time and transform it into str (= 00:00)
  4. append (0, '00: 00') to new_ticks The following loop

python


        new_ticks = []
        for tick in ticks:  #Loop with the acquired ticks
            #0th tuple(x coordinate point)Get
            t = tick[0]
            time_value = self.startDate + dt.timedelta(seconds=t)
            time_value_str = time_value.strftime('%H:%M:%S')
            new_ticks.append([t, time_value_str])
            # new_ticks = [(Plot points,Times of Day), (),...()]
            #Change the displayed characters to time
        return new_ticks

Finally

Thank you for reading. As I noticed during execution, the fastest drawing speed was about 0.05s. Note that the execution speed did not change even if the timer interval was shorter than this.

Reference site

official wx.lib.plot — wxPython Phoenix 4.1.1a1 documentation

Reference for real-time drawing wxPython: Draw animation and graph at the same time

Time axis reference [Note] Customize the y-axis scale of the plot ─ wxPython wxPython-users - wx.lib.plot custom labels?

Recommended Posts

[wxpython] How to use wx.lib.plot basic & time axis
How to use Jupyter notebook [Super Basic]
How to use xml.etree.ElementTree
How to use Python-shell
How to use tf.data
How to use virtualenv
How to use MkDocs for the first time
How to use Seaboan
How to use image-match
How to use shogun
How to achieve time wait processing with wxpython
How to use Virtualenv
How to use numpy.vectorize
How to use pytest_report_header
How to use partial
How to use Bio.Phylo
How to use SymPy
How to use x-means
How to use WikiExtractor.py
How to use virtualenv
How to use Matplotlib
How to use iptables
How to use numpy
How to use TokyoTechFes2015
How to use venv
How to use Pyenv
How to use list []
How to use python-kabusapi
How to use OptParse
How to use return
How to use dotenv
How to use pyenv-virtualenv
How to use Go.mod
How to use imutils
How to use import
Beginners! Basic Linux commands and how to use them!
How to use Qt Designer
How to use search sorted
python3: How to use bottle (2)
Understand how to use django-filter
How to use the generator
How to use FastAPI ③ OpenAPI
How to use Python argparse
How to use IPython Notebook
How to use Pandas Rolling
[Note] How to use virtualenv
How to use redis-py Dictionaries
[Python] How to use checkio
[Go] How to use "... (3 periods)"
[Python] How to use input ()
How to use the decorator
[Introduction] How to use open3d
How to use Python lambda
How to use Jupyter Notebook
[Python] How to use virtualenv
python3: How to use bottle (3)
python3: How to use bottle
How to increase the axis
How to use Google Colaboratory
How to use Python bytes
How to use the zip function