mwxlib 1.1.7__py3-none-any.whl → 1.2.3__py3-none-any.whl

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.

Potentially problematic release.


This version of mwxlib might be problematic. Click here for more details.

mwx/__init__.py CHANGED
@@ -8,7 +8,7 @@ from .framework import Frame, MiniFrame, ShellFrame, deb
8
8
 
9
9
  ## Controls
10
10
  from .controls import Param, LParam, Knob, ControlPanel, Clipboard, Icon
11
- from .controls import Button, ToggleButton, ClassicButton, TextCtrl, Choice, Gauge, Indicator
11
+ from .controls import Button, ToggleButton, ClassicButton, TextBox, Choice, Gauge, Indicator
12
12
 
13
13
  ## Plugman
14
14
  ## from .graphman import Frame as GraphmanFrame, Layer, Thread, Graph
mwx/controls.py CHANGED
@@ -712,24 +712,25 @@ class KnobCtrlPanel(scrolled.ScrolledPanel):
712
712
  return params
713
713
  return filter(lambda c: getattr(c, 'check', None), params)
714
714
 
715
- def set_params(self, argv=None, checked_only=False):
715
+ def set_params(self, argv, checked_only=False):
716
716
  params = self.get_params(checked_only)
717
- if argv is None:
718
- for p in params:
719
- try:
720
- p.reset()
721
- except (AttributeError, TypeError):
722
- pass
723
- else:
724
- for p, v in zip(params, argv):
725
- try:
726
- p.reset(v) # eval v:str -> value
727
- except AttributeError:
728
- p.value = v
729
- except Exception as e: # failed to eval
730
- print(f"- Failed to reset {p!r}.", e)
717
+ for p, v in zip(params, argv):
718
+ try:
719
+ p.reset(v) # eval v:str -> value
720
+ except AttributeError:
721
+ p.value = v
722
+ except Exception as e:
723
+ print(f"- Failed to eval {v}:", e)
731
724
 
732
- reset_params = set_params
725
+ def reset_params(self, checked_only=False):
726
+ params = self.get_params(checked_only)
727
+ for p in params:
728
+ try:
729
+ p.reset()
730
+ except (AttributeError, TypeError):
731
+ ## TypeError might occur if p.reset(v) is called with
732
+ ## missing 1 required positional argument.
733
+ pass
733
734
 
734
735
  def copy_to_clipboard(self, checked_only=False):
735
736
  params = self.get_params(checked_only)
@@ -1041,7 +1042,7 @@ class ToggleButton(wx.ToggleButton):
1041
1042
  self.SetBitmap(Icon(icon))
1042
1043
 
1043
1044
 
1044
- class TextCtrl(wx.Control):
1045
+ class TextBox(wx.Control):
1045
1046
  """Text control
1046
1047
 
1047
1048
  Args:
@@ -1095,6 +1096,9 @@ class TextCtrl(wx.Control):
1095
1096
  pass
1096
1097
 
1097
1098
 
1099
+ TextCtrl = TextBox #: for backward compatibility
1100
+
1101
+
1098
1102
  class Choice(wx.Control):
1099
1103
  """Editable Choice (ComboBox) control
1100
1104
 
mwx/framework.py CHANGED
@@ -1,7 +1,7 @@
1
1
  #! python3
2
2
  """mwxlib framework.
3
3
  """
4
- __version__ = "1.1.7"
4
+ __version__ = "1.2.3"
5
5
  __author__ = "Kazuya O'moto <komoto@jeol.co.jp>"
6
6
 
7
7
  from contextlib import contextmanager
mwx/graphman.py CHANGED
@@ -16,6 +16,7 @@ import platform
16
16
  import re
17
17
  import wx
18
18
  from wx import aui
19
+ from wx import stc
19
20
 
20
21
  from matplotlib import cm
21
22
  from matplotlib import colors
@@ -322,18 +323,22 @@ class LayerInterface(CtrlInterface):
322
323
  except AttributeError:
323
324
  self.parameters = None
324
325
 
325
- def copy_params(**kwargs):
326
- if self.parameters:
327
- return self.copy_to_clipboard(**kwargs)
326
+ def copy_params(evt, checked_only=False):
327
+ if isinstance(evt.EventObject, (wx.TextEntry, stc.StyledTextCtrl)):
328
+ evt.Skip()
329
+ elif self.parameters:
330
+ self.copy_to_clipboard(checked_only)
328
331
 
329
- def paste_params(**kwargs):
330
- if self.parameters:
331
- return self.paste_from_clipboard(**kwargs)
332
+ def paste_params(evt, checked_only=False):
333
+ if isinstance(evt.EventObject, (wx.TextEntry, stc.StyledTextCtrl)):
334
+ evt.Skip()
335
+ elif self.parameters:
336
+ self.paste_from_clipboard(checked_only)
332
337
 
333
- def reset_params(**kwargs):
338
+ def reset_params(evt, checked_only=False):
334
339
  self.Draw(None)
335
340
  if self.parameters:
336
- return self.set_params(**kwargs)
341
+ self.reset_params(checked_only)
337
342
 
338
343
  self.handler.append({ # DNA<Layer>
339
344
  None : {
@@ -356,15 +361,15 @@ class LayerInterface(CtrlInterface):
356
361
  })
357
362
  self.menu = [
358
363
  (wx.ID_COPY, "&Copy params\t(C-c)", "Copy params",
359
- lambda v: copy_params(checked_only=wx.GetKeyState(wx.WXK_SHIFT)),
364
+ lambda v: copy_params(v, checked_only=wx.GetKeyState(wx.WXK_SHIFT)),
360
365
  lambda v: v.Enable(bool(self.parameters))),
361
366
 
362
367
  (wx.ID_PASTE, "&Paste params\t(C-v)", "Read params",
363
- lambda v: paste_params(checked_only=wx.GetKeyState(wx.WXK_SHIFT)),
368
+ lambda v: paste_params(v, checked_only=wx.GetKeyState(wx.WXK_SHIFT)),
364
369
  lambda v: v.Enable(bool(self.parameters))),
365
370
  (),
366
371
  (wx.ID_RESET, "&Reset params\t(C-n)", "Reset params", Icon('-'),
367
- lambda v: reset_params(checked_only=wx.GetKeyState(wx.WXK_SHIFT)),
372
+ lambda v: reset_params(v, checked_only=wx.GetKeyState(wx.WXK_SHIFT)),
368
373
  lambda v: v.Enable(bool(self.parameters))),
369
374
  (),
370
375
  (wx.ID_EDIT, "&Edit module", "Edit module", Icon('pen'),
mwx/matplot2.py CHANGED
@@ -348,22 +348,22 @@ class MatplotPanel(wx.Panel):
348
348
  xbound = property(
349
349
  lambda self: np.array(self.axes.get_xbound()),
350
350
  lambda self,v: self.axes.set_xbound(v),
351
- doc="x-axis numerical bounds where lowerBound < upperBound)")
351
+ doc="X-axis numerical bounds where lowerBound < upperBound)")
352
352
 
353
353
  ybound = property(
354
354
  lambda self: np.array(self.axes.get_ybound()),
355
355
  lambda self,v: self.axes.set_ybound(v),
356
- doc="y-axis numerical bounds where lowerBound < upperBound)")
356
+ doc="Y-axis numerical bounds where lowerBound < upperBound)")
357
357
 
358
358
  xlim = property(
359
359
  lambda self: np.array(self.axes.get_xlim()),
360
360
  lambda self,v: self.axes.set_xlim(v),
361
- doc="x-axis range [left, right]")
361
+ doc="X-axis range [left, right]")
362
362
 
363
363
  ylim = property(
364
364
  lambda self: np.array(self.axes.get_ylim()),
365
365
  lambda self,v: self.axes.set_ylim(v),
366
- doc="y-axis range [bottom, top]")
366
+ doc="Y-axis range [bottom, top]")
367
367
 
368
368
  @property
369
369
  def ddpu(self):
mwx/matplot2g.py CHANGED
@@ -14,7 +14,7 @@ from scipy import ndimage as ndi
14
14
 
15
15
  from . import framework as mwx
16
16
  from .framework import Menu
17
- ## from .utilus import warn
17
+ from .utilus import warn
18
18
  from .utilus import funcall as _F
19
19
  from .controls import Clipboard
20
20
  from .matplot2 import MatplotPanel
@@ -88,7 +88,7 @@ def _to_image(src, cutoff=0, threshold=None, binning=1):
88
88
  if src.dtype == np.uint8: # RGB or gray image <uint8>
89
89
  return bins, (0, 255), src
90
90
 
91
- if hasattr(cutoff, '__iter__'): # cutoff vlim:list is specified.
91
+ if hasattr(cutoff, '__iter__'): # cutoff vlim: (vmin, vmax) is specified.
92
92
  a, b = cutoff
93
93
  elif cutoff > 0:
94
94
  a = np.percentile(src, cutoff)
@@ -129,7 +129,7 @@ class AxesImagePhantom:
129
129
  """
130
130
  def __init__(self, parent, buf, name, show=True,
131
131
  localunit=None, aspect=1.0, **attributes):
132
- self.__owner = parent
132
+ self.parent = parent
133
133
  self.__name = name
134
134
  self.__localunit = localunit or None # [+] value, no assertion
135
135
  self.__aspect_ratio = aspect
@@ -141,7 +141,7 @@ class AxesImagePhantom:
141
141
  threshold = self.parent.nbytes_threshold,
142
142
  )
143
143
  self.__bins = bins
144
- self.__vlim = vlim
144
+ self.__cuts = vlim
145
145
  self.__art = parent.axes.imshow(img,
146
146
  cmap = cm.gray,
147
147
  aspect = 'equal',
@@ -157,39 +157,6 @@ class AxesImagePhantom:
157
157
  def __eq__(self, x):
158
158
  return x is self.__art
159
159
 
160
- parent = property(lambda self: self.__owner)
161
- artist = property(lambda self: self.__art)
162
- name = property(lambda self: self.__name)
163
- buffer = property(lambda self: self.__buf) # buffer.setter is defined below.
164
- binning = property(lambda self: self.__bins)
165
-
166
- image = property(
167
- lambda self: self.__art.get_array(),
168
- doc="A displayed image array<uint8>.")
169
-
170
- vlim = property(
171
- lambda self: self.__vlim,
172
- doc="Image lower/upper values.")
173
-
174
- clim = property(
175
- lambda self: self.__art.get_clim(),
176
- lambda self,v: self.__art.set_clim(v),
177
- doc="Buffer lower/upper values.")
178
-
179
- attributes = property(
180
- lambda self: self.__attributes,
181
- doc="Miscellaneous info about the frame/buffer.")
182
-
183
- pathname = property(
184
- lambda self: self.__attributes.get('pathname'),
185
- lambda self,v: self.update_attributes({'pathname': v}),
186
- doc="A fullpath of buffer, when bounds to file.")
187
-
188
- annotation = property(
189
- lambda self: self.__attributes.get('annotation', ''),
190
- lambda self,v: self.update_attributes({'annotation': v}),
191
- doc="Annotation of the buffer.")
192
-
193
160
  def update_attributes(self, attr=None, **kwargs):
194
161
  """Update frame-specifc attributes.
