wxmplot 2026.2.2__tar.gz → 2026.2.3__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 (85) hide show
  1. {wxmplot-2026.2.2/wxmplot.egg-info → wxmplot-2026.2.3}/PKG-INFO +5 -2
  2. wxmplot-2026.2.3/examples/image_canvas_scroll.py +123 -0
  3. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/pyproject.toml +4 -2
  4. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/__init__.py +1 -5
  5. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/image_canvas.py +22 -8
  6. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/line_plot.py +3 -3
  7. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/version.py +3 -3
  8. {wxmplot-2026.2.2 → wxmplot-2026.2.3/wxmplot.egg-info}/PKG-INFO +5 -2
  9. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot.egg-info/SOURCES.txt +1 -0
  10. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot.egg-info/requires.txt +3 -0
  11. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot.egg-info/scm_file_list.json +1 -0
  12. wxmplot-2026.2.3/wxmplot.egg-info/scm_version.json +8 -0
  13. wxmplot-2026.2.2/wxmplot.egg-info/scm_version.json +0 -8
  14. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/ChangeLog +0 -0
  15. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/LICENSE +0 -0
  16. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/MANIFEST.in +0 -0
  17. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/README.md +0 -0
  18. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/BasicExamples.ipynb +0 -0
  19. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/DynamicExamples.ipynb +0 -0
  20. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/basic_screenshot.py +0 -0
  21. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/compare_dislin_mpl.py +0 -0
  22. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/compare_plplot.py +0 -0
  23. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/contour.py +0 -0
  24. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/demo.py +0 -0
  25. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/discrete_data.py +0 -0
  26. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/errorbar.py +0 -0
  27. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/extra_marker_and_line_reporting.py +0 -0
  28. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/fill_to_zero.py +0 -0
  29. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/fill_uncertainties.py +0 -0
  30. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/floatcontrol.py +0 -0
  31. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/histogram.py +0 -0
  32. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/image.py +0 -0
  33. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/image_canvas_ex.py +0 -0
  34. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/image_scroll.py +0 -0
  35. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/imagematrix.py +0 -0
  36. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/interactive_multiplewindows.py +0 -0
  37. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/leftright.py +0 -0
  38. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/multiple_yaxes.py +0 -0
  39. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/multiplot.py +0 -0
  40. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/pilatus_display.py +0 -0
  41. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/plot_fromdatafile.py +0 -0
  42. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/plot_many_traces.py +0 -0
  43. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/plotapp.py +0 -0
  44. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/rgb_image.py +0 -0
  45. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/scatterplot.py +0 -0
  46. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/scope_mode_function.py +0 -0
  47. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/scope_mode_generator.py +0 -0
  48. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/scope_mode_mpl_compare.py +0 -0
  49. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/simple.py +0 -0
  50. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/stackedplot.py +0 -0
  51. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/stripchart.py +0 -0
  52. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/theme_compare.py +0 -0
  53. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/tiff_display.py +0 -0
  54. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/tifffile.py +0 -0
  55. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/time.dat +0 -0
  56. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/time_update.py +0 -0
  57. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/timedata.py +0 -0
  58. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/wxmplot_jupyter.ipynb +0 -0
  59. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/examples/xafs.dat +0 -0
  60. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/setup.cfg +0 -0
  61. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/setup.py +0 -0
  62. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/tests/basic_test.py +0 -0
  63. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/allow_idle_macosx.py +0 -0
  64. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/baseframe.py +0 -0
  65. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/basepanel.py +0 -0
  66. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/colors.py +0 -0
  67. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/config.py +0 -0
  68. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/histogram.py +0 -0
  69. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/imageconf.py +0 -0
  70. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/imageframe.py +0 -0
  71. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/imagematrixframe.py +0 -0
  72. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/imagepanel.py +0 -0
  73. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/inputhook.py +0 -0
  74. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/interactive.py +0 -0
  75. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/multiframe.py +0 -0
  76. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/plotapp.py +0 -0
  77. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/plotconfigframe.py +0 -0
  78. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/plotframe.py +0 -0
  79. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/plotpanel.py +0 -0
  80. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/residualplotframe.py +0 -0
  81. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/stackedplotframe.py +0 -0
  82. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/utils.py +0 -0
  83. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot/vispy_utils.py +0 -0
  84. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot.egg-info/dependency_links.txt +0 -0
  85. {wxmplot-2026.2.2 → wxmplot-2026.2.3}/wxmplot.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wxmplot
3
- Version: 2026.2.2
3
+ Version: 2026.2.3
4
4
  Summary: wxPython plotting widgets using matplotlib
5
5
  Author-email: Matthew Newville <matt.newville@gmail.com>
6
6
  License-Expression: MIT
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
23
  Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Programming Language :: Python :: 3.14
25
- Requires-Python: >=3.10
25
+ Requires-Python: >=3.11
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: wxPython>=4.2.3
@@ -46,6 +46,9 @@ Requires-Dist: Sphinx; extra == "doc"
46
46
  Requires-Dist: jupyter_sphinx; extra == "doc"
47
47
  Requires-Dist: sphinx-copybutton; extra == "doc"
48
48
  Requires-Dist: sphinxcontrib-video; extra == "doc"
49
+ Requires-Dist: nbsphinx; extra == "doc"
50
+ Requires-Dist: sphinx-design; extra == "doc"
51
+ Requires-Dist: sphinx-breeze-theme; extra == "doc"
49
52
  Provides-Extra: test
50
53
  Requires-Dist: pytest; extra == "test"
51
54
  Requires-Dist: pytest-cov; extra == "test"
@@ -0,0 +1,123 @@
1
+ import time, os, sys
2
+ from random import shuffle
3
+
4
+ import numpy as np
5
+
6
+ import wx
7
+ from wxutils import pack
8
+ from wxmplot import ImageFrame
9
+ from wxmplot.image_canvas import ImageCanvas
10
+
11
+
12
+ class ImageCanvasFrame(wx.Frame):
13
+ def __init__(self, parent, title='WXMPLOT Image Canvas', size=(700,600)):
14
+ wx.Frame.__init__(self, None, title=title, size=size,
15
+ style=wx.DEFAULT_FRAME_STYLE)
16
+
17
+ self.im_canvas = ImageCanvas(self)
18
+ sizer = wx.BoxSizer(wx.VERTICAL)
19
+ sizer.Add(self.im_canvas, 1, wx.ALL|wx.GROW)
20
+ pack(self, sizer)
21
+
22
+ def set_colormap(self, colormap: str):
23
+ self.im_canvas.set_colormap(colormap)
24
+
25
+ def set_image(self, image: np.ndarray):
26
+ self.im_canvas.set_image(image)
27
+ self.im_canvas.set_auto_scale(True, level=0.5)
28
+
29
+ class TestFrame(wx.Frame):
30
+ def __init__(self, parent=None, *args,**kwds):
31
+ kwds["style"] = wx.DEFAULT_FRAME_STYLE|wx.RESIZE_BORDER|wx.TAB_TRAVERSAL
32
+
33
+ wx.Frame.__init__(self, parent, -1, '',
34
+ wx.DefaultPosition, wx.Size(-1,-1), **kwds)
35
+ self.SetTitle(" WXMPlot Image Scroll")
36
+
37
+ self.SetFont(wx.Font(12,wx.SWISS,wx.NORMAL,wx.BOLD,False))
38
+ menu = wx.Menu()
39
+
40
+ self.arrays = []
41
+ self.imageframe = None
42
+
43
+ framesizer = wx.BoxSizer(wx.VERTICAL)
44
+
45
+ panel = wx.Panel(self, -1, size=(-1, -1))
46
+ panelsizer = wx.BoxSizer(wx.VERTICAL)
47
+
48
+ panelsizer.Add( wx.StaticText(panel, -1, 'Show Images'),
49
+ 0, wx.LEFT|wx.EXPAND, 10)
50
+
51
+ btn = wx.Button(panel, -1, 'start scrolling', size=(-1,-1))
52
+ btn.Bind(wx.EVT_BUTTON,self.onScrollImages)
53
+
54
+ btn2 = wx.Button(panel, -1, 'stop scrolling', size=(-1,-1))
55
+ btn2.Bind(wx.EVT_BUTTON,self.onStop)
56
+
57
+ self.msg = wx.StaticText(panel, label='000 images in 00.0000 seconds ', size=(500, -1))
58
+
59
+ panelsizer.Add(btn, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 5)
60
+ panelsizer.Add(btn2, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 5)
61
+ panelsizer.Add(self.msg, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 5)
62
+
63
+ panel.SetSizer(panelsizer)
64
+ panelsizer.Fit(panel)
65
+
66
+ framesizer.Add(panel, 0, wx.EXPAND, 2)
67
+ self.SetSizer(framesizer)
68
+ framesizer.Fit(self)
69
+ self.create_data()
70
+ self.ShowImageCanvasFrame()
71
+ self.count = 0
72
+ self.Bind(wx.EVT_TIMER, self.onTimer)
73
+ self.timer = wx.Timer(self)
74
+ self.Refresh()
75
+
76
+ def create_data(self):
77
+ nx = ny = 1001
78
+ print("Creating 240 images..")
79
+ for xoff in np.linspace(-2.5, 2.5, 20):
80
+ y, x = np.mgrid[-10+xoff:10+xoff:nx*1j, -10:10:nx*1j]
81
+ for xscale in (4.5, 5.0, 6.0):
82
+ for yscale in (0.8, 1.0, 1.1, 1.2):
83
+ dat = np.sin(x*x/xscale + y*y/yscale)/(1 + (x+y)*(x+y))
84
+ dat += np.random.normal(scale=0.12, size=(nx, ny))
85
+ self.arrays.append(dat)
86
+ shuffle(self.arrays)
87
+ print("Built %d arrays, shape=%s" % (len(self.arrays), repr(dat.shape)))
88
+
89
+ def ShowImageCanvasFrame(self):
90
+ if self.imageframe is None:
91
+ self.imageframe = ImageCanvasFrame(self)
92
+ try:
93
+ self.imageframe.Show()
94
+ except RuntimeError:
95
+ self.imageframe = ImageCanvasframe(self)
96
+ self.imageframe.Show()
97
+
98
+ self.imageframe.set_image(self.arrays[0])
99
+ self.imageframe.set_colormap('bone')
100
+ self.imageframe.Raise()
101
+
102
+ def onTimer(self, event):
103
+ self.count += 1
104
+ message = " %d images in %.3f sec" % (self.count,
105
+ time.time()-self.t0)
106
+ self.msg.SetLabel(message)
107
+ wx.CallAfter(self.imageframe.set_image,
108
+ self.arrays[self.count % len(self.arrays)])
109
+
110
+ def onScrollImages(self,event=None):
111
+ self.ShowImageCanvasFrame()
112
+ self.count = 0
113
+ self.t0 = time.time()
114
+ self.timer.Start(10)
115
+
116
+ def onStop(self,event=None):
117
+ self.timer.Stop()
118
+
119
+ if __name__ == '__main__':
120
+ app = wx.App()
121
+ f = TestFrame(None,-1)
122
+ f.Show(True)
123
+ app.MainLoop()
@@ -15,7 +15,7 @@ omit = ["tests/*"]
15
15
  [project]
