BERATools 0.2.0__py3-none-any.whl → 0.2.1__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 (153) hide show
  1. beratools/__init__.py +1 -7
  2. beratools/core/algo_centerline.py +491 -351
  3. beratools/core/algo_common.py +497 -0
  4. beratools/core/algo_cost.py +192 -0
  5. beratools/core/{dijkstra_algorithm.py → algo_dijkstra.py} +503 -460
  6. beratools/core/algo_footprint_rel.py +577 -0
  7. beratools/core/algo_line_grouping.py +944 -0
  8. beratools/core/algo_merge_lines.py +214 -0
  9. beratools/core/algo_split_with_lines.py +304 -0
  10. beratools/core/algo_tiler.py +428 -0
  11. beratools/core/algo_vertex_optimization.py +469 -0
  12. beratools/core/constants.py +52 -86
  13. beratools/core/logger.py +76 -85
  14. beratools/core/tool_base.py +196 -133
  15. beratools/gui/__init__.py +11 -15
  16. beratools/gui/{beratools.json → assets/beratools.json} +2185 -2300
  17. beratools/gui/batch_processing_dlg.py +513 -463
  18. beratools/gui/bt_data.py +481 -487
  19. beratools/gui/bt_gui_main.py +710 -691
  20. beratools/gui/main.py +26 -0
  21. beratools/gui/map_window.py +162 -146
  22. beratools/gui/tool_widgets.py +725 -493
  23. beratools/tools/Beratools_r_script.r +1120 -1120
  24. beratools/tools/Ht_metrics.py +116 -116
  25. beratools/tools/__init__.py +7 -7
  26. beratools/tools/batch_processing.py +136 -132
  27. beratools/tools/canopy_threshold_relative.py +672 -670
  28. beratools/tools/canopycostraster.py +222 -222
  29. beratools/tools/centerline.py +136 -176
  30. beratools/tools/common.py +857 -885
  31. beratools/tools/fl_regen_csf.py +428 -428
  32. beratools/tools/forest_line_attributes.py +408 -408
  33. beratools/tools/line_footprint_absolute.py +213 -363
  34. beratools/tools/line_footprint_fixed.py +436 -282
  35. beratools/tools/line_footprint_functions.py +733 -720
  36. beratools/tools/line_footprint_relative.py +73 -64
  37. beratools/tools/line_grouping.py +45 -0
  38. beratools/tools/ln_relative_metrics.py +615 -615
  39. beratools/tools/r_cal_lpi_elai.r +24 -24
  40. beratools/tools/r_generate_pd_focalraster.r +100 -100
  41. beratools/tools/r_interface.py +79 -79
  42. beratools/tools/r_point_density.r +8 -8
  43. beratools/tools/rpy_chm2trees.py +86 -86
  44. beratools/tools/rpy_dsm_chm_by.py +81 -81
  45. beratools/tools/rpy_dtm_by.py +63 -63
  46. beratools/tools/rpy_find_cellsize.py +43 -43
  47. beratools/tools/rpy_gnd_csf.py +74 -74
  48. beratools/tools/rpy_hummock_hollow.py +85 -85
  49. beratools/tools/rpy_hummock_hollow_raster.py +71 -71
  50. beratools/tools/rpy_las_info.py +51 -51
  51. beratools/tools/rpy_laz2las.py +40 -40
  52. beratools/tools/rpy_lpi_elai_lascat.py +466 -466
  53. beratools/tools/rpy_normalized_lidar_by.py +56 -56
  54. beratools/tools/rpy_percent_above_dbh.py +80 -80
  55. beratools/tools/rpy_points2trees.py +88 -88
  56. beratools/tools/rpy_vegcoverage.py +94 -94
  57. beratools/tools/tiler.py +48 -206
  58. beratools/tools/tool_template.py +69 -54
  59. beratools/tools/vertex_optimization.py +61 -620
  60. beratools/tools/zonal_threshold.py +144 -144
  61. beratools-0.2.1.dist-info/METADATA +109 -0
  62. beratools-0.2.1.dist-info/RECORD +74 -0
  63. {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/WHEEL +1 -1
  64. {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/licenses/LICENSE +22 -22
  65. beratools/gui/cli.py +0 -18
  66. beratools/gui/gui.json +0 -8
  67. beratools/gui_tk/ASCII Banners.txt +0 -248
  68. beratools/gui_tk/__init__.py +0 -20
  69. beratools/gui_tk/beratools_main.py +0 -515
  70. beratools/gui_tk/bt_widgets.py +0 -442
  71. beratools/gui_tk/cli.py +0 -18
  72. beratools/gui_tk/img/BERALogo.png +0 -0
  73. beratools/gui_tk/img/closed.gif +0 -0
  74. beratools/gui_tk/img/closed.png +0 -0
  75. beratools/gui_tk/img/open.gif +0 -0
  76. beratools/gui_tk/img/open.png +0 -0
  77. beratools/gui_tk/img/tool.gif +0 -0
  78. beratools/gui_tk/img/tool.png +0 -0
  79. beratools/gui_tk/main.py +0 -14
  80. beratools/gui_tk/map_window.py +0 -144
  81. beratools/gui_tk/runner.py +0 -1481
  82. beratools/gui_tk/tooltip.py +0 -55
  83. beratools/third_party/pyqtlet2/__init__.py +0 -9
  84. beratools/third_party/pyqtlet2/leaflet/__init__.py +0 -26
  85. beratools/third_party/pyqtlet2/leaflet/control/__init__.py +0 -6
  86. beratools/third_party/pyqtlet2/leaflet/control/control.py +0 -59
  87. beratools/third_party/pyqtlet2/leaflet/control/draw.py +0 -52
  88. beratools/third_party/pyqtlet2/leaflet/control/layers.py +0 -20
  89. beratools/third_party/pyqtlet2/leaflet/core/Parser.py +0 -24
  90. beratools/third_party/pyqtlet2/leaflet/core/__init__.py +0 -2
  91. beratools/third_party/pyqtlet2/leaflet/core/evented.py +0 -180
  92. beratools/third_party/pyqtlet2/leaflet/layer/__init__.py +0 -5
  93. beratools/third_party/pyqtlet2/leaflet/layer/featuregroup.py +0 -34
  94. beratools/third_party/pyqtlet2/leaflet/layer/icon/__init__.py +0 -1
  95. beratools/third_party/pyqtlet2/leaflet/layer/icon/icon.py +0 -30
  96. beratools/third_party/pyqtlet2/leaflet/layer/imageoverlay.py +0 -18
  97. beratools/third_party/pyqtlet2/leaflet/layer/layer.py +0 -105
  98. beratools/third_party/pyqtlet2/leaflet/layer/layergroup.py +0 -45
  99. beratools/third_party/pyqtlet2/leaflet/layer/marker/__init__.py +0 -1
  100. beratools/third_party/pyqtlet2/leaflet/layer/marker/marker.py +0 -91
  101. beratools/third_party/pyqtlet2/leaflet/layer/tile/__init__.py +0 -2
  102. beratools/third_party/pyqtlet2/leaflet/layer/tile/gridlayer.py +0 -4
  103. beratools/third_party/pyqtlet2/leaflet/layer/tile/tilelayer.py +0 -16
  104. beratools/third_party/pyqtlet2/leaflet/layer/vector/__init__.py +0 -5
  105. beratools/third_party/pyqtlet2/leaflet/layer/vector/circle.py +0 -15
  106. beratools/third_party/pyqtlet2/leaflet/layer/vector/circlemarker.py +0 -18
  107. beratools/third_party/pyqtlet2/leaflet/layer/vector/path.py +0 -5
  108. beratools/third_party/pyqtlet2/leaflet/layer/vector/polygon.py +0 -14
  109. beratools/third_party/pyqtlet2/leaflet/layer/vector/polyline.py +0 -18
  110. beratools/third_party/pyqtlet2/leaflet/layer/vector/rectangle.py +0 -14
  111. beratools/third_party/pyqtlet2/leaflet/map/__init__.py +0 -1
  112. beratools/third_party/pyqtlet2/leaflet/map/map.py +0 -220
  113. beratools/third_party/pyqtlet2/mapwidget.py +0 -45
  114. beratools/third_party/pyqtlet2/web/custom.js +0 -43
  115. beratools/third_party/pyqtlet2/web/map.html +0 -23
  116. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers-2x.png +0 -0
  117. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers.png +0 -0
  118. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon-2x.png +0 -0
  119. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon.png +0 -0
  120. beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-shadow.png +0 -0
  121. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.css +0 -656
  122. beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.js +0 -6
  123. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.codeclimate.yml +0 -14
  124. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.editorconfig +0 -4
  125. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.gitattributes +0 -22
  126. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.travis.yml +0 -43
  127. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/LICENSE +0 -20
  128. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers-2x.png +0 -0
  129. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers.png +0 -0
  130. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon-2x.png +0 -0
  131. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon.png +0 -0
  132. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-shadow.png +0 -0
  133. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet-2x.png +0 -0
  134. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.png +0 -0
  135. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.svg +0 -156
  136. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.css +0 -10
  137. beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.js +0 -10
  138. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/LICENSE +0 -22
  139. beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/leaflet.rotatedMarker.js +0 -57
  140. beratools/tools/forest_line_ecosite.py +0 -216
  141. beratools/tools/lapis_all.py +0 -103
  142. beratools/tools/least_cost_path_from_chm.py +0 -152
  143. beratools-0.2.0.dist-info/METADATA +0 -63
  144. beratools-0.2.0.dist-info/RECORD +0 -142
  145. /beratools/gui/{img → assets}/BERALogo.png +0 -0
  146. /beratools/gui/{img → assets}/closed.gif +0 -0
  147. /beratools/gui/{img → assets}/closed.png +0 -0
  148. /beratools/{gui_tk → gui/assets}/gui.json +0 -0
  149. /beratools/gui/{img → assets}/open.gif +0 -0
  150. /beratools/gui/{img → assets}/open.png +0 -0
  151. /beratools/gui/{img → assets}/tool.gif +0 -0
  152. /beratools/gui/{img → assets}/tool.png +0 -0
  153. {beratools-0.2.0.dist-info → beratools-0.2.1.dist-info}/entry_points.txt +0 -0
@@ -1,1481 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # This script is part of the BERA Tools.
4
- # Authors: Richard Zeng
5
- # Created: 01/03/2023
6
- # License: MIT
7
-
8
- # import __future__
9
- import os
10
- import sys
11
-
12
- from os import path
13
- from sys import platform as _platform
14
-
15
- if sys.version_info[0] < 3:
16
- raise Exception("Must be using Python 3")
17
-
18
- import re # Added by Rachel for snake_to_camel function
19
- import json
20
- import platform
21
- import glob
22
-
23
- import threading
24
- import signal
25
- from time import sleep
26
-
27
- import tkinter as tk
28
- from tkinter import ttk
29
- from tkinter.scrolledtext import ScrolledText
30
- from tkinter import filedialog
31
- from tkinter.simpledialog import askinteger
32
- from tkinter import messagebox
33
- import webbrowser
34
- from pathlib import Path
35
-
36
- from beratools.gui_tk.beratools_main import BeraTools
37
- from beratools.tools.common import *
38
- from .tooltip import *
39
- from PIL import Image, ImageTk
40
-
41
- bt = BeraTools()
42
-
43
-
44
- class FileSelector(tk.Frame):
45
- def __init__(self, json_str, runner, master=None):
46
- # first make sure that the json data has the correct fields
47
- j = json.loads(json_str)
48
- self.name = j['name']
49
- self.description = j['description']
50
- self.flag = j['flag']
51
- self.parameter_type = j['parameter_type']
52
- self.file_type = ""
53
- if "ExistingFile" in self.parameter_type:
54
- self.file_type = j['parameter_type']['ExistingFile']
55
- elif "NewFile" in self.parameter_type:
56
- self.file_type = j['parameter_type']['NewFile']
57
- if "Directory" in self.parameter_type:
58
- self.file_type = j['parameter_type']['Directory']
59
- self.optional = j['optional']
60
- # default_value = j['default_value']
61
-
62
- self.runner = runner
63
-
64
- ttk.Frame.__init__(self, master, padding='0.02i')
65
- self.grid()
66
-
67
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
68
- self.label.grid(row=0, column=0, sticky=tk.W)
69
- self.label.columnconfigure(0, weight=1)
70
- CreateToolTip(self.label, self.description)
71
-
72
- if not self.optional:
73
- self.label['text'] = self.label['text'] + "*"
74
-
75
- fs_frame = ttk.Frame(self, padding='0.0i')
76
- self.value = tk.StringVar()
77
- self.entry = ttk.Entry(
78
- fs_frame, width=45, justify=tk.LEFT, textvariable=self.value)
79
- self.entry.grid(row=0, column=0, sticky=tk.NSEW)
80
- self.entry.columnconfigure(0, weight=1)
81
- # if default_value:
82
- # self.value.set(default_value)
83
-
84
- self.open_button = ttk.Button(fs_frame, width=4, text="...", command=self.select_file, padding='0.02i')
85
- self.open_button.grid(row=0, column=1, sticky=tk.E)
86
- self.open_button.columnconfigure(0, weight=1)
87
- fs_frame.grid(row=1, column=0, sticky=tk.NSEW)
88
- fs_frame.columnconfigure(0, weight=10)
89
- fs_frame.columnconfigure(1, weight=1)
90
- # self.pack(fill=tk.BOTH, expand=1)
91
- self.columnconfigure(0, weight=1)
92
- self.rowconfigure(0, weight=1)
93
- self.rowconfigure(1, weight=1)
94
-
95
- # Add the bindings
96
- if _platform == "darwin":
97
- self.entry.bind("<Command-Key-a>", self.select_all)
98
- else:
99
- self.entry.bind("<Control-Key-a>", self.select_all)
100
- self.entry.bind('<Control-c>', lambda _: 'break')
101
-
102
- def select_file(self):
103
- try:
104
- result = self.value.get()
105
- if "Directory" in self.parameter_type:
106
- result = filedialog.askdirectory(initialdir=self.runner.working_dir, title="Select directory")
107
- elif "ExistingFile" in self.parameter_type or "NewFile" in self.parameter_type:
108
- file_types = [('All files', '*.*')]
109
- if 'RasterAndVector' in self.file_type:
110
- file_types = [("Shapefiles", "*.shp"), ('Raster files', ('*.dep', '*.tif', '*.tiff',
111
- '*.bil', '*.flt', '*.sdat',
112
- '*.rdc', '*.asc', '*grd'))]
113
- elif 'Raster' in self.file_type:
114
- file_types = [('Tiff raster files', ('*.tif', '*.tiff')),
115
- ('Other raster files', ('*.dep', '*.bil', '*.flt',
116
- '*.sdat', '*.rdc', '*.asc', '*.grd'))]
117
- elif 'Lidar' in self.file_type:
118
- file_types = [("LiDAR files", ('*.las', '*.zlidar', '*.laz', '*.zip'))]
119
- elif 'Vector' in self.file_type:
120
- file_types = [("Shapefiles", "*.shp")]
121
- elif 'Text' in self.file_type:
122
- file_types = [("Text files", "*.txt"), ("all files", "*.*")]
123
- elif 'Csv' in self.file_type:
124
- file_types = [("CSC files", "*.csv"), ("all files", "*.*")]
125
- elif 'Dat' in self.file_type:
126
- file_types = [("Binary data files", "*.dat"), ("all files", "*.*")]
127
- elif 'Html' in self.file_type:
128
- file_types = [("HTML files", "*.html")]
129
- elif 'json' in self.file_type or 'JSON' in self.file_type:
130
- file_types = [("JSON files", "*.json"), ("CSV files", "*.csv")]
131
-
132
- # choose default extension to file name
133
- first_ext = file_types[0][1]
134
- if type(first_ext) is str:
135
- default_ext = first_ext
136
- elif type(first_ext) is tuple:
137
- default_ext = first_ext[0]
138
-
139
- default_ext = default_ext.replace('*', '')
140
-
141
- if "ExistingFile" in self.parameter_type:
142
- result = filedialog.askopenfilename(
143
- initialdir=self.runner.working_dir, title="Select "+self.name, filetypes=file_types)
144
- else:
145
- result = filedialog.asksaveasfilename(title="Save "+self.name, filetypes=file_types,
146
- defaultextension=default_ext)
147
-
148
- # elif "NewFile" in self.parameter_type:
149
- # result = filedialog.asksaveasfilename()
150
-
151
- if result != '':
152
- self.value.set(result)
153
- self.runner.working_dir = os.path.dirname(result)
154
-
155
- except:
156
- t = "file"
157
- if self.parameter_type == "Directory":
158
- t = "directory"
159
- messagebox.showinfo("Warning", "Could not find {}".format(t))
160
-
161
- def get_value(self):
162
- if self.value.get():
163
- v = self.value.get()
164
- # Do some quality assurance here.
165
- # Is there a directory included?
166
- if not path.dirname(v):
167
- v = path.join(self.runner.working_dir, v)
168
-
169
- # What about a file extension?
170
- ext = os.path.splitext(v)[-1].lower().strip()
171
- if not ext:
172
- ext = ""
173
- if 'RasterAndVector' in self.file_type:
174
- ext = '.tif'
175
- elif 'Raster' in self.file_type:
176
- ext = '.tif'
177
- elif 'Lidar' in self.file_type:
178
- ext = '.las'
179
- elif 'Vector' in self.file_type:
180
- ext = '.shp'
181
- elif 'Text' in self.file_type:
182
- ext = '.txt'
183
- elif 'Csv' in self.file_type:
184
- ext = '.csv'
185
- elif 'Html' in self.file_type:
186
- ext = '.html'
187
-
188
- v += ext
189
-
190
- v = path.normpath(v)
191
-
192
- # return "{}='{}'".format(self.flag, v)
193
- return self.flag, v
194
- else:
195
- t = "file"
196
- if self.parameter_type == "Directory":
197
- t = "directory"
198
- if not self.optional:
199
- # messagebox.showinfo("Error", "FileSelector: Unspecified {} parameter {}.".format(t, self.flag))
200
- print("FileSelector: Unspecified {} parameter {}.".format(t, self.flag))
201
-
202
- return self.flag, ''
203
-
204
- return None
205
-
206
- def select_all(self, event):
207
- self.entry.select_range(0, tk.END)
208
- return 'break'
209
-
210
-
211
- class FileOrFloat(tk.Frame):
212
- def __init__(self, json_str, runner, master=None):
213
- # first make sure that the json data has the correct fields
214
- j = json.loads(json_str)
215
- self.name = j['name']
216
- self.description = j['description']
217
- self.flag = j['flag']
218
- self.parameter_type = j['parameter_type']
219
- self.file_type = j['parameter_type']['ExistingFileOrFloat']
220
- self.optional = j['optional']
221
- # default_value = j['default_value']
222
-
223
- self.runner = runner
224
-
225
- ttk.Frame.__init__(self, master)
226
- self.grid()
227
- self['padding'] = '0.02i'
228
-
229
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
230
- self.label.grid(row=0, column=0, sticky=tk.W)
231
- self.label.columnconfigure(0, weight=1)
232
-
233
- if not self.optional:
234
- self.label['text'] = self.label['text'] + "*"
235
-
236
- fs_frame = ttk.Frame(self, padding='0.0i')
237
- self.value = tk.StringVar()
238
- self.entry = ttk.Entry(
239
- fs_frame, width=35, justify=tk.LEFT, textvariable=self.value)
240
- self.entry.grid(row=0, column=0, sticky=tk.NSEW)
241
- self.entry.columnconfigure(0, weight=1)
242
- # if default_value:
243
- # self.value.set(default_value)
244
-
245
- self.open_button = ttk.Button(
246
- fs_frame, width=4, text="...", command=self.select_file)
247
- self.open_button.grid(row=0, column=1, sticky=tk.E)
248
-
249
- self.label = ttk.Label(fs_frame, text='OR', justify=tk.LEFT)
250
- self.label.grid(row=0, column=2, sticky=tk.W)
251
-
252
- self.value2 = tk.StringVar()
253
- self.entry2 = ttk.Entry(
254
- fs_frame, width=10, justify=tk.LEFT, textvariable=self.value2)
255
- self.entry2.grid(row=0, column=3, sticky=tk.NSEW)
256
- self.entry2.columnconfigure(0, weight=1)
257
- self.entry2['justify'] = 'right'
258
-
259
- fs_frame.grid(row=1, column=0, sticky=tk.NSEW)
260
- fs_frame.columnconfigure(0, weight=10)
261
- fs_frame.columnconfigure(1, weight=1)
262
- # self.pack(fill=tk.BOTH, expand=1)
263
- self.columnconfigure(0, weight=1)
264
- self.rowconfigure(0, weight=1)
265
- self.rowconfigure(1, weight=1)
266
-
267
- # Add the bindings
268
- if _platform == "darwin":
269
- self.entry.bind("<Command-Key-a>", self.select_all)
270
- else:
271
- self.entry.bind("<Control-Key-a>", self.select_all)
272
- self.entry.bind('<Control-c>', lambda _: 'break')
273
-
274
- def select_file(self):
275
- try:
276
- result = self.value.get()
277
- file_types = [('All files', '*.*')]
278
- if 'RasterAndVector' in self.file_type:
279
- file_types = [("Shapefiles", "*.shp"), ('Raster files', ('*.dep', '*.tif', '*.tiff', '*.bil',
280
- '*.flt', '*.sdat', '*.rdc', '*.asc'))]
281
- elif 'Raster' in self.file_type:
282
- file_types = [('Raster files', ('*.dep', '*.tif', '*.tiff', '*.bil',
283
- '*.flt', '*.sdat', '*.rdc', '*.asc'))]
284
- elif 'Lidar' in self.file_type:
285
- file_types = [("LiDAR files", ('*.las', '*.zlidar', '*.laz', '*.zip'))]
286
- elif 'Vector' in self.file_type:
287
- file_types = [("Shapefiles", "*.shp")]
288
- elif 'Text' in self.file_type:
289
- file_types = [("Text files", "*.txt"), ("all files", "*.*")]
290
- elif 'Csv' in self.file_type:
291
- file_types = [("CSC files", "*.csv"), ("all files", "*.*")]
292
- elif 'Html' in self.file_type:
293
- file_types = [("HTML files", "*.html")]
294
-
295
- result = filedialog.askopenfilename(
296
- initialdir=self.runner.working_dir, title="Select file", filetypes=file_types)
297
-
298
- self.value.set(result)
299
-
300
- # update the working directory
301
- self.runner.working_dir = os.path.dirname(result)
302
-
303
- except:
304
- t = "file"
305
- if self.parameter_type == "Directory":
306
- t = "directory"
307
- messagebox.showinfo("Warning", "Could not find {}".format(t))
308
-
309
- def RepresentsFloat(self, s):
310
- try:
311
- float(s)
312
- return True
313
- except ValueError:
314
- return False
315
-
316
- def get_value(self):
317
- if self.value.get():
318
- v = self.value.get()
319
- # Do some quality assurance here.
320
- # Is there a directory included?
321
- if not path.dirname(v):
322
- v = path.join(self.runner.working_dir, v)
323
-
324
- # What about a file extension?
325
- ext = os.path.splitext(v)[-1].lower()
326
- if not ext:
327
- ext = ""
328
- if 'RasterAndVector' in self.file_type:
329
- ext = '.tif'
330
- elif 'Raster' in self.file_type:
331
- ext = '.tif'
332
- elif 'Lidar' in self.file_type:
333
- ext = '.las'
334
- elif 'Vector' in self.file_type:
335
- ext = '.shp'
336
- elif 'Text' in self.file_type:
337
- ext = '.txt'
338
- elif 'Csv' in self.file_type:
339
- ext = '.csv'
340
- elif 'Html' in self.file_type:
341
- ext = '.html'
342
-
343
- v = v + ext
344
-
345
- v = path.normpath(v)
346
- return self.flag, v
347
- elif self.value2.get():
348
- v = self.value2.get()
349
- if self.RepresentsFloat(v):
350
- return "{}={}".format(self.flag, v)
351
- else:
352
- messagebox.showinfo(
353
- "Error", "Error converting parameter {} to type Float.".format(self.flag))
354
- else:
355
- if not self.optional:
356
- messagebox.showinfo(
357
- "Error", "Unspecified file/numeric parameter {}.".format(self.flag))
358
-
359
- return None
360
-
361
- def select_all(self, event):
362
- self.entry.select_range(0, tk.END)
363
- return 'break'
364
-
365
-
366
- class MultifileSelector(tk.Frame):
367
- def __init__(self, json_str, runner, master=None):
368
- # first make sure that the json data has the correct fields
369
- j = json.loads(json_str)
370
- self.name = j['name']
371
- self.description = j['description']
372
- self.flag = j['flag']
373
- self.parameter_type = j['parameter_type']
374
- self.file_type = ""
375
- self.file_type = j['parameter_type']['FileList']
376
- self.optional = j['optional']
377
- default_value = j['default_value']
378
-
379
- self.runner = runner
380
-
381
- ttk.Frame.__init__(self, master)
382
- self.grid()
383
- self['padding'] = '0.05i'
384
-
385
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
386
- self.label.grid(row=0, column=0, sticky=tk.W)
387
- self.label.columnconfigure(0, weight=1)
388
-
389
- if not self.optional:
390
- self.label['text'] = self.label['text'] + "*"
391
-
392
- fs_frame = ttk.Frame(self, padding='0.0i')
393
- # , variable=self.value)
394
- self.opt = tk.Listbox(fs_frame, width=44, height=4)
395
- self.opt.grid(row=0, column=0, sticky=tk.NSEW)
396
- s = ttk.Scrollbar(fs_frame, orient=tk.VERTICAL, command=self.opt.yview)
397
- s.grid(row=0, column=1, sticky=(tk.N, tk.S))
398
- self.opt['yscrollcommand'] = s.set
399
-
400
- btn_frame = ttk.Frame(fs_frame, padding='0.0i')
401
- self.open_button = ttk.Button(
402
- btn_frame, width=4, text="...", command=self.select_file)
403
- self.open_button.grid(row=0, column=0, sticky=tk.NE)
404
- self.open_button.columnconfigure(0, weight=1)
405
- self.open_button.rowconfigure(0, weight=1)
406
-
407
- self.delete_button = ttk.Button(
408
- btn_frame, width=4, text="del", command=self.delete_entry)
409
- self.delete_button.grid(row=1, column=0, sticky=tk.NE)
410
- self.delete_button.columnconfigure(0, weight=1)
411
- self.delete_button.rowconfigure(1, weight=1)
412
-
413
- btn_frame.grid(row=0, column=2, sticky=tk.NE)
414
-
415
- fs_frame.grid(row=1, column=0, sticky=tk.NSEW)
416
- fs_frame.columnconfigure(0, weight=10)
417
- fs_frame.columnconfigure(1, weight=1)
418
- fs_frame.columnconfigure(2, weight=1)
419
- # self.pack(fill=tk.BOTH, expand=1)
420
- self.columnconfigure(0, weight=1)
421
- self.rowconfigure(0, weight=1)
422
- self.rowconfigure(1, weight=1)
423
-
424
- def select_file(self):
425
- try:
426
- # result = self.value.get()
427
- init_dir = self.runner.working_dir
428
- file_types = [('All files', '*.*')]
429
- if 'RasterAndVector' in self.file_type:
430
- file_types = [("Shapefiles", "*.shp"), ('Raster files', ('*.dep', '*.tif', '*.tiff', '*.bil',
431
- '*.flt', '*.sdat', '*.rdc', '*.asc'))]
432
- elif 'Raster' in self.file_type:
433
- file_types = [('Raster files', ('*.dep', '*.tif', '*.tiff', '*.bil',
434
- '*.flt', '*.sdat', '*.rdc', '*.asc'))]
435
- elif 'Lidar' in self.file_type:
436
- file_types = [("LiDAR files", ('*.las', '*.zlidar', '*.laz', '*.zip'))]
437
- elif 'Vector' in self.file_type:
438
- file_types = [("Shapefiles", "*.shp")]
439
- elif 'Text' in self.file_type:
440
- file_types = [("Text files", "*.txt"), ("all files", "*.*")]
441
- elif 'Csv' in self.file_type:
442
- file_types = [("CSC files", "*.csv"), ("all files", "*.*")]
443
- elif 'Html' in self.file_type:
444
- file_types = [("HTML files", "*.html")]
445
-
446
- result = filedialog.askopenfilenames(
447
- initialdir=init_dir, title="Select files", filetypes=file_types)
448
- if result:
449
- for v in result:
450
- self.opt.insert(tk.END, v)
451
-
452
- # update the working directory
453
- self.runner.working_dir = os.path.dirname(result[0])
454
- except:
455
- messagebox.showinfo("Warning", "Could not find file")
456
-
457
- def delete_entry(self):
458
- self.opt.delete(tk.ANCHOR)
459
-
460
- def get_value(self):
461
- try:
462
- l = self.opt.get(0, tk.END)
463
- if l:
464
- s = ""
465
- for i in range(0, len(l)):
466
- v = l[i]
467
- if not path.dirname(v):
468
- v = path.join(self.runner.working_dir, v)
469
- v = path.normpath(v)
470
- if i < len(l) - 1:
471
- s += "{};".format(v)
472
- else:
473
- s += "{}".format(v)
474
-
475
- # return "{}='{}'".format(self.flag, s)
476
- return self.flag, v
477
- else:
478
- if not self.optional:
479
- print("Error", "Unspecified non-optional parameter {}.".format(self.flag))
480
- except:
481
- messagebox.showinfo(
482
- "Error", "Error formatting files for parameter {}".format(self.flag))
483
-
484
- return None
485
-
486
-
487
- class BooleanInput(tk.Frame):
488
- def __init__(self, json_str, master=None):
489
- # first make sure that the json data has the correct fields
490
- j = json.loads(json_str)
491
- self.name = j['name']
492
- self.description = j['description']
493
- self.flag = j['flag']
494
- self.parameter_type = j['parameter_type']
495
- # just for quality control. BooleanInputs are always optional.
496
- self.optional = True
497
- # default_value = j['default_value']
498
-
499
- ttk.Frame.__init__(self, master)
500
- self.grid()
501
- self['padding'] = '0.05i'
502
-
503
- frame = ttk.Frame(self, padding='0.0i')
504
-
505
- self.value = tk.IntVar()
506
- c = ttk.Checkbutton(frame, text=self.name,
507
- width=55, variable=self.value)
508
- c.grid(row=0, column=0, sticky=tk.W)
509
- CreateToolTip(c, self.description)
510
-
511
- # set the default value
512
- # if j['default_value'] is not None and j['default_value'] != 'false':
513
- # self.value.set(1)
514
- # else:
515
- # self.value.set(0)
516
-
517
- frame.grid(row=1, column=0, sticky=tk.W)
518
- frame.columnconfigure(0, weight=1)
519
-
520
- # self.pack(fill=tk.BOTH, expand=1)
521
- self.columnconfigure(0, weight=1)
522
- self.rowconfigure(0, weight=1)
523
-
524
- def get_value(self):
525
- value = self.value.get()
526
- if value == 1:
527
- return self.flag, True
528
- else:
529
- return self.flag, False
530
-
531
-
532
- class OptionsInput(tk.Frame):
533
- def __init__(self, json_str, master=None):
534
-
535
- # first make sure that the json data has the correct fields
536
- j = json.loads(json_str)
537
- self.name = j['name']
538
- self.description = j['description']
539
- self.flag = j['flag']
540
- self.parameter_type = j['parameter_type']
541
- self.optional = j['optional']
542
- self.data_type = j['data_type']
543
- if 'saved_value' in j.keys():
544
- default_value = str(j['saved_value'])
545
- else:
546
- default_value = str(j['default_value'])
547
- self.value = default_value # initialize in event of no default and no selection
548
-
549
- ttk.Frame.__init__(self, master)
550
- self.grid()
551
- self['padding'] = '0.02i'
552
-
553
- frame = ttk.Frame(self, padding='0.0i')
554
-
555
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
556
- self.label.grid(row=0, column=0, sticky=tk.W)
557
- self.label.columnconfigure(0, weight=1)
558
-
559
- frame2 = ttk.Frame(frame, padding='0.0i')
560
- opt = ttk.Combobox(frame2, width=40)
561
- opt.grid(row=0, column=0, sticky=tk.NSEW)
562
-
563
- i = 1
564
- default_index = -1
565
- option_list = j['parameter_type']['OptionList']
566
- if option_list:
567
- option_list = [str(item) for item in option_list] # convert to strings
568
- values = ()
569
- for v in option_list:
570
- values += (v,)
571
- if v == str(default_value):
572
- default_index = i - 1
573
- i = i + 1
574
-
575
- opt['values'] = values
576
-
577
- opt.bind("<<ComboboxSelected>>", self.select)
578
- if default_index >= 0:
579
- opt.current(default_index)
580
- opt.event_generate("<<ComboboxSelected>>")
581
-
582
- frame2.grid(row=0, column=0, sticky=tk.W)
583
- frame.grid(row=1, column=0, sticky=tk.W)
584
- frame.columnconfigure(0, weight=1)
585
-
586
- self.columnconfigure(0, weight=1)
587
- self.rowconfigure(0, weight=1)
588
-
589
- opt.bind('<Control-c>', lambda _: 'break')
590
-
591
- def get_value(self):
592
- if self.value is not None:
593
- value = self.value
594
- if 'Float' == self.data_type:
595
- value = float(self.value)
596
- elif 'Integer' == self.data_type:
597
- value = int(self.value)
598
- elif 'Boolean' == self.data_type:
599
- if type(value) is str:
600
- value = True if self.value == 'True' else False
601
-
602
- return self.flag, value
603
- else:
604
- if not self.optional:
605
- print("Error", "Unspecified non-optional parameter {}.".format(self.flag))
606
-
607
- return None
608
-
609
- def select(self, event):
610
- widget = event.widget
611
- self.value = widget.get() # selection[0])
612
-
613
-
614
- class DataInput(tk.Frame):
615
- def __init__(self, json_str, master=None):
616
-
617
- # first make sure that the json data has the correct fields
618
- j = json.loads(json_str)
619
- self.name = j['name']
620
- self.description = j['description']
621
- self.flag = j['flag']
622
- self.parameter_type = j['parameter_type']
623
- self.optional = j['optional']
624
- # default_value = j['default_value']
625
-
626
- ttk.Frame.__init__(self, master)
627
- self.grid()
628
- self['padding'] = '0.1i'
629
-
630
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
631
- self.label.grid(row=0, column=0, sticky=tk.W)
632
- self.label.columnconfigure(0, weight=1)
633
- CreateToolTip(self.label, self.description)
634
-
635
- self.value = tk.StringVar()
636
- # if default_value:
637
- # self.value.set(default_value)
638
- # else:
639
- # self.value.set("")
640
-
641
- self.entry = ttk.Entry(self, justify=tk.LEFT, textvariable=self.value)
642
- self.entry.grid(row=0, column=1, sticky=tk.NSEW)
643
- self.entry.columnconfigure(1, weight=10)
644
-
645
- if not self.optional:
646
- self.label['text'] = self.label['text'] + "*"
647
-
648
- if ("Integer" in self.parameter_type or
649
- "Float" in self.parameter_type or
650
- "Double" in self.parameter_type):
651
- self.entry['justify'] = 'right'
652
-
653
- # Add the bindings
654
- if _platform == "darwin":
655
- self.entry.bind("<Command-Key-a>", self.select_all)
656
- else:
657
- self.entry.bind("<Control-Key-a>", self.select_all)
658
- self.entry.bind('<Control-c>', lambda _: 'break')
659
-
660
- # self.pack(fill=tk.BOTH, expand=1)
661
- self.columnconfigure(0, weight=1)
662
- self.columnconfigure(1, weight=10)
663
- self.rowconfigure(0, weight=1)
664
-
665
- def RepresentsInt(self, s):
666
- try:
667
- int(s)
668
- return True
669
- except ValueError:
670
- return False
671
-
672
- def RepresentsFloat(self, s):
673
- try:
674
- float(s)
675
- return True
676
- except ValueError:
677
- return False
678
-
679
- def get_value(self):
680
- v = self.value.get()
681
- if v:
682
- if "Integer" in self.parameter_type:
683
- if self.RepresentsInt(self.value.get()):
684
- # return "{}={}".format(self.flag, self.value.get())
685
- return self.flag, int(self.value.get())
686
- else:
687
- messagebox.showinfo("Error", "Error converting parameter {} to type Integer.".format(self.flag))
688
- elif "Float" in self.parameter_type:
689
- if self.RepresentsFloat(self.value.get()):
690
- # return "{}={}".format(self.flag, self.value.get())
691
- return self.flag, float(self.value.get())
692
- else:
693
- messagebox.showinfo("Error", "Error converting parameter {} to type Float.".format(self.flag))
694
- elif "Double" in self.parameter_type:
695
- if self.RepresentsFloat(self.value.get()):
696
- # return "{}={}".format(self.flag, self.value.get())
697
- return self.flag, float(self.value.get())
698
- else:
699
- messagebox.showinfo("Error", "Error converting parameter {} to type Double.".format(self.flag))
700
- else: # String or StringOrNumber types
701
- # return "{}='{}'".format(self.flag, self.value.get())
702
- return self.flag, self.value.get()
703
- else:
704
- if not self.optional:
705
- messagebox.showinfo("Error", "Unspecified non-optional parameter {}.".format(self.flag))
706
-
707
- return None
708
-
709
- def select_all(self, event):
710
- self.entry.select_range(0, tk.END)
711
- return 'break'
712
-
713
-
714
- class MainGui(tk.Frame):
715
- def __init__(self, tool_name=None, master=None):
716
- self.descriptionList = None
717
- self.search_string = None
718
- self.toolbox_open = None
719
- self.toolbox_name = None
720
- self.tools_list = None
721
- self.sorted_tools = None
722
- self.lower_toolboxes = None
723
- self.upper_toolboxes = None
724
- self.file_menu = None
725
- self.closed_toolbox_icon = None
726
- self.open_toolbox_icon = None
727
- self.tool_icon = None
728
- self.tool_tree = None
729
- self.progress = None
730
- self.progress_var = None
731
- self.progress_label = None
732
- self.help_button = None
733
- self.cancel_button = None
734
- self.run_button = None
735
- self.arg_scroll_frame = None
736
- self.view_code_button = None
737
- self.show_advanced_button = None
738
- self.current_tool_lbl = None
739
- self.current_tool_frame = None
740
- self.search_scroll = None
741
- self.search_results_listbox = None
742
- self.search_bar = None
743
- self.search_label = None
744
- self.search_frame = None
745
- self.search_text = None
746
- self.search_list = None
747
- self.tools_frame = None
748
- self.toolbox_list = None
749
- self.tools_and_toolboxes = None
750
- self.out_text = None
751
- self.search_tool_selected = None
752
- self.reset_button = None
753
- self.current_tool_api = None
754
-
755
- if platform.system() == 'Windows':
756
- self.ext = '.exe'
757
- else:
758
- self.ext = ''
759
-
760
- exe_name = "BERA_tools{}".format(self.ext)
761
-
762
- # BERA tool list
763
- self.bera_tools = bt.bera_tools
764
- self.tools_list = bt.tools_list
765
- self.sorted_tools = bt.sorted_tools
766
- self.toolbox_list = bt.toolbox_list
767
- self.upper_toolboxes = bt.upper_toolboxes
768
- self.lower_toolboxes = bt.lower_toolboxes
769
-
770
- self.exe_path = path.dirname(path.abspath(__file__))
771
- os.chdir(self.exe_path)
772
- for filename in glob.iglob('**/*', recursive=True):
773
- if filename.endswith(exe_name):
774
- self.exe_path = path.dirname(path.abspath(filename))
775
- break
776
-
777
- bt.set_bera_dir(self.exe_path)
778
-
779
- ttk.Frame.__init__(self, master)
780
- self.script_dir = os.path.dirname(os.path.realpath(__file__))
781
- self.grid()
782
- self.tool_name = tool_name
783
- self.master.title("BERA Tools")
784
- if _platform == "darwin":
785
- os.system('''/usr/bin/osascript -e 'tell app "Finder" to set front-most of process "Python" to true' ''')
786
- self.create_widgets()
787
- self.working_dir = bt.get_working_dir() # str(Path.home())
788
-
789
- def root(self):
790
- return self._root()
791
-
792
- def create_widgets(self):
793
- #########################################################
794
- # Overall/Top level Frame
795
- #
796
- # define left-side frame (left_frame) and right-side frame (right_frame)
797
- self.pack(fill="both", expand=True)
798
- self.rowconfigure(0, weight=1)
799
- self.columnconfigure(0, weight=1)
800
- self.columnconfigure(1, weight=1)
801
-
802
- left_frame = ttk.Frame(self, padding='0.1i')
803
- right_frame = ttk.Frame(self, padding='0.1i')
804
-
805
- # set-up layout
806
- left_frame.grid(row=0, column=0, sticky=tk.NSEW)
807
- right_frame.grid(row=0, column=1, sticky=tk.NSEW)
808
-
809
- left_frame.rowconfigure(0, weight=10)
810
- left_frame.rowconfigure(1, weight=1)
811
- left_frame.columnconfigure(0, weight=1)
812
-
813
- # right_frame.rowconfigure(0, weight=1)
814
- # right_frame.rowconfigure(1, weight=1)
815
- # right_frame.rowconfigure(2, weight=1)
816
- right_frame.rowconfigure(3, weight=1)
817
- # right_frame.rowconfigure(4, weight=1)
818
-
819
- right_frame.columnconfigure(0, weight=1)
820
-
821
- # Icons to be used in tool treeview
822
- self.tool_icon = tk.PhotoImage(file=self.script_dir + '//img//tool.gif')
823
- self.open_toolbox_icon = tk.PhotoImage(file=self.script_dir + '//img//open.gif')
824
- self.closed_toolbox_icon = tk.PhotoImage(file=self.script_dir + '//img//closed.gif')
825
-
826
- #########################################################
827
- # Toolboxes Frame
828
- # define tools_frame and tool_tree
829
- self.tools_frame = ttk.LabelFrame(left_frame, text="{} Available Tools".format(len(self.tools_list)),
830
- padding='0.1i')
831
- self.tool_tree = ttk.Treeview(self.tools_frame, height=21)
832
-
833
- # Set up layout
834
- self.tool_tree.grid(row=0, column=0, sticky=tk.NSEW)
835
- self.tool_tree.column("#0", width=280) # Set width so all tools are readable within the frame
836
- self.tools_frame.grid(row=0, column=0, sticky=tk.NSEW)
837
- self.tools_frame.columnconfigure(0, weight=10)
838
- self.tools_frame.columnconfigure(1, weight=1)
839
- self.tools_frame.rowconfigure(0, weight=10)
840
- self.tools_frame.rowconfigure(1, weight=1)
841
-
842
- # Add toolboxes and tools to treeview
843
- self.add_tools_to_treeview()
844
- self.update_selected_bera_tool()
845
-
846
- # bind tools in treeview to self.tree_update_tool_help function
847
- # and toolboxes to self.update_toolbox_icon function
848
- # TODO: BERA tool help
849
- self.tool_tree.tag_bind('tool', "<<TreeviewSelect>>", self.tree_update_tool_help)
850
- self.tool_tree.tag_bind('toolbox', "<<TreeviewSelect>>", self.update_toolbox_icon)
851
-
852
- # Add vertical scrollbar to treeview frame
853
- s = ttk.Scrollbar(self.tools_frame, orient=tk.VERTICAL, command=self.tool_tree.yview)
854
- s.grid(row=0, column=1, sticky=tk.NS)
855
- self.tool_tree['yscrollcommand'] = s.set
856
-
857
- #########################################################
858
- # Search Bar
859
- # create variables for search results and search input
860
- self.search_list = []
861
- self.search_text = tk.StringVar()
862
-
863
- # Create the elements of the search frame
864
- self.search_frame = ttk.LabelFrame(left_frame, padding='0.1i',
865
- text="{} Tools Found".format(len(self.search_list)))
866
- self.search_label = ttk.Label(self.search_frame, text="Search: ")
867
- self.search_bar = ttk.Entry(self.search_frame, width=30, textvariable=self.search_text)
868
- self.search_results_listbox = tk.Listbox(self.search_frame, height=11, exportselection=False)
869
- self.search_scroll = ttk.Scrollbar(self.search_frame, orient=tk.VERTICAL,
870
- command=self.search_results_listbox.yview)
871
- self.search_results_listbox['yscrollcommand'] = self.search_scroll.set
872
-
873
- # Add bindings
874
- self.search_results_listbox.bind("<<ListboxSelect>>", self.update_search_tool_info)
875
- self.search_bar.bind('<Return>', self.update_search)
876
-
877
- # Define layout of the frame
878
- self.search_frame.grid(row=1, column=0, sticky=tk.NSEW)
879
- self.search_label.grid(row=0, column=0, sticky=tk.NW)
880
- self.search_bar.grid(row=0, column=1, sticky=tk.NE)
881
- self.search_results_listbox.grid(row=1, column=0, columnspan=2, sticky=tk.NSEW, pady=5)
882
- self.search_scroll.grid(row=1, column=2, sticky=tk.NS)
883
-
884
- # Configure rows and columns of the frame
885
- self.search_frame.columnconfigure(0, weight=1)
886
- self.search_frame.columnconfigure(1, weight=10)
887
- self.search_frame.columnconfigure(1, weight=1)
888
- self.search_frame.rowconfigure(0, weight=1)
889
- self.search_frame.rowconfigure(1, weight=10)
890
-
891
- # add recent tools to search list
892
- self.add_recent_tool_to_search()
893
-
894
- #########################################################
895
- # Current Tool Frame
896
- # Create the elements of the current tool frame
897
- self.current_tool_frame = ttk.Frame(right_frame, padding='0.01i')
898
- self.current_tool_lbl = ttk.Label(self.current_tool_frame, text="Current Tool: {}".format(self.tool_name),
899
- justify=tk.LEFT, font=("Helvetica", 11, "bold"))
900
- self.show_advanced_button = ttk.Button(self.current_tool_frame, text="Show Advanced Options",
901
- width=24, command=self.show_advanced)
902
- CreateToolTip(self.show_advanced_button, 'Show/hide tool advanced options')
903
- self.view_code_button = ttk.Button(self.current_tool_frame, text="View Code", width=12, command=self.view_code)
904
- CreateToolTip(self.view_code_button, 'Go to the tool code web page')
905
-
906
- # Define layout of the frame
907
- self.show_advanced_button.grid(row=0, column=1, sticky=tk.E)
908
- self.view_code_button.grid(row=0, column=2, sticky=tk.E)
909
- self.current_tool_lbl.grid(row=0, column=0, sticky=tk.W)
910
- self.current_tool_frame.grid(row=0, column=0, columnspan=2, sticky=tk.NSEW)
911
-
912
- # Configure rows and columns of the frame
913
- self.current_tool_frame.columnconfigure(0, weight=1)
914
- self.current_tool_frame.columnconfigure(1, weight=1)
915
-
916
- self.arg_scroll_frame = ttk.Frame(right_frame, padding='0.0i')
917
- self.arg_scroll_frame.grid(row=1, column=0, sticky=tk.NSEW)
918
- self.arg_scroll_frame.columnconfigure(0, weight=1)
919
-
920
- #########################################################
921
- # Buttons Frame
922
- #
923
- # Create the elements of the buttons frame
924
- buttons_frame = ttk.Frame(right_frame, padding='0.1i')
925
- self.mpc_label = ttk.Label(buttons_frame, text="Use CPU Cores:")
926
- max_cores = bt.get_max_cpu_cores()
927
- if bt.get_max_procs() <= 0:
928
- bt.set_max_procs(max_cores)
929
- self.mpc_scale = tk.Scale(buttons_frame, from_=1, to=max_cores, length=180,
930
- orient='horizontal', command=self.update_procs)
931
- # multiprocessing core slide
932
- CreateToolTip(self.mpc_label, 'The number of CPU cores to be used in parallel processes')
933
- self.mpc_scale.set(bt.get_max_procs())
934
-
935
- self.reset_button = ttk.Button(buttons_frame, text="Clear Arguments", width=16, command=self.reset_tool)
936
- self.run_button = ttk.Button(buttons_frame, text="Run", width=8, command=lambda: self.start_run_tool_thread())
937
- self.cancel_button = ttk.Button(buttons_frame, text="Cancel", width=8, command=self.cancel_operation)
938
- self.help_button = ttk.Button(buttons_frame, text="Help", width=8, command=self.tool_help_button)
939
- CreateToolTip(self.reset_button, 'Clear all tool arguments and set to default')
940
- CreateToolTip(self.cancel_button, 'Cancel tool operation')
941
- CreateToolTip(self.run_button, 'Run the tool')
942
- CreateToolTip(self.help_button, 'Go to the tool help web page')
943
-
944
- # Define layout of the frame
945
- self.reset_button.grid(row=0, column=0, padx=4)
946
- self.mpc_label.grid(row=0, column=1, padx=4)
947
- self.mpc_scale.grid(row=0, column=2, padx=5, ipady=10)
948
- self.run_button.grid(row=0, column=3)
949
- self.cancel_button.grid(row=0, column=4)
950
- self.help_button.grid(row=0, column=5)
951
- buttons_frame.grid(row=2, column=0, columnspan=2, sticky=tk.E)
952
- buttons_frame.rowconfigure(2, weight=1)
953
-
954
- #########################################################
955
- # Output Frame
956
- # Create the elements of the output frame
957
- output_frame = ttk.Frame(right_frame)
958
- out_label = ttk.Label(output_frame, text="Output:", justify=tk.LEFT)
959
- self.out_text = ScrolledText(output_frame, width=63, height=15, wrap=tk.NONE, padx=7, pady=7, exportselection=0)
960
- output_scrollbar = ttk.Scrollbar(output_frame, orient=tk.HORIZONTAL, command=self.out_text.xview)
961
- self.out_text['xscrollcommand'] = output_scrollbar.set
962
-
963
- # color theme for text output
964
- self.out_text.tag_config('missing', foreground='red')
965
- # Retrieve and insert the text for the current tool
966
-
967
- # BERA Tools help text
968
- k = bt.get_bera_tool_info(self.tool_name)
969
- if k:
970
- self.out_text.insert(tk.END, k)
971
-
972
- # Define layout of the frame
973
- output_frame.grid(row=3, column=0, columnspan=2, sticky=tk.NSEW) # inside right_frame
974
-
975
- out_label.grid(row=0, column=0, sticky=tk.NW)
976
- self.out_text.grid(row=1, column=0, sticky=tk.NSEW)
977
- output_scrollbar.grid(row=2, column=0, sticky=tk.EW)
978
-
979
- output_frame.rowconfigure(1, weight=1)
980
- output_frame.columnconfigure(0, weight=1)
981
-
982
- # Add the binding
983
- if _platform == "darwin":
984
- self.out_text.bind("<Command-Key-a>", self.select_all)
985
- else:
986
- self.out_text.bind("<Control-Key-a>", self.select_all)
987
-
988
- # disable copy, paste and delete keys
989
- self.out_text.bind('<Control-v>', lambda _: 'break')
990
- self.out_text.bind('<Control-c>', lambda _: 'break')
991
- self.out_text.bind('<BackSpace>', lambda _: 'break')
992
-
993
- # edit context menu
994
- self.edit_context_menu = tk.Menu(self, tearoff=False)
995
- self.edit_context_menu.add_command(label="Copy",
996
- command=lambda: self.focus_get().event_generate("<<Copy>>"))
997
- self.out_text.bind("<Button-3>", self.display_popup)
998
-
999
- #########################################################
1000
- # Progress Frame
1001
- #
1002
- # Create the elements of the progress frame
1003
- progress_frame = ttk.Frame(right_frame, padding='0.1i')
1004
- self.progress_label = ttk.Label(progress_frame, text="Progress:", justify=tk.LEFT)
1005
- self.progress_var = tk.DoubleVar()
1006
- self.progress = ttk.Progressbar(progress_frame, orient="horizontal", variable=self.progress_var, length=200,
1007
- maximum=100)
1008
- CreateToolTip(self.progress_label, 'Show tool operation progress')
1009
- CreateToolTip(self.progress, 'Show tool operation progress')
1010
-
1011
- # Define layout of the frame
1012
- self.progress_label.grid(row=0, column=0, sticky=tk.E, padx=5)
1013
- self.progress.grid(row=0, column=1, sticky=tk.E)
1014
- progress_frame.grid(row=4, column=0, columnspan=2, sticky=tk.SE)
1015
-
1016
- progress_frame.rowconfigure(4, weight=1)
1017
- progress_frame.columnconfigure(0, weight=1)
1018
-
1019
- #########################################################
1020
- # Tool Selection
1021
- #
1022
- # Select the appropriate tool, if specified, otherwise the first tool
1023
- self.tool_tree.focus(self.tool_name)
1024
- self.tool_tree.selection_set(self.tool_name)
1025
- self.tool_tree.event_generate("<<TreeviewSelect>>")
1026
-
1027
- #########################################################
1028
- # Menus
1029
- menubar = tk.Menu(self)
1030
-
1031
- self.file_menu = tk.Menu(menubar, tearoff=0)
1032
- self.file_menu.add_command(label="Set Working Directory", command=self.set_directory)
1033
-
1034
- if bt.get_verbose_mode():
1035
- self.file_menu.add_command(label="Do Not Print Tool Output", command=self.update_verbose)
1036
- else:
1037
- self.file_menu.add_command(label="Print Tool Output", command=self.update_verbose)
1038
-
1039
- self.file_menu.add_command(label="Set Num. Processors", command=self.set_procs)
1040
-
1041
- self.file_menu.add_separator()
1042
- self.file_menu.add_command(label="Exit", command=self.quit)
1043
- menubar.add_cascade(label="File", menu=self.file_menu)
1044
-
1045
- editmenu = tk.Menu(menubar, tearoff=0)
1046
- editmenu.add_command(label="Cut", command=lambda: self.focus_get().event_generate("<<Cut>>"))
1047
- editmenu.add_command(label="Copy", command=lambda: self.focus_get().event_generate("<<Copy>>"))
1048
- editmenu.add_command(label="Paste", command=lambda: self.focus_get().event_generate("<<Paste>>"))
1049
- menubar.add_cascade(label="Edit ", menu=editmenu)
1050
-
1051
- helpmenu = tk.Menu(menubar, tearoff=0)
1052
- helpmenu.add_command(label="About", command=self.about)
1053
- helpmenu.add_command(label="License", command=self.license)
1054
- menubar.add_cascade(label="Help ", menu=helpmenu)
1055
-
1056
- self.master.config(menu=menubar)
1057
-
1058
- def display_popup(self, event):
1059
- self.edit_context_menu.post(event.x_root, event.y_root)
1060
-
1061
- def update_verbose(self):
1062
- if bt.get_verbose_mode():
1063
- bt.set_verbose_mode(False)
1064
- self.file_menu.entryconfig(1, label="Print Tool Output")
1065
- else:
1066
- bt.set_verbose_mode(True)
1067
- self.file_menu.entryconfig(1, label="Do Not Print Tool Output")
1068
-
1069
- def update_selected_bera_tool(self):
1070
- selected_item = -1
1071
- for toolbox in self.bera_tools['toolbox']:
1072
- for item in toolbox['tools']:
1073
- if item['name']:
1074
- if item == self.tool_name: # update selected_item it tool found
1075
- selected_item = len(self.tools_list) - 1
1076
-
1077
- if selected_item == -1: # set self.tool_name as default tool
1078
- selected_item = 0
1079
- self.tool_name = self.tools_list[0]
1080
-
1081
- def save_tool_parameter(self):
1082
- data_path = Path(__file__).resolve().cwd().parent.parent.joinpath(r'.data')
1083
- if not data_path.exists():
1084
- data_path.mkdir()
1085
- json_file = data_path.joinpath(data_path, 'saved_tool_parameters.json')
1086
-
1087
- # Retrieve tool parameters from GUI
1088
- args = self.get_widgets_arguments()
1089
-
1090
- tool_params = {}
1091
- if json_file.exists():
1092
- with open(json_file, 'r') as open_file:
1093
- data = json.load(open_file)
1094
- if data:
1095
- tool_params = data
1096
-
1097
- with open(json_file, 'w') as new_file:
1098
- tool_params[self.current_tool_api] = args
1099
- json.dump(tool_params, new_file, indent=4)
1100
-
1101
- def get_current_tool_parameters(self):
1102
- tool_params = bt.get_bera_tool_parameters(self.tool_name)
1103
- self.current_tool_api = tool_params['tool_api']
1104
- return tool_params
1105
-
1106
- # read selection when tool selected from treeview then call self.update_tool_help
1107
- def tree_update_tool_help(self, event):
1108
- cur_item = self.tool_tree.focus()
1109
- self.tool_name = self.tool_tree.item(cur_item).get('text').replace(" ", "")
1110
- self.update_tool_info()
1111
-
1112
- # read selection when tool selected from search results then call self.update_tool_help
1113
- def update_search_tool_info(self, event):
1114
- selection = self.search_results_listbox.curselection()
1115
- self.tool_name = self.search_results_listbox.get(selection[0])
1116
-
1117
- self.update_tool_info()
1118
- if self.search_tool_selected:
1119
- print("Index {} selected".format(self.search_tool_selected[0]))
1120
-
1121
- def update_tool_info(self):
1122
- self.out_text.delete('1.0', tk.END)
1123
- for widget in self.arg_scroll_frame.winfo_children():
1124
- widget.destroy()
1125
-
1126
- k = bt.get_bera_tool_info(self.tool_name)
1127
- self.print_to_output(k)
1128
- self.print_to_output('\n')
1129
-
1130
- j = self.get_current_tool_parameters()
1131
-
1132
- param_num = 0
1133
- for p in j['parameters']:
1134
- json_str = json.dumps(p, sort_keys=True, indent=2, separators=(',', ': '))
1135
- pt = p['parameter_type']
1136
- widget = None
1137
-
1138
- if 'ExistingFileOrFloat' in pt:
1139
- widget = FileOrFloat(json_str, self, self.arg_scroll_frame)
1140
- widget.grid(row=param_num, column=0, sticky=tk.NSEW)
1141
- param_num = param_num + 1
1142
- elif 'ExistingFile' in pt or 'NewFile' in pt or 'Directory' in pt:
1143
- widget = FileSelector(json_str, self, self.arg_scroll_frame)
1144
- widget.grid(row=param_num, column=0, sticky=tk.NSEW)
1145
- param_num = param_num + 1
1146
- elif 'FileList' in pt:
1147
- widget = MultifileSelector(json_str, self, self.arg_scroll_frame)
1148
- widget.grid(row=param_num, column=0, sticky=tk.W)
1149
- param_num = param_num + 1
1150
- elif 'OptionList' in pt:
1151
- if 'data_type' in p.keys():
1152
- if p['data_type'] == 'Boolean':
1153
- widget = BooleanInput(json_str, self.arg_scroll_frame)
1154
- widget.grid(row=param_num, column=0, sticky=tk.W)
1155
- param_num = param_num + 1
1156
- else:
1157
- widget = OptionsInput(json_str, self.arg_scroll_frame)
1158
- widget.grid(row=param_num, column=0, sticky=tk.W)
1159
- param_num = param_num + 1
1160
- elif ('Float' in pt or 'Integer' in pt or
1161
- 'Text' in pt or 'String' in pt or 'StringOrNumber' in pt or
1162
- 'StringList' in pt or 'VectorAttributeField' in pt):
1163
- widget = DataInput(json_str, self.arg_scroll_frame)
1164
- widget.grid(row=param_num, column=0, sticky=tk.NSEW)
1165
- param_num = param_num + 1
1166
- else:
1167
- messagebox.showinfo("Error", "Unsupported parameter type: {}.".format(pt))
1168
-
1169
- param_value = None
1170
- if 'saved_value' in p.keys():
1171
- param_value = p['saved_value']
1172
- if param_value is None:
1173
- param_value = p['default_value']
1174
- if param_value is not None:
1175
- if type(widget) is OptionsInput:
1176
- widget.value = param_value
1177
- elif widget:
1178
- widget.value.set(param_value)
1179
- else:
1180
- print('No default value found: {}'.format(p['name']))
1181
-
1182
- # hide optional widgets
1183
- if widget:
1184
- if widget.optional and hasattr(widget, 'label'):
1185
- widget.label.config(foreground='blue')
1186
-
1187
- if widget.optional and not bt.show_advanced:
1188
- widget.grid_forget()
1189
-
1190
- self.update_args_box()
1191
- self.out_text.see("%d.%d" % (1, 0))
1192
-
1193
- def update_toolbox_icon(self, event):
1194
- cur_item = self.tool_tree.focus()
1195
- dict_tool = self.tool_tree.item(cur_item) # retrieve the toolbox name
1196
- self.toolbox_name = dict_tool.get('text').replace(" ", "") # delete the space between the icon and text
1197
- self.toolbox_open = dict_tool.get('open') # retrieve whether the toolbox is open or not
1198
- if self.toolbox_open: # set image accordingly
1199
- self.tool_tree.item(self.toolbox_name, image=self.open_toolbox_icon)
1200
- else:
1201
- self.tool_tree.item(self.toolbox_name, image=self.closed_toolbox_icon)
1202
-
1203
- def update_search(self, event):
1204
- self.search_list = []
1205
- self.search_string = self.search_text.get().lower()
1206
- self.search_results_listbox.delete(0, 'end') # empty the search results
1207
- num_results = 0
1208
- for tool in self.tools_list: # search tool names
1209
- tool_lower = tool.lower()
1210
- # search string found within tool name
1211
- if tool_lower.find(self.search_string) != (-1):
1212
- num_results = num_results + 1
1213
- # tool added to listbox and to search results string
1214
- self.search_results_listbox.insert(num_results, tool)
1215
- self.search_list.append(tool)
1216
- index = 0
1217
-
1218
- # update label to show tools found
1219
- self.search_frame.config(text="{} Tools Found".format(len(self.search_list)))
1220
-
1221
- def tool_help_button(self):
1222
- # open the user manual section for the current tool
1223
- webbrowser.open_new_tab(self.get_current_tool_parameters()['tech_link'])
1224
-
1225
- def add_tools_to_treeview(self):
1226
- # Add toolboxes and tools to treeview
1227
- index = 0
1228
- for toolbox in self.lower_toolboxes:
1229
- if toolbox.find('/') != (-1): # toolboxes
1230
- self.tool_tree.insert(toolbox[:toolbox.find('/')], 0, text=" " + toolbox[toolbox.find('/') + 1:],
1231
- iid=toolbox[toolbox.find('/') + 1:], tags='toolbox',
1232
- image=self.closed_toolbox_icon)
1233
- for tool in self.sorted_tools[index]: # add tools within toolbox
1234
- self.tool_tree.insert(toolbox[toolbox.find('/') + 1:], 'end', text=" " + tool,
1235
- tags='tool', iid=tool, image=self.tool_icon)
1236
- else: # sub toolboxes
1237
- self.tool_tree.insert('', 'end', text=" " + toolbox, iid=toolbox, tags='toolbox',
1238
- image=self.closed_toolbox_icon)
1239
- for tool in self.sorted_tools[index]: # add tools within sub toolbox
1240
- self.tool_tree.insert(toolbox, 'end', text=" " + tool, iid=tool, tags='tool', image=self.tool_icon)
1241
- index = index + 1
1242
-
1243
- def add_recent_tool_to_search(self):
1244
- if bt.recent_tool:
1245
- self.search_results_listbox.delete(0, 'end')
1246
- self.search_list.append(bt.recent_tool)
1247
- self.search_results_listbox.insert(END, bt.recent_tool)
1248
-
1249
- self.search_frame.config(text='Recent used tool')
1250
- self.tool_name = self.search_results_listbox.get(0)
1251
-
1252
- #########################################################
1253
- # Functions (original)
1254
- def about(self):
1255
- self.out_text.delete('1.0', tk.END)
1256
- self.print_to_output(bt.about())
1257
-
1258
- def license(self):
1259
- self.out_text.delete('1.0', tk.END)
1260
- self.print_to_output(bt.license())
1261
-
1262
- def set_directory(self):
1263
- try:
1264
- self.working_dir = filedialog.askdirectory(initialdir=self.working_dir)
1265
- bt.set_working_dir(self.working_dir)
1266
- except:
1267
- messagebox.showinfo("Warning", "Could not set the working directory.")
1268
-
1269
- def set_procs(self):
1270
- try:
1271
- max_cpu_cores = bt.get_max_cpu_cores()
1272
- max_procs = askinteger(
1273
- title="Max CPU cores used",
1274
- prompt="Set the number of processors to be used (maximum: {}, -1: all):".format(max_cpu_cores),
1275
- parent=self, initialvalue=bt.get_max_procs(), minvalue=-1, maxvalue=max_cpu_cores)
1276
- if max_procs:
1277
- self.update_procs(max_procs)
1278
- self.mpc_scale.set(max_procs)
1279
- except:
1280
- messagebox.showinfo("Warning", "Could not set the number of processors.")
1281
-
1282
- def update_procs(self, value):
1283
- self.__max_procs = int(value)
1284
- bt.set_max_procs(self.__max_procs)
1285
-
1286
- def get_widgets_arguments(self):
1287
- args = {}
1288
- param_missing = False
1289
- for widget in self.arg_scroll_frame.winfo_children():
1290
- v = widget.get_value()
1291
- if v and len(v) == 2:
1292
- args[v[0]] = v[1]
1293
- else:
1294
- self.print_line_to_output('[Missing argument]:'+widget.name+": parameter not specified.", 'missing')
1295
- param_missing = True
1296
-
1297
- if param_missing:
1298
- args = None
1299
-
1300
- return args
1301
-
1302
- def reset_tool(self):
1303
- for widget in self.arg_scroll_frame.winfo_children():
1304
- args = bt.get_bera_tool_parameters(self.tool_name)
1305
- for param in args['parameters']:
1306
- default_value = param['default_value']
1307
- if widget.flag == param['flag']:
1308
- if type(widget) is OptionsInput:
1309
- widget.value = default_value
1310
- else:
1311
- widget.value.set(default_value)
1312
-
1313
- def start_run_tool_thread(self):
1314
- t = threading.Thread(target=self.run_tool, args=())
1315
- t.daemon = True
1316
- t.start()
1317
-
1318
- def run_tool(self):
1319
- bt.set_working_dir(self.working_dir)
1320
-
1321
- args = self.get_widgets_arguments()
1322
- if not args:
1323
- print('Please check the parameters.')
1324
- return
1325
-
1326
- self.print_line_to_output("")
1327
- self.print_line_to_output('Staring tool {} ...'.format(self.tool_name))
1328
- self.print_line_to_output(bt.ascii_art)
1329
- self.print_line_to_output("Tool arguments:")
1330
- self.print_line_to_output(json.dumps(args, indent=4))
1331
- self.print_line_to_output("")
1332
- self.save_tool_parameter()
1333
- bt.recent_tool = self.tool_name
1334
- bt.save_recent_tool()
1335
-
1336
- # Run the tool and check the return value for an error
1337
- for key in args.keys():
1338
- if type(args[key]) is not str:
1339
- args[key] = str(args[key])
1340
-
1341
- # disable button
1342
- # self.run_button.config(text='Running', state='disabled')
1343
- if bt.run_tool_bt(self.current_tool_api, args, self.custom_callback) == 1:
1344
- print("Error running {}".format(self.tool_name))
1345
- # restore Run button
1346
- # self.run_button.config(text='Run', state='enable')
1347
- else:
1348
- self.progress_var.set(0)
1349
- self.progress_label['text'] = "Progress:"
1350
- self.progress.update_idletasks()
1351
- # restore Run button
1352
- # self.run_button.config(text='Run', state='enable')
1353
-
1354
- return
1355
-
1356
- def print_to_output(self, value):
1357
- self.out_text.insert(tk.END, value)
1358
- self.out_text.see(tk.END)
1359
-
1360
- def print_line_to_output(self, value, tag=None):
1361
- self.out_text.insert(tk.END, value + "\n", tag)
1362
- self.out_text.see(tk.END)
1363
-
1364
- def cancel_operation(self):
1365
- bt.cancel_op = True
1366
- self.print_line_to_output('------------------------------------')
1367
- self.print_line_to_output("Tool operation cancelling...")
1368
- self.progress.update_idletasks()
1369
-
1370
- def show_advanced(self):
1371
- if not self.show_advanced_button or len(self.arg_scroll_frame.winfo_children()) <= 0:
1372
- return
1373
-
1374
- if bt.show_advanced:
1375
- bt.show_advanced = False
1376
- else:
1377
- bt.show_advanced = True
1378
-
1379
- if bt.show_advanced:
1380
- self.show_advanced_button.config(text="Hide Advanced Options")
1381
- self.save_tool_parameter()
1382
- self.update_tool_info()
1383
- else:
1384
- self.show_advanced_button.config(text="Show Advanced Options")
1385
- for widget in self.arg_scroll_frame.winfo_children():
1386
- if widget.optional:
1387
- widget.grid_forget()
1388
-
1389
- def view_code(self):
1390
- webbrowser.open_new_tab(self.get_current_tool_parameters()['tech_link'])
1391
-
1392
- def update_args_box(self):
1393
- s = ""
1394
- self.current_tool_lbl['text'] = "Current Tool: {}".format(
1395
- self.tool_name)
1396
-
1397
- # self.spacer['width'] = width=(35-len(self.tool_name))
1398
- # for item in bt.tool_help(self.tool_name).splitlines():
1399
- for item in bt.get_bera_tool_info(self.tool_name).splitlines():
1400
- if item.startswith("-"):
1401
- k = item.split(" ")
1402
- if "--" in k[1]:
1403
- value = k[1].replace(",", "")
1404
- else:
1405
- value = k[0].replace(",", "")
1406
-
1407
- if "flag" in item.lower():
1408
- s = s + value + " "
1409
- else:
1410
- if "file" in item.lower():
1411
- s = s + value + "='{}' "
1412
- else:
1413
- s = s + value + "={} "
1414
-
1415
- # self.args_value.set(s.strip())
1416
-
1417
- def custom_callback(self, value):
1418
- """
1419
- A custom callback for dealing with tool output.
1420
- """
1421
- value = str(value)
1422
-
1423
- if "%" in value:
1424
- try:
1425
- str_progress = extract_string_from_printout(value, '%')
1426
- value = value.replace(str_progress, '').strip() # remove progress string
1427
- progress = float(str_progress.replace("%", "").strip())
1428
- self.progress_var.set(int(progress))
1429
- except ValueError as e:
1430
- print("custom_callback: Problem converting parsed data into number: ", e)
1431
- except Exception as e:
1432
- print(e)
1433
- elif 'PROGRESS_LABEL' in value:
1434
- str_label = extract_string_from_printout(value, 'PROGRESS_LABEL')
1435
- value = value.replace(str_label, '').strip() # remove progress string
1436
- value = value.replace('"', '')
1437
- str_label = str_label.replace("PROGRESS_LABEL", "").strip()
1438
- self.progress_label['text'] = str_label
1439
-
1440
- if value != '':
1441
- self.print_line_to_output(value)
1442
-
1443
- self.update() # this is needed for cancelling and updating the progress bar
1444
-
1445
- def select_all(self, event):
1446
- self.out_text.tag_add(tk.SEL, "1.0", tk.END)
1447
- self.out_text.mark_set(tk.INSERT, "1.0")
1448
- self.out_text.see(tk.INSERT)
1449
- return 'break'
1450
-
1451
-
1452
- # handle debugging exit signal and kill sub-process
1453
- def handler(event):
1454
- # root.destroy()
1455
- print('Caught ^C')
1456
- bt.cancel_op = True
1457
- sleep(3000)
1458
-
1459
-
1460
- def check(btr):
1461
- btr.root.after(1000, check) # time in ms.
1462
-
1463
-
1464
- def main_runner():
1465
- tool_name = None
1466
- if len(sys.argv) > 1:
1467
- tool_name = str(sys.argv[1])
1468
- btr = MainGui(tool_name)
1469
-
1470
- signal.signal(signal.SIGINT, lambda x, y: print('terminal ^C') or handler(None))
1471
- btr.bind_all('<Control-c>', handler)
1472
-
1473
- ico = Image.open(r'img\BERALogo.png')
1474
- photo = ImageTk.PhotoImage(ico)
1475
- btr.root().wm_iconphoto(False, photo)
1476
- btr.root().eval('tk::PlaceWindow . center')
1477
- btr.mainloop()
1478
-
1479
-
1480
- if __name__ == '__main__':
1481
- main_runner()