195
162
  The frame holds any attributes with dictionary
@@ -215,10 +182,70 @@ class AxesImagePhantom:
215
182
  if {'pathname', 'annotation'} & attr.keys():
216
183
  self.parent.handler('frame_updated', self)
217
184
 
185
+ def update_buffer(self, buf=None):
186
+ """Update buffer and the image (internal use only)."""
187
+ if buf is not None:
188
+ self.__buf = _to_buffer(buf)
189
+
190
+ bins, vlim, img = _to_image(self.__buf,
191
+ cutoff = self.parent.score_percentile,
192
+ threshold = self.parent.nbytes_threshold,
193
+ )
194
+ self.__bins = bins
195
+ self.__cuts = vlim
196
+ self.__art.set_array(img)
197
+ self.parent.handler('frame_modified', self)
198
+
199
+ def update_extent(self):
200
+ """Update logical extent of the image (internal use only)."""
201
+ h, w = self.__buf.shape[:2]
202
+ ux, uy = self.xy_unit
203
+ w *= ux/2
204
+ h *= uy/2
205
+ self.__art.set_extent((-w,w,-h,h))
206
+
218
207
  selector = _Property('Selector')
219
208
  markers = _Property('Markers')
220
209
  region = _Property('Region')
221
210
 
211
+ artist = property(
212
+ lambda self: self.__art)
213
+
214
+ binning = property(
215
+ lambda self: self.__bins,
216
+ doc="Binning value resulting from the score_percentile.")
217
+
218
+ cuts = property(
219
+ lambda self: self.__cuts,
220
+ doc="Lower/Upper cutoff values of the buffer.")
221
+
222
+ image = property(
223
+ lambda self: self.__art.get_array(),
224
+ doc="A displayed image array<uint8>.")
225
+
226
+ clim = property(
227
+ lambda self: self.__art.get_clim(),
228
+ lambda self,v: self.__art.set_clim(v),
229
+ doc="Lower/Upper color limit values of the buffer.")
230
+
231
+ attributes = property(
232
+ lambda self: self.__attributes,
233
+ doc="Miscellaneous info about the frame/buffer.")
234
+
235
+ pathname = property(
236
+ lambda self: self.__attributes.get('pathname'),
237
+ lambda self,v: self.update_attributes({'pathname': v}),
238
+ doc="A fullpath of buffer, when bounds to file.")
239
+
240
+ annotation = property(
241
+ lambda self: self.__attributes.get('annotation', ''),
242
+ lambda self,v: self.update_attributes({'annotation': v}),
243
+ doc="Annotation of the buffer.")
244
+
245
+ @property
246
+ def name(self):
247
+ return self.__name
248
+
222
249
  @name.setter
223
250
  def name(self, v):
224
251
  self.__name = v
@@ -278,28 +305,6 @@ class AxesImagePhantom:
278
305
  """Page number in the parent book."""
279
306
  return self.parent.index(self)
280
307
 
281
- def update_buffer(self, buf=None):
282
- """Update buffer and the image (internal use only)."""
283
- if buf is not None:
284
- self.__buf = _to_buffer(buf)
285
-
286
- bins, vlim, img = _to_image(self.__buf,
287
- cutoff = self.parent.score_percentile,
288
- threshold = self.parent.nbytes_threshold,
289
- )
290
- self.__bins = bins
291
- self.__vlim = vlim
292
- self.__art.set_array(img)
293
- self.parent.handler('frame_modified', self)
294
-
295
- def update_extent(self):
296
- """Update logical extent of the image (internal use only)."""
297
- h, w = self.__buf.shape[:2]
298
- ux, uy = self.xy_unit
299
- w *= ux/2
300
- h *= uy/2
301
- self.__art.set_extent((-w,w,-h,h))
302
-
303
308
  @property
304
309
  def roi(self):
305
310
  """Current buffer ROI (region of interest)."""
@@ -315,9 +320,14 @@ class AxesImagePhantom:
315
320
  self.roi[:] = v # cannot broadcast input array into different shape
316
321
  self.update_buffer()
317
322
 
323
+ @property
324
+ def buffer(self):
325
+ return self.__buf
326
+
318
327
  @buffer.setter
319
328
  def buffer(self, v):
320
329
  self.update_buffer(v)
330
+ self.update_extent()
321
331
 
322
332
  def xytoc(self, x, y=None, nearest=True):
323
333
  """Convert xydata (x,y) -> data[(x,y)] value of neaerst pixel.
@@ -618,9 +628,6 @@ class GraphPlot(MatplotPanel):
618
628
  - aspect : aspect ratio
619
629
  - pathname : full path of the buffer file
620
630
  """
621
- if buf is None:
622
- return
623
-
624
631
  if isinstance(buf, str):
625
632
  buf = Image.open(buf)
626
633
 
@@ -687,9 +694,6 @@ class GraphPlot(MatplotPanel):
687
694
  j = self.index(j)
688
695
 
689
696
  buffers = [art.buffer for art in self.__Arts]
690
- if hasattr(j, '__iter__'):
691
- return [buffers[i] for i in j]
692
-
693
697
  return buffers[j] # j can also be slicing
694
698
 
695
699
  def __setitem__(self, j, v):
@@ -699,14 +703,14 @@ class GraphPlot(MatplotPanel):
699
703
  except ValueError:
700
704
  return self.load(v, name=j) # new buffer
701
705
 
702
- if hasattr(j, '__iter__') or isinstance(j, slice):
703
- raise ValueError("attempt to assign buffers into slice")
706
+ if isinstance(j, slice):
707
+ raise ValueError("attempt to assign buffers via slicing")
704
708
 
705
709
  if v is None:
706
- self.__delitem__(j)
710
+ raise ValueError("values must be buffers, not NoneType.")
707
711
  else:
708
712
  art = self.__Arts[j]
709
- art.update_buffer(v)
713
+ art.update_buffer(v) # update buffer
710
714
  art.update_extent()
711
715
  self.select(j)
712
716
 
@@ -714,9 +718,7 @@ class GraphPlot(MatplotPanel):
714
718
  if isinstance(j, str):
715
719
  j = self.index(j)
716
720
 
717
- if hasattr(j, '__iter__'):
718
- arts = [self.__Arts[i] for i in j]
719
- elif isinstance(j, slice):
721
+ if isinstance(j, slice):
720
722
  arts = self.__Arts[j]
721
723
  else:
722
724
  arts = [self.__Arts[j]]
@@ -784,16 +786,27 @@ class GraphPlot(MatplotPanel):
784
786
  if self.__Arts and self.__index is not None:
785
787
  return self.__Arts[self.__index]
786
788
 
787
- buffer = property(
788
- lambda self: self.frame and self.frame.buffer,
789
- lambda self,v: self.__setitem__(self.__index, v),
790
- lambda self: self.__delitem__(self.__index),
791
- doc="current buffer array")
789
+ @property
790
+ def buffer(self):
791
+ """Current buffer array."""
792
+ if self.frame:
793
+ return self.frame.buffer
794
+
795
+ @buffer.setter
796
+ def buffer(self, v):
797
+ if self.frame:
798
+ self.__setitem__(self.__index, v)
799
+ else:
800
+ self.load(v)
801
+
802
+ @property
803
+ def newbuffer(self):
804
+ """New buffer loader."""
805
+ return None
792
806
 
793
- newbuffer = property(
794
- lambda self: None,
795
- lambda self,v: self.load(v),
796
- doc="new buffer loader")
807
+ @newbuffer.setter
808
+ def newbuffer(self, v):
809
+ self.load(v)
797
810
 
798
811
  @property
799
812
  def unit(self):
@@ -951,7 +964,7 @@ class GraphPlot(MatplotPanel):
951
964
  data = frame.roi
952
965
  GraphPlot.clipboard_name = name
953
966
  GraphPlot.clipboard_data = data
954
- bins, vlim, img = _to_image(data, frame.vlim)
967
+ bins, vlim, img = _to_image(data, frame.cuts)
955
968
  Clipboard.imwrite(img)
956
969
  self.message("Write buffer to clipboard.")
957
970
  except Exception as e:
mwx/mgplt.py CHANGED
@@ -139,7 +139,7 @@ class GnuplotFrame(mwx.Frame):
139
139
  lambda v: self.panel.paste_from_clipboard()),
140
140
  (),
141
141
  (wx.ID_RESET, "&Reset params\tCtrl-n", "Reset params to ini-value",
142
- lambda v: self.panel.set_params()),
142
+ lambda v: self.panel.reset_params()),
143
143
  ]
144
144
  self.menubar["Gnuplot"] = [
145
145
  (mwx.ID_(80), "&Gnuplot setting\tCtrl-g", "Edit settings",
mwx/nutshell.py CHANGED
@@ -4,6 +4,7 @@
4
4
  from functools import wraps
5
5
  from importlib import import_module
6
6
  from contextlib import contextmanager
7
+ from pathlib import Path
7
8
  from pprint import pformat
8
9
  from bdb import BdbQuit
9
10
  import traceback
@@ -32,7 +33,8 @@ from .framework import CtrlInterface, AuiNotebook, Menu
32
33
 
33
34
 
34
35
  ## URL pattern (flag = re.M | re.A)
35
- url_re = r"https?://[\w/:%#$&?()~.=+-]+"
36
+ ## url_re = r"https?://[\w/:%#$&?()~.=+-]+"
37
+ url_re = r"https?://[\w/:%#$&?~.=+-]+" # excluding ()
36
38
 
37
39
  ## no-file pattern
38
40
  nofile_re = r'[\/:*?"<>|]'
@@ -220,21 +222,21 @@ class AutoCompInterfaceMixin:
220
222
  if self.CallTipActive():
221
223
  self.CallTipCancel()
222
224
 
223
- text = next(self.gen_text_at_caret(), None)
225
+ text = self.SelectedText or self.expr_at_caret or self.line_at_caret
224
226
  if text:
225
227
  text = introspect.getRoot(text, terminator='(')
226
228
  try:
227
229
  obj = self.eval(text)
228
230
  self.help(obj)
229
231
  except Exception as e:
230
- self.message("- {} : {!r}".format(e, text))
232
+ self.message(e)
231
233
 
232
234
  def call_helpTip(self, evt):
233
235
  """Show a calltip for the selected function."""
234
236
  if self.CallTipActive():
235
237
  self.CallTipCancel()
236
238
 
237
- text = next(self.gen_text_at_caret(), None)
239
+ text = self.SelectedText or self.expr_at_caret or self.line_at_caret
238
240
  if text:
239
241
  p = self.cpos
240
242
  self.autoCallTipShow(text,
@@ -629,7 +631,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
629
631
 
630
632
  ## Custom indicators ([BUG] indicator=1 is reset when the buffer is udpated.)
631
633
  ## [10-11] filter_text
632
- ## [2] URL for load_file
634
+ ## [2] URL
633
635
  ## [3] match_paren
634
636
  self.IndicatorSetStyle(10, stc.STC_INDIC_TEXTFORE)
635
637
  self.IndicatorSetForeground(10, "red")
@@ -1721,10 +1723,18 @@ class Buffer(EditorInterface, EditWindow):
1721
1723
  return self.__filename
1722
1724
 
1723
1725
  def update_filestamp(self, fn):
1724
- if fn and os.path.isfile(fn):
1725
- self.__mtime = os.path.getmtime(fn) # update timestamp (modified time)
1726
+ self.__path = Path(fn)
1727
+ if self.__path.is_file():
1728
+ self.__mtime = self.__path.stat().st_mtime # update timestamp (modified time)
1729
+ elif re.match(url_re, fn):
1730
+ self.__mtime = -1
1726
1731
  else:
1727
- self.__mtime = None
1732
+ try:
1733
+ self.__path.resolve(True) # Check if the path format is valid.
1734
+ except FileNotFoundError:
1735
+ self.__mtime = False # valid path (but not found)
1736
+ except OSError:
1737
+ self.__mtime = None # *invalid path*
1728
1738
  if self.__filename != fn:
1729
1739
  self.__filename = fn
1730
1740
  self.update_caption()
@@ -1735,16 +1745,31 @@ class Buffer(EditorInterface, EditWindow):
1735
1745
 
1736
1746
  Returns:
1737
1747
  None : no file
1738
- = 0 : a file
1748
+ = 0 : a file (even if not found)
1739
1749
  > 0 : a file edited externally
1740
1750
  < 0 : a url file
1741
1751
  """
1742
- fn = self.filename
1743
- if os.path.isfile(fn):
1744
- return os.path.getmtime(fn) - self.__mtime
1745
- if re.match(url_re, fn):
1746
- return -1
1747
- return None
1752
+ try:
1753
+ return self.__path.stat().st_mtime - self.__mtime
1754
+ except FileNotFoundError:
1755
+ self.__mtime = False # valid path (but not found)
1756
+ except OSError:
1757
+ pass
1758
+ return self.__mtime
1759
+
1760
+ @property
1761
+ def need_buffer_save(self):
1762
+ """Returns whether the buffer should be saved.
1763
+ The file has been modified internally.
1764
+ """
1765
+ return self.mtdelta is not None and self.IsModified()
1766
+
1767
+ @property
1768
+ def need_buffer_load(self):
1769
+ """Returns whether the buffer should be loaded.
1770
+ The file has been modified externally.
1771
+ """
1772
+ return self.mtdelta is not None and self.mtdelta > 0
1748
1773
 
1749
1774
  @property
1750
1775
  def caption_prefix(self):
@@ -1769,20 +1794,6 @@ class Buffer(EditorInterface, EditWindow):
1769
1794
  except AttributeError:
1770
1795
  pass
1771
1796
 
1772
- @property
1773
- def need_buffer_save(self):
1774
- """Returns whether the buffer should be saved.
1775
- The file has been modified internally.
1776
- """
1777
- return self.mtdelta is not None and self.IsModified()
1778
-
1779
- @property
1780
- def need_buffer_load(self):
1781
- """Returns whether the buffer should be loaded.
1782
- The file has been modified externally.
1783
- """
1784
- return self.mtdelta is not None and self.mtdelta > 0
1785
-
1786
1797
  def __init__(self, parent, filename, **kwargs):
1787
1798
  EditWindow.__init__(self, parent, **kwargs)
1788
1799
  EditorInterface.__init__(self)
@@ -1952,20 +1963,20 @@ class Buffer(EditorInterface, EditWindow):
1952
1963
  ## Processing text selection, dragging, or dragging+
1953
1964
  evt.Skip()
1954
1965
  return
1966
+
1955
1967
  pos = evt.Position
1968
+ self.goto_char(pos) # Clear selection
1956
1969
  i = 2
1957
1970
  if self.IndicatorValueAt(i, pos): # [C-indic click]
1958
1971
  p = self.IndicatorStart(i, pos)
1959
1972
  q = self.IndicatorEnd(i, pos)
1960
1973
  url = self.GetTextRange(p, q).strip()
1961
- self.message("URL {!r}".format(url))
1962
1974
  if wx.GetKeyState(wx.WXK_SHIFT): # [C-S-indic click]
1963
1975
  import webbrowser
1964
1976
  return webbrowser.open(url)
1965
1977
  else:
1966
1978
  ## Note: post-call for the confirmation dialog.
1967
1979
  wx.CallAfter(self.parent.load_file, url)
1968
- self.anchor = pos # Clear selection
1969
1980
 
1970
1981
  def on_modified(self, buf):
1971
1982
  """Called when the buffer is modified."""
@@ -2083,34 +2094,21 @@ class Buffer(EditorInterface, EditWindow):
2083
2094
  dispatcher.send(signal='Interpreter.push',
2084
2095
  sender=self, command=None, more=False)
2085
2096
 
2086
- def gen_text_at_caret(self):
2087
- """Generates the selected text,
2088
- otherwise the line or expression at the caret.
2089
- """
2090
- def _gen_text():
2091
- text = self.SelectedText
2092
- if text:
2093
- yield text
2094
- else:
2095
- yield self.line_at_caret
2096
- yield self.expr_at_caret
2097
- return filter(None, _gen_text())
2098
-
2099
2097
  def eval_line(self):
2100
2098
  if self.CallTipActive():
2101
2099
  self.CallTipCancel()
2102
2100
 
2103
- status = "No words"
2104
- for text in self.gen_text_at_caret():
2101
+ text = self.SelectedText or self.expr_at_caret
2102
+ if text:
2105
2103
  try:
2106
2104
  obj = eval(text, self.globals, self.locals)
2107
2105
  except Exception as e:
2108
- status = "- {} : {!r}".format(e, text)
2106
+ self.message(e)
2109
2107
  else:
2110
2108
  self.CallTipShow(self.cpos, pformat(obj))
2111
2109
  self.message(text)
2112
- return
2113
- self.message(status)
2110
+ else:
2111
+ self.message("No words")
2114
2112
 
2115
2113
  def exec_region(self):
2116
2114
  try:
@@ -2419,14 +2417,14 @@ class EditorBook(AuiNotebook, CtrlInterface):
2419
2417
  buf._load_textfile(res.text, filename)
2420
2418
  self.swap_buffer(buf, lineno)
2421
2419
  return True
2422
- return False
2423
- ## raise Exception("The requested URL was not found", filename)
2420
+ ## raise Exception("URL not found:", filename)
2421
+ res.raise_for_status() # raise HTTP error
2424
2422
  if buf._load_file(filename):
2425
2423
  self.swap_buffer(buf, lineno)
2426
2424
  return True
2427
2425
  return False
2428
- except OSError as e:
2429
- self.post_message(e)
2426
+ except (OSError, UnicodeDecodeError) as e:
2427
+ self.post_message("Failed to load:", e)
2430
2428
  self.delete_buffer(buf)
2431
2429
  return False
2432
2430
 
@@ -2441,11 +2439,10 @@ class EditorBook(AuiNotebook, CtrlInterface):
2441
2439
  for fn in dlg.Paths:
2442
2440
  self.find_file(fn)
2443
2441
  return
2444
- if self.load_file(filename) == False: # not None
2442
+ if self.load_file(filename) == False: # noqa: not None
2445
2443
  buf = self.create_buffer(filename)
2446
- buf._Buffer__mtime = 0 # => need_buffer_save
2447
2444
  self.swap_buffer(buf)
2448
- self.post_message(f"New file: {filename!r}.")
2445
+ self.post_message("New file.")
2449
2446
 
2450
2447
  def save_file(self, filename, buf=None, verbose=True):
2451
2448
  """Save the current buffer to a file.
@@ -2467,8 +2464,8 @@ class EditorBook(AuiNotebook, CtrlInterface):
2467
2464
  self.default_buffer = None
2468
2465
  return True
2469
2466
  return False
2470
- except Exception as e:
2471
- self.post_message(f"Failed to save {filename!r}.", e)
2467
+ except (OSError, UnicodeDecodeError) as e:
2468
+ self.post_message("Failed to save:", e)
2472
2469
  return False
2473
2470
 
2474
2471
  def load_buffer(self, buf=None):
@@ -3592,39 +3589,25 @@ class Nautilus(EditorInterface, Shell):
3592
3589
  self.cpos, self.anchor = self.anchor, self.cpos
3593
3590
  self.EnsureCaretVisible()
3594
3591
 
3595
- def gen_text_at_caret(self):
3596
- """Generates the selected text,
3597
- otherwise the line or expression at the caret.
3598
- (override) Generates command line (that starts with a prompt).
3599
- """
3600
- def _gen_text():
3601
- text = self.SelectedText
3602
- if text:
3603
- yield text
3604
- else:
3605
- yield self.getCommand() # self.line_at_caret
3606
- yield self.expr_at_caret
3607
- return filter(None, _gen_text())
3608
-
3609
3592
  def eval_line(self, evt):
3610
3593
  """Evaluate the selected word or line."""
3611
3594
  if self.CallTipActive():
3612
3595
  self.CallTipCancel()
3613
3596
 
3614
- status = "No words"
3615
- for text in self.gen_text_at_caret():
3597
+ text = self.SelectedText or self.expr_at_caret
3598
+ if text:
3616
3599
  tokens = split_words(text)
3617
3600
  try:
3618
3601
  cmd = self.magic_interpret(tokens)
3619
3602
  cmd = self.regulate_cmd(cmd)
3620
3603
  obj = self.eval(cmd)
3621
3604
  except Exception as e:
3622
- status = "- {} : {!r}".format(e, text)
3605
+ self.message(e)
3623
3606
  else:
3624
3607
  self.CallTipShow(self.cpos, pformat(obj))
3625
3608
  self.message(cmd)
3626
- return
3627
- self.message(status)
3609
+ else:
3610
+ self.message("No words")
3628
3611
 
3629
3612
  def exec_region(self, evt):
3630
3613
  """Execute the the selected region."""
@@ -10,7 +10,7 @@ import wx.media
10
10
 
11
11
  from mwx.framework import _F, hotkey
12
12
  from mwx.graphman import Layer
13
- from mwx.controls import LParam, Icon, Button, TextCtrl
13
+ from mwx.controls import LParam, Icon, Button, TextBox
14
14
 
15
15
 
16
16
  def read_info(path):
@@ -101,7 +101,7 @@ class Plugin(Layer):
101
101
  handler=self.set_offset,
102
102
  updater=self.get_offset,
103
103
  )
104
- self.crop = TextCtrl(self, icon="cut", size=(130,-1),
104
+ self.crop = TextBox(self, icon="cut", size=(130,-1),
105
105
  handler=self.set_crop,
106
106
  updater=self.get_crop,
107
107
  )
@@ -210,20 +210,18 @@ class Plugin(Layer):
210
210
 
211
211
  def set_offset(self, tc):
212
212
  """Set offset value by referring to ss/to value."""
213
- try:
213
+ if self._path:
214
214
  self.mc.Seek(self.DELTA + int(tc.value * 1000))
215
- except Exception:
216
- pass
217
215
 
218
216
  def get_offset(self, tc):
219
217
  """Get offset value and assigns it to ss/to value."""
220
- try:
218
+ if self._path:
221
219
  tc.value = round(self.mc.Tell()) / 1000
222
- except Exception:
223
- pass
224
220
 
225
221
  def set_crop(self):
226
222
  """Set crop area (W:H:Left:Top) to roi."""
223
+ if not self._path:
224
+ return
227
225
  frame = self.graph.frame
228
226
  if frame:
229
227
  try:
@@ -238,6 +236,8 @@ class Plugin(Layer):
238
236
 
239
237
  def get_crop(self):
240
238
  """Get crop area (W:H:Left:Top) from roi."""
239
+ if not self._path:
240
+ return
241
241
  crop = ''
242
242
  frame = self.graph.frame
243
243
  if frame:
@@ -246,28 +246,24 @@ class Plugin(Layer):
246
246
  xo, yo = nx[0], ny[1]
247
247
  xp, yp = nx[1], ny[0]
248
248
  crop = "{}:{}:{}:{}".format(xp-xo, yp-yo, xo, yo)
249
- if self._path and not crop:
249
+ if not crop:
250
250
  crop = "{}:{}:0:0".format(*self.video_size)
251
251
  self.crop.Value = crop
252
252
 
253
253
  def seekto(self, offset):
254
254
  """Seek position with offset [ms] from the `to` position."""
255
- try:
255
+ if self._path:
256
256
  t = self.to.value + offset/1000
257
257
  if 0 <= t < self.video_dur:
258
258
  self.to.value = round(t, 3)
259
259
  self.set_offset(self.to)
260
- except AttributeError:
261
- pass
262
260
 
263
261
  def seekd(self, offset):
264
262
  """Seek position with offset [ms] from the current position."""
265
- try:
263
+ if self._path:
266
264
  t = self.mc.Tell() + offset
267
265
  if 0 <= t < self.video_dur * 1000:
268
266
  self.mc.Seek(self.DELTA + t)
269
- except AttributeError:
270
- pass
271
267
 
272
268
  def snapshot(self):
273
269
  """Create a snapshot of the current frame.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mwxlib
3
- Version: 1.1.7
3
+ Version: 1.2.3
4
4
  Summary: A wrapper of matplotlib and wxPython (phoenix)
5
5
  Home-page: https://github.com/komoto48g/mwxlib
6
6
  Author: Kazuya O'moto
@@ -1,14 +1,14 @@
1
- mwx/__init__.py,sha256=psabnAMei5VzB2TsB2qBNLrIZMX0LiqjlXCpNGmDejk,668
1
+ mwx/__init__.py,sha256=pS7ZG8QKRypiFFiaWAq_opBB6I_1viZ0zUMk2TbjzE0,667
2
2
  mwx/bookshelf.py,sha256=b_TMDaNIzLHoL0xbbqb3tt0BnRvhLAqaCn_pBdrigZw,7523
3
- mwx/controls.py,sha256=trJKRFgum3u-8f8hd6N3qk6jHt4dVqiLgE6CKK5EE4U,47971
4
- mwx/framework.py,sha256=bgO-mgMMYZtb3cQtPzOyErEVtpSvd9RJEdqFMpcpz6c,76125
5
- mwx/graphman.py,sha256=LjN1R8UZxHycrgAfnl_SoTT_vHuKcSISr5VVf0cMflM,69821
3
+ mwx/controls.py,sha256=X4zx2h6oggUsQxi2PRk4RUsJieYTmcAPIvWwaz-ysTI,48107
4
+ mwx/framework.py,sha256=wXI4nzQcG0NV3c5ne4io9HJ1y7jd7xbjB_cOao8HKsU,76125
5
+ mwx/graphman.py,sha256=RqD0W9I2BvJ3Q2kyMiyyg4n-T4-_x7PDuCI5bGAg5k4,70110
6
6
  mwx/images.py,sha256=oxCn0P-emiWujSS2gUgU5TUnr5cPjix2jBcjOBDr24I,48701
7
- mwx/matplot2.py,sha256=RuVWXC2A_qgZRNmBBptbHDn5MyxaWBqp3ru4bP_lDE0,33150
8
- mwx/matplot2g.py,sha256=dBAODQvSM_yf2uQUCrRO03ZOK3MycR8lEXTJfRXDlbY,64432
7
+ mwx/matplot2.py,sha256=Zwte-wwzCg_OHzsBniVgKdaNLzsvJaa1gc0n7VdAqxw,33150
8
+ mwx/matplot2g.py,sha256=tShPGy7whj51yvANja-MDmjfxiLubQ1C6rCsBZ0DAnY,64473
9
9
  mwx/matplot2lg.py,sha256=JRWjWnLJUytbSq6wxs4P0gbVUr3xoLSF6Wwqd5V_pJI,27404
10
- mwx/mgplt.py,sha256=M5rt-H7Uq1OHnlFvMA4a3945UBvppbR9L_mw8NL_YZ0,5602
11
- mwx/nutshell.py,sha256=wzcZSxTM9G2bYtIBri51-AA6JhiKrtfsh-DMgwJZ5io,141340
10
+ mwx/mgplt.py,sha256=8mXbHpCmm7lz3XbAxOg7IVC7DaSGBEby1UfTlMl9kjk,5604
11
+ mwx/nutshell.py,sha256=7nQ7UUFM9kvjDjHNUEdOkkeqZPiU6zOERwamqBdJpQs,140856
12
12
  mwx/testsuite.py,sha256=kiM3-BVhr42LRRN7xG7pYl3at8o2vnypWSxD8KRvA7c,1228
13
13
  mwx/utilus.py,sha256=HFvP682SyeSp8yNfUrdUXPhWdLuWVlsUSg6LqNBJOT8,37451
14
14
  mwx/wxmon.py,sha256=yzWqrbY6LzpfRwQeytYUeqFhFuLVm_XEvrVAL_k0HBQ,12756
@@ -16,14 +16,14 @@ mwx/wxpdb.py,sha256=AObuf4JLAmlQLj-yf0_mkBBd-Bhhz2Vb8hbTVcHEZOs,18875
16
16
  mwx/wxwil.py,sha256=hhyB1lPrF9ixeObxCOKQv0Theu-B-kpJg_yVU3EGSNg,5406
17
17
  mwx/wxwit.py,sha256=1hHtMi2YEy2T_LnUpwdmrIdtCuvxMOFyykqnbq6jLP0,7294
18
18
  mwx/plugins/__init__.py,sha256=jnJ-Sl9XJ_7BFDslD_r7dsbxsOT57q_IaEriV53XIGY,41
19
- mwx/plugins/ffmpeg_view.py,sha256=Yp1BMeNfkTTPwykvWW1_h4wrvhBSeBUdAvKhnMQIa6g,11102
19
+ mwx/plugins/ffmpeg_view.py,sha256=NIHFJLPeliOXH3ke0NvQzYBhY0oeEP6dgZQofB5Ry1c,11031
20
20
  mwx/plugins/fft_view.py,sha256=08A_Y73XirV7kXpwf-v0mUA0Hr0MOfdMXv3tvL1hvWA,2789
21
21
  mwx/plugins/frame_listview.py,sha256=gowjQ-ARNonMkDSXkQgPKq4U9YBJ-vQ0jK2krBVOdCs,10420
22
22
  mwx/plugins/line_profile.py,sha256=zzm6_7lnAnNepLbh07ordp3nRWDFQJtu719ZVjrVf8s,819
23
23
  mwx/py/__init__.py,sha256=xykgfOytOwNuvXsfkLoumFZSTN-iBsHOjczYXngjmUE,12
24
24
  mwx/py/filling.py,sha256=fumUG1F5M9TL-Dfqni4G85uk7TmvnUunTbdcPDV0vfo,16857
25
- mwxlib-1.1.7.dist-info/LICENSE,sha256=PGtRKCaTkmUDlBQwpptJAxJtdqxIUtAmdBsaT9nUVkA,1091
26
- mwxlib-1.1.7.dist-info/METADATA,sha256=G8xDeGhF9BCMfozwYrtRmZgMltFt3QcKGv_ka5UjSEg,7258
27
- mwxlib-1.1.7.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
28
- mwxlib-1.1.7.dist-info/top_level.txt,sha256=SI1Mh118AstnUFGPNq5aMNKiAnVNmZk1S9Ij-OwAEpY,4
29
- mwxlib-1.1.7.dist-info/RECORD,,
25
+ mwxlib-1.2.3.dist-info/LICENSE,sha256=PGtRKCaTkmUDlBQwpptJAxJtdqxIUtAmdBsaT9nUVkA,1091
26
+ mwxlib-1.2.3.dist-info/METADATA,sha256=2sSF51qYRi78w0McPUzYn2yr9H1ISRKXMfvWSB-ATls,7258
27
+ mwxlib-1.2.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
28
+ mwxlib-1.2.3.dist-info/top_level.txt,sha256=SI1Mh118AstnUFGPNq5aMNKiAnVNmZk1S9Ij-OwAEpY,4
29
+ mwxlib-1.2.3.dist-info/RECORD,,
File without changes