wxmplot 0.9.59__tar.gz → 0.9.60__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. wxmplot-0.9.60/PKG-INFO +163 -0
  2. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/leftright.py +1 -1
  3. wxmplot-0.9.60/examples/multiple_yaxes.py +22 -0
  4. wxmplot-0.9.60/examples/scope_mode_mpl_compare.py +28 -0
  5. wxmplot-0.9.60/examples/stripchart2.py +197 -0
  6. wxmplot-0.9.60/pyproject.toml +63 -0
  7. wxmplot-0.9.60/setup.cfg +4 -0
  8. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/basepanel.py +31 -10
  9. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/config.py +161 -113
  10. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/imageframe.py +36 -33
  11. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/plotconfigframe.py +134 -26
  12. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/plotframe.py +12 -0
  13. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/plotpanel.py +89 -54
  14. wxmplot-0.9.60/wxmplot/utils.py +176 -0
  15. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/version.py +2 -2
  16. wxmplot-0.9.60/wxmplot.egg-info/PKG-INFO +163 -0
  17. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot.egg-info/SOURCES.txt +2 -2
  18. wxmplot-0.9.60/wxmplot.egg-info/requires.txt +23 -0
  19. wxmplot-0.9.59/PKG-INFO +0 -55
  20. wxmplot-0.9.59/examples/scope_mode_mpl_compare.py +0 -29
  21. wxmplot-0.9.59/pyproject.toml +0 -7
  22. wxmplot-0.9.59/requirements.txt +0 -6
  23. wxmplot-0.9.59/setup.cfg +0 -66
  24. wxmplot-0.9.59/wxmplot/utils.py +0 -402
  25. wxmplot-0.9.59/wxmplot.egg-info/PKG-INFO +0 -55
  26. wxmplot-0.9.59/wxmplot.egg-info/requires.txt +0 -13
  27. {wxmplot-0.9.59 → wxmplot-0.9.60}/ChangeLog +0 -0
  28. {wxmplot-0.9.59 → wxmplot-0.9.60}/LICENSE +0 -0
  29. {wxmplot-0.9.59 → wxmplot-0.9.60}/MANIFEST.in +0 -0
  30. {wxmplot-0.9.59 → wxmplot-0.9.60}/README.md +0 -0
  31. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/basic_screenshot.py +0 -0
  32. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/compare_dislin_mpl.py +0 -0
  33. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/compare_plplot.py +0 -0
  34. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/contour.py +0 -0
  35. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/demo.py +0 -0
  36. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/dump_config.py +0 -0
  37. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/errorbar.py +0 -0
  38. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/fill_to_zero.py +0 -0
  39. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/fill_uncertainties.py +0 -0
  40. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/floatcontrol.py +0 -0
  41. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/histogram.py +0 -0
  42. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/image.py +0 -0
  43. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/image_scroll.py +0 -0
  44. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/imagematrix.py +0 -0
  45. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/interactive_multiplewindows.py +0 -0
  46. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/multiplot.py +0 -0
  47. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/pilatus_display.py +0 -0
  48. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/plot_fromdatafile.py +0 -0
  49. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/plot_many_traces.py +0 -0
  50. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/plotapp.py +0 -0
  51. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/rgb_image.py +0 -0
  52. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/scatterplot.py +0 -0
  53. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/scope_mode_function.py +0 -0
  54. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/scope_mode_generator.py +0 -0
  55. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/simple.py +0 -0
  56. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/stackedplot.py +0 -0
  57. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/stripchart.py +0 -0
  58. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/theme_compare.py +0 -0
  59. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/tiff_display.py +0 -0
  60. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/tifffile.py +0 -0
  61. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/time.dat +0 -0
  62. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/time_update.py +0 -0
  63. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/timedata.py +0 -0
  64. {wxmplot-0.9.59 → wxmplot-0.9.60}/examples/xafs.dat +0 -0
  65. {wxmplot-0.9.59 → wxmplot-0.9.60}/setup.py +0 -0
  66. {wxmplot-0.9.59 → wxmplot-0.9.60}/tests/basic_test.py +0 -0
  67. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/__init__.py +0 -0
  68. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/allow_idle_macosx.py +0 -0
  69. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/baseframe.py +0 -0
  70. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/colors.py +0 -0
  71. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/imageconf.py +0 -0
  72. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/imagematrixframe.py +0 -0
  73. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/imagepanel.py +0 -0
  74. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/inputhook.py +0 -0
  75. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/interactive.py +0 -0
  76. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/multiframe.py +0 -0
  77. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/plotapp.py +0 -0
  78. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/residualplotframe.py +0 -0
  79. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot/stackedplotframe.py +0 -0
  80. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot.egg-info/dependency_links.txt +0 -0
  81. {wxmplot-0.9.59 → wxmplot-0.9.60}/wxmplot.egg-info/top_level.txt +0 -0
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.1
2
+ Name: wxmplot
3
+ Version: 0.9.60
4
+ Summary: wxPython plotting widgets using matplotlib
5
+ Author-email: Matthew Newville <matt.newville@gmail.com>
6
+ License: The MIT License
7
+
8
+ Copyright (c) 2022 Matthew Newville, The University of Chicago
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
11
+ this software and associated documentation files (the "Software"), to deal in
12
+ the Software without restriction, including without limitation the rights to
13
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14
+ of the Software, and to permit persons to whom the Software is furnished to do
15
+ so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXP80RESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE qUSE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Hoempage, https://github.com/newville/wxmplot
29
+ Project-URL: Documentation, https://newville.github.io/wxmplot
30
+ Project-URL: Tracker, https://github.com/newville/wxmplot/issues
31
+ Keywords: wxPython,matplotlib
32
+ Classifier: Development Status :: 5 - Production/Stable
33
+ Classifier: Intended Audience :: End Users/Desktop
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: MacOS :: MacOS X
37
+ Classifier: Operating System :: Microsoft :: Windows
38
+ Classifier: Operating System :: POSIX
39
+ Classifier: Topic :: Scientific/Engineering
40
+ Classifier: Topic :: Scientific/Engineering :: Visualization
41
+ Classifier: Programming Language :: Python
42
+ Classifier: Programming Language :: Python :: 3 :: Only
43
+ Classifier: Programming Language :: Python :: 3.9
44
+ Classifier: Programming Language :: Python :: 3.10
45
+ Classifier: Programming Language :: Python :: 3.11
46
+ Classifier: Programming Language :: Python :: 3.12
47
+ Classifier: Programming Language :: Python :: 3.13
48
+ Requires-Python: >=3.9
49
+ Description-Content-Type: text/markdown
50
+ License-File: LICENSE
51
+ Requires-Dist: wxPython>=4.2.0
52
+ Requires-Dist: wxutils>=0.3.0
53
+ Requires-Dist: matplotlib>=3.6.0
54
+ Requires-Dist: numpy>=1.22
55
+ Requires-Dist: Pillow>=7.0
56
+ Requires-Dist: pyyaml>=5.0
57
+ Requires-Dist: pyshortcuts>=1.9.5
58
+ Provides-Extra: dev
59
+ Requires-Dist: build; extra == "dev"
60
+ Requires-Dist: twine; extra == "dev"
61
+ Provides-Extra: doc
62
+ Requires-Dist: Sphinx; extra == "doc"
63
+ Requires-Dist: jupyter_sphinx; extra == "doc"
64
+ Provides-Extra: test
65
+ Requires-Dist: pytest; extra == "test"
66
+ Requires-Dist: pytest-cov; extra == "test"
67
+ Requires-Dist: coverage; extra == "test"
68
+ Provides-Extra: all
69
+ Requires-Dist: wxmplotls[dev,doc,test]; extra == "all"
70
+
71
+ [![Version](https://img.shields.io/pypi/v/wxmplot.svg)](https://pypi.org/project/wxmplot)
72
+ [![Downloads](https://pepy.tech/badge/wxmplot/month)](https://pepy.tech/project/wxmplot)
73
+
74
+
75
+ wxmplot provides advanced `wxPython`_ widgets for plotting and image display
76
+ of numerical data based on `matplotlib`_. While `matplotlib`_ provides
77
+ excellent general purpose plotting functionality and supports many GUI and
78
+ non-GUI backends it does not have a very tight integration with any
79
+ particular GUI toolkit. With a large number of plotting components and
80
+ options, it is not easy for programmers to select plotting options for every
81
+ stuation and not easy for end users to manipulate `matplotlib`_ plots.
82
+ Similarly, while `wxPython`_ has some plotting functionality, it has nothing
83
+ as good or complete as `matplotlib`_. The wxmplot package attempts to bridge
84
+ that gap. With the plotting and image display Panels and Frames from
85
+ wxmplot, programmers are able to provide plotting widgets that make it easy
86
+ for end users to customize plots and interact with their data.
87
+
88
+ WXMplot provides wx.Panels for basic 2D line plots and image display that are
89
+ richly featured and provide end-users with interactivity (zooming, reading
90
+ positions, rotating images) and customization (line types, labels, marker
91
+ type, colors, and color tables) of the graphics without having to know
92
+ matplotlib. wxmplot does not expose all of matplotlib's capabilities, but
93
+ does provide 2D plotting and image display Panels and Frames that are easy to
94
+ add to wxPython applications to handle many common plotting and image display
95
+ needs.
96
+
97
+ The wxmplot package is aimed at programmers who want to include high quality
98
+ scientific graphics in their applications that can be manipulated by the
99
+ end-user. If you're a python programmer who is comfortable writing complex
100
+ pyplot scripts or plotting interactively from IPython, this package may seem
101
+ too limiting for your needs. On the other hand, wxmplot provides more and
102
+ and better customizations than matplotlib's basic navigation toolbars.
103
+
104
+ The main widigets provided by wxmplot are:
105
+
106
+ PlotPanel: wx.Panel for basic 2-D line plots (roughly matplotlib `plot`)
107
+
108
+ PlotFrame: wx.Frame containing a PlotPanel
109
+
110
+ ImagePanel: wx.Panel for image display (roughly matplotlib `imshow`)
111
+
112
+ ImageFrame: wx.Frame containing ImagePanel
113
+
114
+ 2D Line plotting with PlotPanel /PlotFrame
115
+ ------------------------------------
116
+
117
+ PlotPanel and PlotFrame give the end-user the ability to:
118
+
119
+ 1. display x, y coordinates (left-click)
120
+ 2. zoom in on a particular region of the plot (left-drag)
121
+ 3. customize titles, labels, legend, color, linestyle, marker,
122
+ and whether a grid is shown. A separate configuration frame
123
+ is used to set these attributes.
124
+ 4. save high-qualiy plot images (as PNGs), copy to system
125
+ clipboard, or print.
126
+
127
+ For the programmer, these provide simple plotting methods:
128
+
129
+ plot(x,y): start a new plot, and plot data x,y
130
+
131
+ Some of the optional arguments (all keyword/values) include
132
+
133
+ color='Blue' for any X11 color name, (rgb) tuple, or '#RRGGBB'
134
+ style='solid' 'solid,'dashed','dotted','dot-dash'
135
+ linewidth=2 integer 0 (no line) to 10
136
+ marker='None' any of a wide range of marker symbols
137
+ markersize=8 integer 0 to 30
138
+ xlabel=' ' label for X axis
139
+ ylabel=' ' label for Y axis
140
+ title=' ' title for top of PlotFrame
141
+ grid=True boolean for whether to show grid.
142
+
143
+ oplot(x,y): plot data x,y, on same plot as current data
144
+
145
+ with optional arguments as plot()
146
+
147
+ clear(): clear plot
148
+
149
+ save_figure(): bring up file dialog for saving image of figure
150
+
151
+ Image Display with ImagePanel and ImageFrame
152
+ ------------------------------------------
153
+
154
+ This displays a numpy array as an greyscale or false color image. The end
155
+ user can zoom in, rotate, or flip the image, and adjust the smoothing of
156
+ the image and adjust the color table and intensity scale.
157
+
158
+ Programmatically, one can make the same adjustments to an ImagePanel by
159
+ changing its configuaration attributes and running the redraw() method.
160
+
161
+ =====
162
+
163
+ last update: 2018-Oct-13 Matthew Newville <newville@cars.uchicago.edu>
@@ -13,4 +13,4 @@ y2 = 92 + 65*np.cos(x/16.) * np.exp(-x*x/7e3) + noise(size=n, scale=0.3)
13
13
 
14
14
  wi.plot(x, y1, title='Test 2 Axes with different y scales',
15
15
  xlabel='x (mm)', ylabel='y1', ymin=-0.75, ymax=0.75)
16
- wi.plot(x, y2, y2label='y2', side='right', ymin=0)
16
+ wi.plot(x, y2, y2label='y2', yaxes=2, ymin=0)
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/python
2
+ #
3
+ # example plot with 3 different right-hand axes with different y scales
4
+ import numpy as np
5
+ import wxmplot.interactive as wi
6
+
7
+ noise = np.random.normal
8
+ n = 201
9
+ x = np.linspace(0, 100, n)
10
+ y1 = np.sin(x/3.4)/(0.2*x+2) + noise(size=n, scale=0.1)
11
+ y2 = 92.2 + 26.5*np.cos(x/16.) * np.exp(-x*x/7e3) + noise(size=n, scale=0.3)
12
+ y3 = 0.04 + 0.07*np.sin(x/46.) * np.exp(-x*x/7e3) + noise(size=n, scale=0.0003)
13
+ y4 = -210. + 0.6*x * np.exp(-x*x/7e3) + noise(size=n, scale=0.3)
14
+
15
+
16
+ disp = wi.plot(x, y1, title='Test 4 Axes with different y scales',
17
+ show_legend=True, yaxes_tracecolor=True,
18
+ xlabel='x (mm)', label='signal 1', ylabel='ylabel')
19
+
20
+ wi.plot(x, y2, y2label='y2label', label='signal 2', yaxes=2)
21
+ wi.plot(x, y3, y3label='y3label', label='signal 3', yaxes=3)
22
+ wi.plot(x, y4, y4label='y4label', label='signal 4', yaxes=4)
@@ -0,0 +1,28 @@
1
+ # This could be comparable to
2
+ # https://matplotlib.org/stable/gallery/animation/strip_chart.html
3
+
4
+ import numpy as np
5
+ from wxmplot.interactive import plot, set_data_generator
6
+
7
+ np.random.seed(19680801 // 10)
8
+
9
+ class Scope:
10
+ def __init__(self, nmax=50, dt=0.1):
11
+ self.dt = dt
12
+ self.nmax = nmax
13
+ self.t, self.y = [], []
14
+ print(nmax, dt)
15
+
16
+ def update(self):
17
+ if len(self.y) > self.nmax:
18
+ self.t, self.y = [], []
19
+ self.t.append(self.dt*len(self.y))
20
+ p = np.random.rand()
21
+ self.y.append(np.random.rand() if p < 0.15 else 0)
22
+ return [(self.t, self.y)]
23
+
24
+ NMAX, DT = 200, 0.05
25
+ scope = Scope(nmax=NMAX, dt=DT)
26
+
27
+ plot([0], [0], xmax=NMAX*DT)
28
+ set_data_generator(scope.update)
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/python
2
+ import time
3
+ import numpy as np
4
+ import sys
5
+ import wx
6
+ from datetime import datetime
7
+ from wx.lib import masked
8
+ from floatcontrol import FloatCtrl
9
+ from wxmplot import PlotPanel
10
+
11
+
12
+ def next_data():
13
+ "simulated data"
14
+ t0 = time.time()
15
+ lt = time.localtime(t0)
16
+ dt0 = datetime.fromtimestamp(t0)
17
+
18
+ tmin, tsec = lt[4], lt[5]
19
+ u = np.random.random()
20
+ v = np.random.random()
21
+ x = np.sin( (u + tsec)/3.0) + tmin/30. + v/5.0
22
+ return dt0, x
23
+
24
+
25
+ def next_data2():
26
+ "simulated data"
27
+ t0 = time.time()
28
+ dt0 = datetime.fromtimestamp(t0)
29
+
30
+ lt = time.localtime(t0)
31
+ tmin, tsec = lt[4], lt[5]
32
+ u = np.random.random()
33
+ v = np.random.random()
34
+ x = 13.0*np.sin((u + tsec)/1.0) + (tmin+tsec/10)/2. + v/2.
35
+ return dt0, x
36
+
37
+ class StripChartFrame(wx.Frame):
38
+ def __init__(self, parent, ID, **kws):
39
+ kws["style"] = wx.DEFAULT_FRAME_STYLE|wx.RESIZE_BORDER|wx.TAB_TRAVERSAL
40
+
41
+ wx.Frame.__init__(self, parent, ID, '',
42
+ wx.DefaultPosition, wx.Size(-1,-1), **kws)
43
+ self.SetTitle("wxmplot StripChart Demo")
44
+
45
+ self.tmin = 30.0
46
+
47
+ self.SetFont(wx.Font(12,wx.SWISS,wx.NORMAL,wx.BOLD,False))
48
+ menu = wx.Menu()
49
+ menu_exit = menu.Append(-1, "E&xit", "Terminate the program")
50
+
51
+ menuBar = wx.MenuBar()
52
+ menuBar.Append(menu, "&File");
53
+ self.SetMenuBar(menuBar)
54
+
55
+ self.Bind(wx.EVT_MENU, self.OnExit, menu_exit)
56
+ self.Bind(wx.EVT_CLOSE, self.OnExit)
57
+
58
+ sbar = self.CreateStatusBar(2,wx.CAPTION)
59
+ sfont = sbar.GetFont()
60
+ sfont.SetWeight(wx.BOLD)
61
+ sfont.SetPointSize(11)
62
+ sbar.SetFont(sfont)
63
+ self.SetStatusWidths([-3,-1])
64
+ self.SetStatusText('',0)
65
+
66
+ mainsizer = wx.BoxSizer(wx.VERTICAL)
67
+
68
+ btnpanel = wx.Panel(self, -1)
69
+ btnsizer = wx.BoxSizer(wx.HORIZONTAL)
70
+
71
+ b_on = wx.Button(btnpanel, -1, 'Start', size=(-1,-1))
72
+ b_off = wx.Button(btnpanel, -1, 'Stop', size=(-1,-1))
73
+
74
+ b_on.Bind(wx.EVT_BUTTON, self.onStartTimer)
75
+ b_off.Bind(wx.EVT_BUTTON, self.onStopTimer)
76
+
77
+ tlabel = wx.StaticText(btnpanel, -1, ' Time range:')
78
+ self.time_range = FloatCtrl(btnpanel, size=(100, -1),
79
+ value=abs(self.tmin), precision=1)
80
+
81
+ btnsizer.Add(b_on, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0)
82
+ btnsizer.Add(b_off, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0)
83
+ btnsizer.Add(tlabel, 1, wx.GROW|wx.ALL|wx.ALIGN_LEFT|wx.LEFT, 0)
84
+ btnsizer.Add(self.time_range, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0)
85
+
86
+ btnpanel.SetSizer(btnsizer)
87
+ btnsizer.Fit(btnpanel)
88
+
89
+ self.plotpanel = PlotPanel(self, messenger=self.write_message)
90
+ self.plotpanel.BuildPanel()
91
+ self.plotpanel.set_xlabel('Time from Present (s)')
92
+ mainsizer.Add(btnpanel, 0, wx.GROW|wx.ALIGN_LEFT|wx.LEFT, 0)
93
+ mainsizer.Add(self.plotpanel, 1, wx.GROW|wx.ALL|wx.ALIGN_LEFT|wx.LEFT, 0)
94
+ self.SetSizer(mainsizer)
95
+ mainsizer.Fit(self)
96
+
97
+ self.timer1 = wx.Timer(self)
98
+ self.timer2 = wx.Timer(self)
99
+ self.Bind(wx.EVT_TIMER, self.onTimer1, self.timer1)
100
+ self.Bind(wx.EVT_TIMER, self.onTimer2, self.timer2)
101
+
102
+ self.Refresh()
103
+ self.SetSize(self.GetBestVirtualSize())
104
+
105
+ wx.CallAfter(self.onStartTimer)
106
+
107
+ def write_message(self, msg, panel=0):
108
+ """write a message to the Status Bar"""
109
+ self.SetStatusText(msg, panel)
110
+
111
+ def onStartTimer(self,event=None):
112
+ t0, y0 = next_data()
113
+ self.tlist = [t0]
114
+ self.ylist = [y0]
115
+
116
+ t2, y2 = next_data2()
117
+ self.t2list = [t2]
118
+ self.y2list = [y2]
119
+ self.tmin_last = -10000
120
+ self.time0 = time.time()
121
+
122
+ self.tmin_last = -10000
123
+ self.time0 = time.time()
124
+ self.timer1.Start(105)
125
+ self.timer2.Start(187)
126
+
127
+ def onStopTimer(self,event=None):
128
+ self.timer1.Stop()
129
+ self.timer2.Stop()
130
+
131
+ def onTimer1(self, event):
132
+ etime = time.time() - self.time0
133
+ self.tmin = float(self.time_range.GetValue())
134
+ t1, y1 = next_data()
135
+ self.tlist.append(t1)
136
+ self.ylist.append(y1)
137
+ if isinstance(t1, datetime):
138
+ t1 = t1.timestamp()
139
+ ts = [dt.timestamp()for dt in self.tlist]
140
+ else:
141
+ ts = [dt for dt in self.tlist]
142
+
143
+ del_ts = np.array(ts)-t1
144
+ mask = np.where(del_ts > -abs(self.tmin))
145
+
146
+ ydat = np.array(self.ylist)[mask]
147
+ tdat = np.array(self.tlist)[mask]
148
+
149
+ n = len(self.ylist)
150
+ print("Y1 ", n, ydat.min(), ydat.max())
151
+ if n <= 2:
152
+ self.plotpanel.plot(tdat, ydat)
153
+ else:
154
+ self.plotpanel.update_line(0, tdat, ydat, draw=True)
155
+ self.write_message("update %i points in %8.4f s" % (n,etime), panel=0)
156
+
157
+ def onTimer2(self, event):
158
+ etime = time.time() - self.time0
159
+ self.tmin = float(self.time_range.GetValue())
160
+ t1, y1 = next_data2()
161
+ self.t2list.append(t1)
162
+ self.y2list.append(y1)
163
+ if isinstance(t1, datetime):
164
+ t1 = t1.timestamp()
165
+ ts = [dt.timestamp()for dt in self.t2list]
166
+ else:
167
+ ts = [dt for dt in self.t2list]
168
+
169
+ del_ts = np.array(ts)-t1
170
+ mask = np.where(del_ts > -abs(self.tmin))
171
+
172
+ ydat = np.array(self.y2list)[mask]
173
+ tdat = np.array(self.t2list)[mask]
174
+
175
+ n = len(self.y2list)
176
+ print("Y2 ", n, ydat.min(), ydat.max())
177
+
178
+ if n <= 2:
179
+ self.plotpanel.plot(tdat, ydat, side='right', ylabel='y2')
180
+ else:
181
+ self.plotpanel.update_line(1, tdat, ydat, side='right', draw=True)
182
+ self.write_message("update %i points in %8.4f s" % (n,etime), panel=1)
183
+
184
+ def OnAbout(self, event):
185
+ dlg = wx.MessageDialog(self, "wxmplot example: stripchart app",
186
+ "About WXMPlot test", wx.OK | wx.ICON_INFORMATION)
187
+ dlg.ShowModal()
188
+ dlg.Destroy()
189
+
190
+ def OnExit(self, event):
191
+ self.Destroy()
192
+
193
+ app = wx.App()
194
+ f = StripChartFrame(None,-1)
195
+ f.Show(True)
196
+ app.MainLoop()
197
+ #
@@ -0,0 +1,63 @@
1
+ [build-system]
2
+ requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools_scm]
6
+ write_to = "wxmplot/version.py"
7
+ version_scheme = "post-release"
8
+
9
+ [tool.setuptools.packages.find]
10
+ include = ["wxmplot"]
11
+
12
+ [tool.coverage.run]
13
+ omit = ["tests/*"]
14
+
15
+ [project]
16
+ name = "wxmplot"
17
+ dynamic = ["version"]
18
+ requires-python = ">= 3.9"
19
+ description = "wxPython plotting widgets using matplotlib"
20
+ readme = "README.md"
21
+ authors = [
22
+ {name = "Matthew Newville", email = "matt.newville@gmail.com"}
23
+ ]
24
+ license = {file = "LICENSE"}
25
+ keywords = ["wxPython", "matplotlib"]
26
+ classifiers = [
27
+ "Development Status :: 5 - Production/Stable",
28
+ "Intended Audience :: End Users/Desktop",
29
+ "Intended Audience :: Developers",
30
+ "License :: OSI Approved :: MIT License",
31
+ "Operating System :: MacOS :: MacOS X",
32
+ "Operating System :: Microsoft :: Windows",
33
+ "Operating System :: POSIX",
34
+ "Topic :: Scientific/Engineering",
35
+ "Topic :: Scientific/Engineering :: Visualization",
36
+ "Programming Language :: Python",
37
+ "Programming Language :: Python :: 3 :: Only",
38
+ "Programming Language :: Python :: 3.9",
39
+ "Programming Language :: Python :: 3.10",
40
+ "Programming Language :: Python :: 3.11",
41
+ "Programming Language :: Python :: 3.12",
42
+ "Programming Language :: Python :: 3.13"
43
+ ]
44
+
45
+ dependencies = [
46
+ "wxPython>=4.2.0",
47
+ "wxutils>=0.3.0",
48
+ "matplotlib>=3.6.0",
49
+ "numpy>=1.22",
50
+ "Pillow>=7.0",
51
+ "pyyaml>=5.0",
52
+ "pyshortcuts>=1.9.5"]
53
+
54
+ [project.urls]
55
+ Hoempage = "https://github.com/newville/wxmplot"
56
+ Documentation = "https://newville.github.io/wxmplot"
57
+ Tracker = "https://github.com/newville/wxmplot/issues"
58
+
59
+ [project.optional-dependencies]
60
+ dev = ["build", "twine"]
61
+ doc = ["Sphinx", "jupyter_sphinx"]
62
+ test = ["pytest", "pytest-cov", "coverage"]
63
+ all = ["wxmplotls[dev, doc, test]"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -44,6 +44,7 @@ class BasePanel(wx.Panel):
44
44
 
45
45
  self.popup_menu = None
46
46
  self._yfmt = self._y2fmt = self._xfmt = None
47
+ self._y3fmt = self._y4fmt = None
47
48
  self.use_dates = False
48
49
  self.show_config_popup = show_config_popup
49
50
  self.launch_dir = get_cwd()
@@ -138,12 +139,6 @@ class BasePanel(wx.Panel):
138
139
 
139
140
  self.canvas.draw()
140
141
 
141
- def get_right_axes(self):
142
- "create, if needed, and return right-hand y axes"
143
- if len(self.fig.get_axes()) < 2:
144
- ax = self.axes.twinx()
145
-
146
- return self.fig.get_axes()[1]
147
142
 
148
143
  def set_xylims(self, limits, axes=None):
149
144
  if axes not in self.user_limits:
@@ -170,9 +165,17 @@ class BasePanel(wx.Panel):
170
165
  self.conf.relabel(ylabel=s, delay_draw=delay_draw)
171
166
 
172
167
  def set_y2label(self, s, delay_draw=False):
173
- "set plot ylabel"
168
+ "set plot y2 label"
174
169
  self.conf.relabel(y2label=s, delay_draw=delay_draw)
175
170
 
171
+ def set_y3label(self, s, delay_draw=False):
172
+ "set plot y3 label"
173
+ self.conf.relabel(y3label=s, delay_draw=delay_draw)
174
+
175
+ def set_y4label(self, s, delay_draw=False):
176
+ "set plot y4 label"
177
+ self.conf.relabel(y4label=s, delay_draw=delay_draw)
178
+
176
179
  def write_message(self, s, panel=0):
177
180
  """ write message to message handler
178
181
  (possibly going to GUI statusbar)"""
@@ -353,6 +356,7 @@ class BasePanel(wx.Panel):
353
356
 
354
357
  def reset_formats(self):
355
358
  self._xfmt = self._yfmt = self._y2fmt = None
359
+ self._y3fmt = self._y4fmt = None
356
360
 
357
361
  def xformatter(self, x, pos):
358
362
  " x-axis formatter "
@@ -366,9 +370,17 @@ class BasePanel(wx.Panel):
366
370
  return self.__format(y, type='y')
367
371
 
368
372
  def y2formatter(self, y, pos):
369
- " y-axis formatter "
373
+ " y2-axis formatter "
370
374
  return self.__format(y, type='y2')
371
375
 
376
+ def y3formatter(self, y, pos):
377
+ " y3-axis formatter "
378
+ return self.__format(y, type='y3')
379
+
380
+ def y4formatter(self, y, pos):
381
+ " y4-axis formatter "
382
+ return self.__format(y, type='y4')
383
+
372
384
  def set_format_str(self, axis):
373
385
  try:
374
386
  ticks = axis.get_major_locator()()
@@ -378,12 +390,11 @@ class BasePanel(wx.Panel):
378
390
  if len(ticks) < 2:
379
391
  ticks.append(0)
380
392
  ticks.append(1)
381
-
382
393
  step = max(2.e-15, abs(np.diff(ticks).mean()))
383
394
  if step > 5e4 or (step < 5.e-4 and ticks.mean() < 5.e-2):
384
395
  fmt = '%.2e'
385
396
  else:
386
- ndigs = max(0, 3 - round(log10(step)))
397
+ ndigs = max(0, 3 - round(log10(1.25*step)))
387
398
  while ndigs >= 0:
388
399
  if np.abs(ticks- np.round(ticks, decimals=ndigs)).max() < 2e-3*step:
389
400
  ndigs -= 1
@@ -411,6 +422,16 @@ class BasePanel(wx.Panel):
411
422
  if self._y2fmt is None:
412
423
  self._y2fmt = self.set_format_str(ax)
413
424
  fmt = self._y2fmt
425
+ elif type == 'y3' and len(self.fig.get_axes()) > 2:
426
+ ax = self.fig.get_axes()[2].yaxis
427
+ if self._y3fmt is None:
428
+ self._y3fmt = self.set_format_str(ax)
429
+ fmt = self._y3fmt
430
+ elif type == 'y4' and len(self.fig.get_axes()) > 3:
431
+ ax = self.fig.get_axes()[3].yaxis
432
+ if self._y4fmt is None:
433
+ self._y4fmt = self.set_format_str(ax)
434
+ fmt = self._y4fmt
414
435
  else:
415
436
  ax = self.axes.xaxis
416
437
  if self._xfmt is None: