scipion-pyworkflow 3.10.5__py3-none-any.whl → 3.11.0__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.
Files changed (127) hide show
  1. pyworkflow/config.py +131 -67
  2. pyworkflow/constants.py +12 -2
  3. pyworkflow/object.py +3 -2
  4. pyworkflow/plugin.py +93 -44
  5. pyworkflow/project/scripts/fix_links.py +4 -1
  6. pyworkflow/resources/showj/arrowDown.png +0 -0
  7. pyworkflow/resources/showj/arrowUp.png +0 -0
  8. pyworkflow/resources/showj/background_section.png +0 -0
  9. pyworkflow/resources/showj/colRowModeOff.png +0 -0
  10. pyworkflow/resources/showj/colRowModeOn.png +0 -0
  11. pyworkflow/resources/showj/delete.png +0 -0
  12. pyworkflow/resources/showj/doc_icon.png +0 -0
  13. pyworkflow/resources/showj/download_icon.png +0 -0
  14. pyworkflow/resources/showj/enabled_gallery.png +0 -0
  15. pyworkflow/resources/showj/galleryViewOff.png +0 -0
  16. pyworkflow/resources/showj/galleryViewOn.png +0 -0
  17. pyworkflow/resources/showj/goto.png +0 -0
  18. pyworkflow/resources/showj/menu.png +0 -0
  19. pyworkflow/resources/showj/separator.png +0 -0
  20. pyworkflow/resources/showj/tableViewOff.png +0 -0
  21. pyworkflow/resources/showj/tableViewOn.png +0 -0
  22. pyworkflow/resources/showj/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  23. pyworkflow/resources/showj/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  24. pyworkflow/resources/showj/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  25. pyworkflow/resources/showj/volumeOff.png +0 -0
  26. pyworkflow/resources/showj/volumeOn.png +0 -0
  27. pyworkflow/viewer.py +23 -1
  28. pyworkflowtests/objects.py +2 -2
  29. pyworkflowtests/protocols.py +1 -3
  30. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/METADATA +21 -25
  31. scipion_pyworkflow-3.11.0.dist-info/RECORD +71 -0
  32. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/WHEEL +1 -1
  33. scipion_pyworkflow-3.11.0.dist-info/entry_points.txt +2 -0
  34. pyworkflow/apps/__init__.py +0 -29
  35. pyworkflow/apps/pw_manager.py +0 -37
  36. pyworkflow/apps/pw_plot.py +0 -51
  37. pyworkflow/apps/pw_project.py +0 -113
  38. pyworkflow/apps/pw_protocol_list.py +0 -143
  39. pyworkflow/apps/pw_protocol_run.py +0 -51
  40. pyworkflow/apps/pw_run_tests.py +0 -267
  41. pyworkflow/apps/pw_schedule_run.py +0 -322
  42. pyworkflow/apps/pw_sleep.py +0 -37
  43. pyworkflow/apps/pw_sync_data.py +0 -439
  44. pyworkflow/apps/pw_viewer.py +0 -78
  45. pyworkflow/gui/__init__.py +0 -36
  46. pyworkflow/gui/browser.py +0 -726
  47. pyworkflow/gui/canvas.py +0 -1190
  48. pyworkflow/gui/dialog.py +0 -977
  49. pyworkflow/gui/form.py +0 -2637
  50. pyworkflow/gui/graph.py +0 -247
  51. pyworkflow/gui/graph_layout.py +0 -271
  52. pyworkflow/gui/gui.py +0 -566
  53. pyworkflow/gui/matplotlib_image.py +0 -233
  54. pyworkflow/gui/plotter.py +0 -247
  55. pyworkflow/gui/project/__init__.py +0 -25
  56. pyworkflow/gui/project/base.py +0 -192
  57. pyworkflow/gui/project/constants.py +0 -139
  58. pyworkflow/gui/project/labels.py +0 -205
  59. pyworkflow/gui/project/project.py +0 -492
  60. pyworkflow/gui/project/searchprotocol.py +0 -154
  61. pyworkflow/gui/project/searchrun.py +0 -181
  62. pyworkflow/gui/project/steps.py +0 -171
  63. pyworkflow/gui/project/utils.py +0 -332
  64. pyworkflow/gui/project/variables.py +0 -179
  65. pyworkflow/gui/project/viewdata.py +0 -472
  66. pyworkflow/gui/project/viewprojects.py +0 -510
  67. pyworkflow/gui/project/viewprotocols.py +0 -2093
  68. pyworkflow/gui/project/viewprotocols_extra.py +0 -560
  69. pyworkflow/gui/text.py +0 -771
  70. pyworkflow/gui/tooltip.py +0 -185
  71. pyworkflow/gui/tree.py +0 -684
  72. pyworkflow/gui/widgets.py +0 -307
  73. pyworkflow/mapper/__init__.py +0 -26
  74. pyworkflow/mapper/mapper.py +0 -222
  75. pyworkflow/mapper/sqlite.py +0 -1578
  76. pyworkflow/mapper/sqlite_db.py +0 -145
  77. pyworkflow/project/__init__.py +0 -31
  78. pyworkflow/project/config.py +0 -454
  79. pyworkflow/project/manager.py +0 -180
  80. pyworkflow/project/project.py +0 -2010
  81. pyworkflow/protocol/__init__.py +0 -38
  82. pyworkflow/protocol/bibtex.py +0 -48
  83. pyworkflow/protocol/constants.py +0 -87
  84. pyworkflow/protocol/executor.py +0 -455
  85. pyworkflow/protocol/hosts.py +0 -313
  86. pyworkflow/protocol/launch.py +0 -270
  87. pyworkflow/protocol/package.py +0 -42
  88. pyworkflow/protocol/params.py +0 -741
  89. pyworkflow/protocol/protocol.py +0 -2582
  90. pyworkflow/tests/__init__.py +0 -29
  91. pyworkflow/tests/test_utils.py +0 -25
  92. pyworkflow/tests/tests.py +0 -341
  93. pyworkflow/utils/__init__.py +0 -38
  94. pyworkflow/utils/dataset.py +0 -414
  95. pyworkflow/utils/echo.py +0 -104
  96. pyworkflow/utils/graph.py +0 -169
  97. pyworkflow/utils/log.py +0 -284
  98. pyworkflow/utils/path.py +0 -528
  99. pyworkflow/utils/process.py +0 -132
  100. pyworkflow/utils/profiler.py +0 -92
  101. pyworkflow/utils/progressbar.py +0 -154
  102. pyworkflow/utils/properties.py +0 -631
  103. pyworkflow/utils/reflection.py +0 -129
  104. pyworkflow/utils/utils.py +0 -879
  105. pyworkflow/utils/which.py +0 -229
  106. pyworkflow/webservices/__init__.py +0 -8
  107. pyworkflow/webservices/config.py +0 -11
  108. pyworkflow/webservices/notifier.py +0 -162
  109. pyworkflow/webservices/repository.py +0 -59
  110. pyworkflow/webservices/workflowhub.py +0 -74
  111. pyworkflowtests/tests/__init__.py +0 -0
  112. pyworkflowtests/tests/test_canvas.py +0 -72
  113. pyworkflowtests/tests/test_domain.py +0 -45
  114. pyworkflowtests/tests/test_logs.py +0 -74
  115. pyworkflowtests/tests/test_mappers.py +0 -392
  116. pyworkflowtests/tests/test_object.py +0 -507
  117. pyworkflowtests/tests/test_project.py +0 -42
  118. pyworkflowtests/tests/test_protocol_execution.py +0 -135
  119. pyworkflowtests/tests/test_protocol_export.py +0 -78
  120. pyworkflowtests/tests/test_protocol_output.py +0 -158
  121. pyworkflowtests/tests/test_streaming.py +0 -47
  122. pyworkflowtests/tests/test_utils.py +0 -210
  123. scipion_pyworkflow-3.10.5.dist-info/RECORD +0 -140
  124. scipion_pyworkflow-3.10.5.dist-info/dependency_links.txt +0 -1
  125. scipion_pyworkflow-3.10.5.dist-info/entry_points.txt +0 -5
  126. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info/licenses}/LICENSE.txt +0 -0
  127. {scipion_pyworkflow-3.10.5.dist-info → scipion_pyworkflow-3.11.0.dist-info}/top_level.txt +0 -0
pyworkflow/gui/gui.py DELETED
@@ -1,566 +0,0 @@
1
- # **************************************************************************
2
- # *
3
- # * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se) [1]
4
- # *
5
- # * [1] SciLifeLab, Stockholm University
6
- # *
7
- # * This program is free software: you can redistribute it and/or modify
8
- # * it under the terms of the GNU General Public License as published by
9
- # * the Free Software Foundation, either version 3 of the License, or
10
- # * (at your option) any later version.
11
- # *
12
- # * This program is distributed in the hope that it will be useful,
13
- # * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- # * GNU General Public License for more details.
16
- # *
17
- # * You should have received a copy of the GNU General Public License
18
- # * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
- # *
20
- # * All comments concerning this program package may be sent to the
21
- # * e-mail address 'scipion@cnb.csic.es'
22
- # *
23
- # **************************************************************************
24
- import os
25
- import tkinter as tk
26
- import tkinter.font as tkFont
27
- import queue
28
- from functools import partial
29
- from tkinter.ttk import Style
30
-
31
- import pyworkflow
32
- import pyworkflow as pw
33
- from pyworkflow.object import Object
34
- from pyworkflow.utils import Message, Icon
35
- from PIL import Image, ImageTk
36
-
37
- from pyworkflow.utils import SpriteImage, Sprite
38
- from .widgets import Button
39
- import numpy as np
40
-
41
- DEFAULT_WINDOW_CLASS = "Scipion Framework"
42
-
43
- # --------------- GUI CONFIGURATION parameters -----------------------
44
- # TODO: read font size and name from config file
45
- FONT_ITALIC = 'fontItalic'
46
- FONT_NORMAL = 'fontNormal'
47
- FONT_BOLD = 'fontBold'
48
- FONT_BIG = 'fontBig'
49
- # TextColor
50
- # cfgCitationTextColor = "dark olive green"
51
- # cfgLabelTextColor = "black"
52
- # cfgSectionTextColor = "blue4"
53
- # Background Color
54
- # cfgBgColor = "light grey"
55
- # cfgLabelBgColor = "white"
56
- # cfgHighlightBgColor = cfgBgColor
57
- #This with trigger the validation of the color falling back the firebrick if fails
58
- cfgButtonActiveBgColor = pw.Config.getActiveColor()
59
- cfgButtonFgColor = pw.Config.SCIPION_BG_COLOR
60
- cfgButtonActiveFgColor = pw.Config.SCIPION_BG_COLOR
61
- cfgButtonBgColor = pw.Config.SCIPION_MAIN_COLOR
62
- cfgEntryBgColor = "lemon chiffon"
63
- # cfgExpertLabelBgColor = "light salmon"
64
- # cfgSectionBgColor = cfgButtonBgColor
65
- # Color
66
- # cfgListSelectColor = "DeepSkyBlue4"
67
- # cfgBooleanSelectColor = "white"
68
- # cfgButtonSelectColor = "DeepSkyBlue2"
69
- # Dimensions limits
70
- # cfgMaxHeight = 650
71
- cfgMaxWidth = 800
72
- # cfgMaxFontSize = 14
73
- # cfgMinFontSize = 6
74
- cfgWrapLenght = cfgMaxWidth - 50
75
-
76
- # Style of treeviews where row height is variable based on the font size
77
- LIST_TREEVIEW = 'List.Treeview'
78
- BORDERLESS_TREEVIEW = 'Borderless.Treeview'
79
-
80
- image_cache = dict()
81
-
82
- class Config(Object):
83
- pass
84
-
85
-
86
- def saveConfig(filename):
87
- from pyworkflow.mapper import SqliteMapper
88
- from pyworkflow.object import String, Integer
89
-
90
- mapper = SqliteMapper(filename)
91
- o = Config()
92
- for k, v in globals().items():
93
- if k.startswith('cfg'):
94
- if type(v) is str:
95
- value = String(v)
96
- else:
97
- value = Integer(v)
98
- setattr(o, k, value)
99
- mapper.insert(o)
100
- mapper.commit()
101
-
102
-
103
- # --------------- FONT related variables and functions -----------------------
104
- def setFont(fontKey, update=False, **opts):
105
- """Register a tkFont and store it in a globals of this module
106
- this method should be called only after a tk.Tk() windows has been
107
- created."""
108
- if not hasFont(fontKey) or update:
109
- globals()[fontKey] = tkFont.Font(**opts)
110
-
111
- return globals()[fontKey]
112
-
113
-
114
- def hasFont(fontKey):
115
- return fontKey in globals()
116
-
117
-
118
- def aliasFont(fontAlias, fontKey):
119
- """Set a fontAlias as another alias name of fontKey"""
120
- g = globals()
121
- g[fontAlias] = g[fontKey]
122
-
123
-
124
- def getDefaultFont():
125
- return tk.font.nametofont("TkDefaultFont")
126
-
127
-
128
- def getNamedFont(fontName):
129
- return globals()[fontName]
130
-
131
-
132
- def getBigFont():
133
- return getNamedFont(FONT_BIG)
134
-
135
-
136
- def setCommonFonts(windows=None):
137
- """Set some predefined common fonts.
138
- Same conditions of setFont applies here."""
139
- f = setFont(FONT_NORMAL, family=pw.Config.SCIPION_FONT_NAME, size=pw.Config.SCIPION_FONT_SIZE)
140
- aliasFont('fontButton', FONT_NORMAL)
141
-
142
- # Set default font size
143
- default_font = getDefaultFont()
144
- default_font.configure(size=pw.Config.SCIPION_FONT_SIZE, family=pw.Config.SCIPION_FONT_NAME)
145
-
146
- fb = setFont(FONT_BOLD, family=pw.Config.SCIPION_FONT_NAME, size=pw.Config.SCIPION_FONT_SIZE,
147
- weight='bold')
148
- fi = setFont(FONT_ITALIC, family=pw.Config.SCIPION_FONT_NAME, size=pw.Config.SCIPION_FONT_SIZE,
149
- slant='italic')
150
-
151
- setFont(FONT_BIG, family=pw.Config.SCIPION_FONT_NAME, size=pw.Config.SCIPION_FONT_SIZE+8)
152
-
153
- if windows:
154
- windows.fontBig = tkFont.Font(size=pw.Config.SCIPION_FONT_SIZE + 2, family=pw.Config.SCIPION_FONT_NAME,
155
- weight='bold')
156
- windows.font = f
157
- windows.fontBold = fb
158
- windows.fontItalic = fi
159
-
160
- # This adds the default value for the listbox inside a combo box
161
- # Which seems to not react to default font!!
162
- windows.root.option_add("*TCombobox*Listbox*Font", default_font)
163
- windows.root.option_add("*TCombobox*Font", default_font)
164
-
165
-
166
- def changeFontSizeByDeltha(font, deltha, minSize=-999, maxSize=999):
167
- size = font['size']
168
- new_size = size + deltha
169
- if minSize <= new_size <= maxSize:
170
- font.configure(size=new_size)
171
-
172
-
173
- def changeFontSize(font, event, minSize=-999, maxSize=999):
174
- deltha = 2
175
- if event.char == '-':
176
- deltha = -2
177
- changeFontSizeByDeltha(font, deltha, minSize, maxSize)
178
-
179
-
180
- # --------------- IMAGE related variables and functions -----------------------
181
- def getImage(imageName, imgDict=None, tkImage=True, percent=100,
182
- maxheight=None):
183
- """ Search for the image in the RESOURCES path list. """
184
-
185
- global image_cache
186
-
187
- if imageName is None:
188
- return None
189
-
190
- # Rename .gif by .png. In Linux with pillow 9.2.0 gif transparency is broken so
191
- # we need to go for png. But in the past, in Macs png didn't work and made us go from png to gif
192
- # We are now providing the 2 formats, prioritising pngs. If png work in MAC and windows then gif
193
- # could be deleted. Otherwise, we may need to do this replacement based on the OS.
194
- # NOTE: "convert my-image.gif PNG32:my-image.png" has converted gifs to pngs RGBA (32 bits) it seems pillow
195
- # needs RGBA format to deal with transparencies.
196
-
197
- # Most protocols.conf uses .gif extension. We need to use png!.
198
-
199
- # ImageName could be either a file name (bookmark.gif) a full path image or a SpriteImage
200
- if isinstance(imageName, SpriteImage):
201
- fromSprite = True
202
- imageStr = str(imageName)
203
- else:
204
- fromSprite=False
205
- imageStr = imageName
206
-
207
- if not os.path.isabs(imageStr) and imageStr not in [Icon.WAITING]:
208
- imageStr = imageStr.replace(".gif", ".png")
209
-
210
- if imageStr in image_cache:
211
- return image_cache[imageStr]
212
-
213
- # If it is a definition of a sprite image
214
- if fromSprite:
215
- image = Sprite.getImage(imageName)
216
- else:
217
- imagePath = pw.findResource(imageStr) if not os.path.isabs(imageStr) else imageStr
218
- image = Image.open(imagePath) if imagePath else None
219
-
220
- if image:
221
- # For a future dark mode we might need to invert the image but it requires some extra work to make it look nice:
222
- # image = invertImage(image)
223
- w, h = image.size
224
- newSize = None
225
- if percent != 100: # Display image with other dimensions
226
- fp = float(percent) / 100.0
227
- newSize = int(fp * w), int(fp * h)
228
- elif maxheight and h > maxheight:
229
- newSize = int(w * float(maxheight) / h), maxheight
230
- if newSize:
231
- image.thumbnail(newSize, Image.LANCZOS)
232
- if tkImage:
233
- image = ImageTk.PhotoImage(image)
234
-
235
- image_cache[imageStr] = image
236
- return image
237
-
238
- def invertImage(img):
239
- # Creating a numpy array out of the image object
240
- img_arry = np.array(img)
241
-
242
- # Maximum intensity value of the color mode
243
- I_max = 255
244
-
245
- # Subtracting 255 (max value possible in a given image
246
- # channel) from each pixel values and storing the result
247
- img_arry = I_max - img_arry
248
-
249
- # Creating an image object from the resultant numpy array
250
- return Image.fromarray(img_arry)
251
- # ---------------- Windows geometry utilities -----------------------
252
- def getGeometry(win):
253
- """ Return the geometry information of the windows
254
- It will be a tuple (width, height, x, y)
255
- """
256
- return (win.winfo_reqwidth(), win.winfo_reqheight(),
257
- win.winfo_x(), win.winfo_y())
258
-
259
-
260
- def centerWindows(root, dim=None, refWindows=None):
261
- """Center a windows in the middle of the screen
262
- or in the middle of other windows(refWindows param)"""
263
- root.update_idletasks()
264
- if dim is None:
265
- gw, gh, _, _ = getGeometry(root)
266
- else:
267
- gw, gh = dim
268
- if refWindows:
269
- rw, rh, rx, ry = getGeometry(refWindows)
270
- x = rx + (rw - gw) / 2
271
- y = ry + (rh - gh) / 2
272
- else:
273
- w = root.winfo_screenwidth()
274
- h = root.winfo_screenheight()
275
- x = (w - gw) / 2
276
- y = (h - gh) / 2
277
-
278
- root.geometry("%dx%d+%d+%d" % (gw, gh, x, y))
279
-
280
-
281
- def configureWeigths(widget, row=0, column=0):
282
- """This function is a shortcut to a common
283
- used pair of calls: rowconfigure and columnconfigure
284
- for making childs widgets take the space available"""
285
- widget.columnconfigure(column, weight=1)
286
- widget.rowconfigure(row, weight=1)
287
-
288
-
289
- def defineStyle():
290
- """
291
- Defines some specific behaviour of the style.
292
- """
293
-
294
- # To specify the height of the rows based on the font size.
295
- # Should be centralized somewhere.
296
- style = Style()
297
- defaultFont = getDefaultFont()
298
-
299
- iconsSizePx = int((pyworkflow.Config.SCIPION_ICON_ZOOM/100 * 32))
300
- fontHeight = defaultFont.metrics()['linespace']
301
- rowheight = max(iconsSizePx, fontHeight)
302
-
303
- style.configure(LIST_TREEVIEW, rowheight=rowheight,
304
- background=pw.Config.SCIPION_BG_COLOR,
305
- fieldbackground=pw.Config.SCIPION_BG_COLOR)
306
- style.configure(LIST_TREEVIEW+".Heading", font=(defaultFont["family"],defaultFont["size"]))
307
-
308
- style.configure(BORDERLESS_TREEVIEW, rowheight=rowheight,
309
- background=pw.Config.SCIPION_BG_COLOR,
310
- fieldbackground=pw.Config.SCIPION_BG_COLOR,
311
- borderwidth=0, font=(defaultFont["family"],defaultFont["size"]))
312
-
313
-
314
- class Window:
315
- """Class to manage a Tk windows.
316
- It will encapsulate some basic creation and
317
- setup functions. """
318
- # To allow plugins to add their own menus
319
- _pluginMenus = dict()
320
-
321
- def __init__(self, title='', masterWindow=None, weight=True,
322
- minsize=(500, 300), icon=Icon.SCIPION_ICON, **kwargs):
323
- """Create a Tk window.
324
- title: string to use as title for the windows.
325
- master: if not provided, the windows create will be the principal one
326
- weight: if true, the first col and row will be configured with weight=1
327
- minsize: a minimum size for height and width
328
- icon: if not None, set the windows icon
329
- """
330
- # Init gui plugins
331
- pw.Config.getDomain()._discoverGUIPlugins()
332
-
333
- if masterWindow is None:
334
- # Unused?? Window._root = self
335
- self._images = {}
336
- # If a window which isn't the main Scipion window is generated from another main window, e. g. with Scipion
337
- # template after the refactoring of the kickoff, in which a dialog is launched and then a form, being it
338
- # called from the command line, so there's no Scipion main window. In that case, a tk.Tk() exists because if
339
- # a tk.TopLevel(), as the dialog, is directly launched, it automatically generates a main tk.Tk(). Thus,
340
- # after that first auto-tk.Tk(), another tk.Tk() was created here, and so the previous information was lost.
341
- # Solution proposed is to generate the root as an invisible window if it doesn't exist previously, and make
342
- # he first window generated a tk.Toplevel. After that, all steps executed later will go through the else
343
- # statement, being that way each new tk.Toplevel() correctly referenced.
344
- root = tk.Tk()
345
- root.withdraw() # Main window, invisible
346
- # invoke the button on the return key
347
- root.bind_class("Button", "<Key-Return>", lambda event: event.widget.invoke())
348
-
349
- self._class = kwargs.get("_class", DEFAULT_WINDOW_CLASS)
350
- self.root = tk.Toplevel(class_=self._class) # Toplevel of main window
351
- else:
352
- class_ = masterWindow._class if hasattr(masterWindow, "_class") else DEFAULT_WINDOW_CLASS
353
- self.root = tk.Toplevel(masterWindow.getRoot(), class_=class_)
354
- self.root.group(masterWindow.getRoot())
355
- self._images = masterWindow._images
356
-
357
- self.root.withdraw()
358
- self.root.title(title)
359
-
360
- if weight:
361
- configureWeigths(self.root)
362
- if minsize is not None:
363
- self.root.minsize(minsize[0], minsize[1])
364
-
365
- # Set the icon
366
- self._setIcon(icon)
367
-
368
- self.root.protocol("WM_DELETE_WINDOW", self._onClosing)
369
- self._w, self._h, self._x, self._y = 0, 0, 0, 0
370
- self.root.bind("<Configure>", self._configure)
371
- self.master = masterWindow
372
- setCommonFonts(self)
373
-
374
- if kwargs.get('enableQueue', False):
375
- self.queue = queue.Queue(maxsize=0)
376
- else:
377
- self.queue = None
378
-
379
- def _setIcon(self, icon):
380
-
381
- if icon is not None:
382
- try:
383
- path = pw.findResource(icon)
384
- # If path is None --> Icon not found
385
- if path is None:
386
- # By default, if icon is not found use default scipion one.
387
- path = pw.findResource(Icon.SCIPION_ICON)
388
-
389
- abspath = os.path.abspath(path)
390
-
391
- img = tk.Image("photo", file=abspath)
392
- self.root.tk.call('wm', 'iconphoto', self.root._w, img)
393
- except Exception as e:
394
- # Do nothing if icon could not be loaded
395
- pass
396
-
397
- def __processQueue(self): # called from main frame
398
- if not self.queue.empty():
399
- func = self.queue.get(block=False)
400
- # executes graphic interface function
401
- func()
402
- self._queueTimer = self.root.after(500, self.__processQueue)
403
-
404
- def enqueue(self, func):
405
- """ Put some function to be executed in the GUI main thread. """
406
- self.queue.put(func)
407
-
408
- def getRoot(self):
409
- return self.root
410
-
411
- def desiredDimensions(self):
412
- """Override this method to calculate desired dimensions."""
413
- return None
414
-
415
- def _configure(self, e):
416
- """ Filter event and call appropriate handler. """
417
- if self.root != e.widget:
418
- return
419
-
420
- _, _, x, y = getGeometry(self.root)
421
- w, h = e.width, e.height
422
-
423
- if w != self._w or h != self._h:
424
- self._w, self._h = w, h
425
- self.handleResize()
426
-
427
- if x != self._x or y != self._y:
428
- self._x, self._y = x, y
429
- self.handleMove()
430
-
431
- def handleResize(self):
432
- """Override this method to respond to resize events."""
433
- pass
434
-
435
- def handleMove(self):
436
- """Override this method to respond to move events."""
437
- pass
438
-
439
- def show(self, center=True, modal=False):
440
- """This function will enter in the Tk mainloop"""
441
- if center:
442
- if self.master is None:
443
- refw = None
444
- else:
445
- refw = self.master.getRoot()
446
- centerWindows(self.root, dim=self.desiredDimensions(),
447
- refWindows=refw)
448
-
449
- self.root.deiconify()
450
- self.root.focus_set()
451
- if self.queue is not None:
452
- self._queueTimer = self.root.after(1000, self.__processQueue)
453
-
454
- if modal:
455
- self.root.wait_window(self.root)
456
- else:
457
- self.root.mainloop()
458
-
459
- def close(self, e=None):
460
- self.root.destroy()
461
- # JMRT: For some reason when Tkinter has an exception
462
- # it does not exit the application as expected and
463
- # remains in the mainloop, so here we are forcing
464
- # to exit the whole system (only applies for the main window)
465
- if self.master is None:
466
- import sys
467
- sys.exit()
468
-
469
- def _onClosing(self):
470
- """Do some cleaning before closing."""
471
- if self.master is None:
472
- pass
473
- else:
474
- self.master.getRoot().focus_set()
475
- if self.queue is not None:
476
- self.root.after_cancel(self._queueTimer)
477
- self.close()
478
-
479
- def getImage(self, imgName, percent=100, maxheight=None):
480
- return getImage(imgName, percent=percent,
481
- maxheight=maxheight)
482
-
483
- def createMainMenu(self, menuConfig):
484
- """Create Main menu from the given MenuConfig object."""
485
- menu = tk.Menu(self.root, font=self.font)
486
- self._addMenuChilds(menu, menuConfig)
487
- self._addPluginMenus(menu)
488
- self.root.config(menu=menu)
489
- return menu
490
-
491
- def _addMenuChilds(self, menu, menuConfig):
492
- """Add entries of menuConfig in menu
493
- (using add_cascade or add_command for sub-menus and final options)."""
494
- # Helper function to create the main menu.
495
- for sub in menuConfig:
496
- menuLabel = sub.text
497
- if not menuLabel: # empty or None label means a separator
498
- menu.add_separator()
499
- elif len(sub) > 0: # sub-menu
500
- submenu = tk.Menu(self.root, tearoff=0, font=self.font)
501
- menu.add_cascade(label=menuLabel, menu=submenu)
502
- self._addMenuChilds(submenu, sub) # recursive filling
503
- else: # menu option
504
- # If there is an entry called "Browse files", when clicked it
505
- # will call the method onBrowseFiles() (it has to be defined!)
506
- def callback(name):
507
- """Return a callback function named "on<Name>"."""
508
- f = "on%s" % "".join(x.capitalize() for x in name.split())
509
- return lambda: getattr(self, f)()
510
-
511
- if sub.shortCut is not None:
512
- menuLabel += ' (' + sub.shortCut + ')'
513
-
514
- menu.add_command(label=menuLabel, compound=tk.LEFT,
515
- image=self.getImage(sub.icon),
516
- command=callback(name=sub.text))
517
-
518
- def _addPluginMenus(self, menu):
519
-
520
- if self._pluginMenus:
521
- submenu = tk.Menu(self.root, tearoff=0, font=self.font)
522
- menu.add_cascade(label="Others", menu=submenu)
523
-
524
- # For each plugin menu
525
- for label in self._pluginMenus:
526
- submenu.add_command(label=label, compound=tk.LEFT,
527
- image=self.getImage(self._pluginMenus.get(label)[1]),
528
- command=partial(self.plugin_callback, label))
529
-
530
- def plugin_callback(self, label):
531
- return self._pluginMenus.get(label)[0](self)
532
-
533
- @classmethod
534
- def registerPluginMenu(cls, label, callback, icon=None):
535
- # TODO: have a proper model instead of a tuple?
536
- cls._pluginMenus[label] = (callback, icon)
537
-
538
- def showError(self, msg, header="Error", exception=None):
539
- """Pops up a dialog with the error message
540
- :param msg Message to display
541
- :param header Title of the dialog
542
- :param exception: Optional. exception associated"""
543
- from .dialog import showError
544
- showError(header, msg, self.root, exception=exception)
545
-
546
- def showInfo(self, msg, header="Info"):
547
- from .dialog import showInfo
548
- showInfo(header, msg, self.root)
549
-
550
- def showWarning(self, msg, header='Warning'):
551
- from .dialog import showWarning
552
- showWarning(header, msg, self.root)
553
-
554
- def askYesNo(self, title, msg):
555
- from .dialog import askYesNo
556
- return askYesNo(title, msg, self.root)
557
-
558
- def createCloseButton(self, parent):
559
- """ Create a button for closing the window, setting
560
- the proper label and icon.
561
- """
562
- return Button(parent, Message.LABEL_BUTTON_CLOSE, Icon.ACTION_CLOSE,
563
- command=self.close)
564
-
565
- def configureWeights(self, row=0, column=0):
566
- configureWeigths(self.root, row, column)