16
16
  name = "wxmplot"
17
17
  dynamic = ["version"]
18
- requires-python = ">= 3.10"
18
+ requires-python = ">= 3.11"
19
19
  description = "wxPython plotting widgets using matplotlib"
20
20
  readme = "README.md"
21
21
  authors = [
@@ -64,6 +64,8 @@ Tracker = "https://github.com/newville/wxmplot/issues"
64
64
 
65
65
  [project.optional-dependencies]
66
66
  dev = ["build", "twine"]
67
- doc = ["Sphinx", "jupyter_sphinx", "sphinx-copybutton", "sphinxcontrib-video"]
67
+ doc = ["Sphinx", "jupyter_sphinx", "sphinx-copybutton", "sphinxcontrib-video",
68
+ "nbsphinx", "sphinx-design", "sphinx-breeze-theme"]
69
+
68
70
  test = ["pytest", "pytest-cov", "coverage"]
69
71
  all = ["wxmplot[dev, doc, test]"]
@@ -8,10 +8,8 @@
8
8
  with from wx programs. More importantly, the widgets created by wxmplot
9
9
  give the end user a flexible set of tools for interacting with their
10
10
  data and customizing the plots and displays. wxmplot panels are more
11
- interactive than typical displayss from matplotlib's pyplot module.
11
+ interactive than typical displays from matplotlib's pyplot module.
12
12
 
13
- version: 0.9.56
14
- last update: 2023-Jul-02
15
13
  License: MIT
16
14
  Author: Matthew Newville <newville@cars.uchicago.edu>
17
15
  Center for Advanced Radiation Sources,
@@ -47,5 +45,3 @@ from .image_canvas import ImageCanvas, BinMethod
47
45
  from .line_plot import LinePlot
48
46
  from .histogram import Histogram, compute_histogram_data
49
47
 
50
- if sys.platform.lower() == 'darwin':
51
- wx.PyApp.IsDisplayAvailable = lambda _: True
@@ -103,6 +103,7 @@ class ImageCanvas(wx.Panel):
103
103
  self._first_image = True
104
104
  self._auto_scale = True
105
105
  self._filter_gaps = False
106
+ self._percentile_level: "float | None" = None
106
107
  self._min_value = 0.0
107
108
  self._max_value = 255.0
108
109
  self._data_min = 0.0
@@ -168,7 +169,7 @@ class ImageCanvas(wx.Panel):
168
169
  self._pixel_info_text = scene.visuals.Text(
169
170
  text="",
170
171
  color=vispy_colour("green"),
171
- font_size=6,
172
+ font_size=8,
172
173
  bold=True,
173
174
  anchor_x="left",
174
175
  anchor_y="top",
@@ -179,7 +180,7 @@ class ImageCanvas(wx.Panel):
179
180
  self._fps_text = scene.visuals.Text(
180
181
  text="",
181
182
  color=vispy_colour("green"),
182
- font_size=6,
183
+ font_size=8,
183
184
  bold=True,
184
185
  anchor_x="left",
185
186
  anchor_y="top",
@@ -226,11 +227,23 @@ class ImageCanvas(wx.Panel):
226
227
  self._image_visual.clim = (self._norm_fwd(min_val), self._norm_fwd(max_val))
227
228
  self._canvas.update()
228
229
 
229
- def set_auto_scale(self, enabled: bool, level: (None, float) = None) -> None:
230
+ def set_auto_scale(self, enabled: bool, level: "float | None" = None) -> None:
230
231
  """Enable or disable automatic contrast scaling."""
231
232
  self._auto_scale = enabled
232
233
  if enabled and self._raw_image is not None:
233
- self._min_value, self._max_value = self._compute_auto_clim(self._raw_image, level=level)
234
+ effective_level = level if level is not None else self._percentile_level
235
+ self._min_value, self._max_value = self._compute_auto_clim(self._raw_image, level=effective_level)
236
+ self._image_visual.clim = (self._norm_fwd(self._min_value), self._norm_fwd(self._max_value))
237
+ self._canvas.update()
238
+
239
+ def set_percentile_level(self, level: "float | None") -> None:
240
+ """Set a persistent contrast clip applied on every frame regardless of auto-scale (None=off)."""
241
+ self._percentile_level = level
242
+ if self._raw_image is not None:
243
+ if level is not None:
244
+ self._min_value, self._max_value = self._compute_auto_clim(self._raw_image, level=level)
245
+ elif self._auto_scale:
246
+ self._min_value, self._max_value = self._compute_auto_clim(self._raw_image)
234
247
  self._image_visual.clim = (self._norm_fwd(self._min_value), self._norm_fwd(self._max_value))
235
248
  self._canvas.update()
236
249
 
@@ -386,7 +399,9 @@ class ImageCanvas(wx.Panel):
386
399
  gpu_image = np.ascontiguousarray(gpu_image)
387
400
 
388
401
  self._data_min, self._data_max = self._compute_full_range(full_res)
389
- if self._auto_scale:
402
+ if self._percentile_level is not None:
403
+ self._min_value, self._max_value = self._compute_auto_clim(full_res, level=self._percentile_level)
404
+ elif self._auto_scale:
390
405
  self._min_value, self._max_value = self._compute_auto_clim(full_res)
391
406
 
392
407
  display_image = self._norm_apply(gpu_image)
@@ -413,7 +428,7 @@ class ImageCanvas(wx.Panel):
413
428
 
414
429
  def _compute_auto_clim(self, img: np.ndarray, level=None) -> tuple[float, float]:
415
430
  """Estimate display clim from a sampled subset of pixels.
416
- Uses percentiles when filter_gaps is on."""
431
+ level: symmetric contrast_level — clips this % from each side (e.g. 2 → [2, 98])."""
417
432
  flat = img.reshape(-1)
418
433
  step = max(1, flat.size // self._AUTO_CLIM_SAMPLE_BUDGET)
419
434
  sample = flat[::step]
@@ -432,9 +447,8 @@ class ImageCanvas(wx.Panel):
432
447
  else:
433
448
  vmin, vmax = float(sample.min()), float(sample.max())
434
449
 
435
- # alternative implementation
436
450
  if level is not None:
437
- vmin, vmax = np.percentile(img, [level, 100.0-level])
451
+ vmin, vmax = np.percentile(img, [level, 100.0 - level])
438
452
 
439
453
  # Ensure min != max to avoid histogram handle overlap
440
454
  if vmax <= vmin:
@@ -528,7 +528,7 @@ class LinePlot(wx.Panel):
528
528
  gc.StrokeLine(ml, mt + ph, ml + pw, mt + ph)
529
529
  gc.StrokeLine(ml, mt, ml, mt + ph)
530
530
 
531
- font_small = wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
531
+ font_small = wx.Font(wx.Size(0, 10), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
532
532
  ranges = self._data_ranges()
533
533
 
534
534
  if ranges is None:
@@ -536,7 +536,7 @@ class LinePlot(wx.Panel):
536
536
  gc.SetPen(wx.TRANSPARENT_PEN)
537
537
  gc.DrawRectangle(ml, mt, pw, ph)
538
538
  gc.SetFont(
539
- wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD),
539
+ wx.Font(wx.Size(0, 13), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD),
540
540
  empty_text,
541
541
  )
542
542
  msg = "No data"
@@ -586,7 +586,7 @@ class LinePlot(wx.Panel):
586
586
  gc.PopState()
587
587
 
588
588
  if self._hover_data_x is not None and self._hover_data_y is not None:
589
- hover_font = wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
589
+ hover_font = wx.Font(wx.Size(0, 8), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
590
590
  gc.SetFont(hover_font, get_theme().green)
591
591
  text = self.format_hover_info(self._hover_data_x, self._hover_data_y)
592
592
  tw, th = gc.GetTextExtent(text)
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2026.2.2'
22
- __version_tuple__ = version_tuple = (2026, 2, 2)
21
+ __version__ = version = '2026.2.3'
22
+ __version_tuple__ = version_tuple = (2026, 2, 3)
23
23
 
24
- __commit_id__ = commit_id = 'gbb23702e8'
24
+ __commit_id__ = commit_id = 'g514ae8af4'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wxmplot
3
- Version: 2026.2.2
3
+ Version: 2026.2.3
4
4
  Summary: wxPython plotting widgets using matplotlib
5
5
  Author-email: Matthew Newville <matt.newville@gmail.com>
6
6
  License-Expression: MIT
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
23
  Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Programming Language :: Python :: 3.14
25
- Requires-Python: >=3.10
25
+ Requires-Python: >=3.11
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: wxPython>=4.2.3
@@ -46,6 +46,9 @@ Requires-Dist: Sphinx; extra == "doc"
46
46
  Requires-Dist: jupyter_sphinx; extra == "doc"
47
47
  Requires-Dist: sphinx-copybutton; extra == "doc"
48
48
  Requires-Dist: sphinxcontrib-video; extra == "doc"
49
+ Requires-Dist: nbsphinx; extra == "doc"
50
+ Requires-Dist: sphinx-design; extra == "doc"
51
+ Requires-Dist: sphinx-breeze-theme; extra == "doc"
49
52
  Provides-Extra: test
50
53
  Requires-Dist: pytest; extra == "test"
51
54
  Requires-Dist: pytest-cov; extra == "test"
@@ -20,6 +20,7 @@ examples/floatcontrol.py
20
20
  examples/histogram.py
21
21
  examples/image.py
22
22
  examples/image_canvas_ex.py
23
+ examples/image_canvas_scroll.py
23
24
  examples/image_scroll.py
24
25
  examples/imagematrix.py
25
26
  examples/interactive_multiplewindows.py
@@ -26,6 +26,9 @@ Sphinx
26
26
  jupyter_sphinx
27
27
  sphinx-copybutton
28
28
  sphinxcontrib-video
29
+ nbsphinx
30
+ sphinx-design
31
+ sphinx-breeze-theme
29
32
 
30
33
  [test]
31
34
  pytest
@@ -92,6 +92,7 @@
92
92
  "examples/histogram.py",
93
93
  "examples/image.py",
94
94
  "examples/image_canvas_ex.py",
95
+ "examples/image_canvas_scroll.py",
95
96
  "examples/image_scroll.py",
96
97
  "examples/imagematrix.py",
97
98
  "examples/interactive_multiplewindows.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2026.2.3",
3
+ "distance": 0,
4
+ "node": "g514ae8af45f85a5f69edaea0db68f16f66ead976",
5
+ "dirty": false,
6
+ "branch": "master",
7
+ "node_date": "2026-09-01"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2026.2.2",
3
- "distance": 0,
4
- "node": "gbb23702e8d55a50452dc722de6db921762942571",
5
- "dirty": false,
6
- "branch": "master",
7
- "node_date": "2026-08-09"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes