BERATools 0.2.3__py3-none-any.whl → 0.2.5__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 (78) hide show
  1. beratools/__init__.py +8 -3
  2. beratools/core/{algo_footprint_rel.py → algo_canopy_footprint_exp.py} +176 -139
  3. beratools/core/algo_centerline.py +61 -77
  4. beratools/core/algo_common.py +48 -57
  5. beratools/core/algo_cost.py +18 -25
  6. beratools/core/algo_dijkstra.py +37 -45
  7. beratools/core/algo_line_grouping.py +100 -100
  8. beratools/core/algo_merge_lines.py +40 -8
  9. beratools/core/algo_split_with_lines.py +289 -304
  10. beratools/core/algo_vertex_optimization.py +25 -46
  11. beratools/core/canopy_threshold_relative.py +755 -0
  12. beratools/core/constants.py +8 -9
  13. beratools/{tools → core}/line_footprint_functions.py +411 -258
  14. beratools/core/logger.py +18 -2
  15. beratools/core/tool_base.py +17 -75
  16. beratools/gui/assets/BERALogo.ico +0 -0
  17. beratools/gui/assets/BERA_Splash.gif +0 -0
  18. beratools/gui/assets/BERA_WizardImage.png +0 -0
  19. beratools/gui/assets/beratools.json +475 -2171
  20. beratools/gui/bt_data.py +585 -234
  21. beratools/gui/bt_gui_main.py +129 -91
  22. beratools/gui/main.py +4 -7
  23. beratools/gui/tool_widgets.py +530 -354
  24. beratools/tools/__init__.py +0 -7
  25. beratools/tools/{line_footprint_absolute.py → canopy_footprint_absolute.py} +81 -56
  26. beratools/tools/canopy_footprint_exp.py +113 -0
  27. beratools/tools/centerline.py +30 -37
  28. beratools/tools/check_seed_line.py +127 -0
  29. beratools/tools/common.py +65 -586
  30. beratools/tools/{line_footprint_fixed.py → ground_footprint.py} +140 -117
  31. beratools/tools/line_footprint_relative.py +64 -35
  32. beratools/tools/tool_template.py +48 -40
  33. beratools/tools/vertex_optimization.py +20 -34
  34. beratools/utility/env_checks.py +53 -0
  35. beratools/utility/spatial_common.py +210 -0
  36. beratools/utility/tool_args.py +138 -0
  37. beratools-0.2.5.dist-info/METADATA +134 -0
  38. beratools-0.2.5.dist-info/RECORD +50 -0
  39. {beratools-0.2.3.dist-info → beratools-0.2.5.dist-info}/WHEEL +1 -1
  40. beratools-0.2.5.dist-info/entry_points.txt +3 -0
  41. beratools-0.2.5.dist-info/licenses/LICENSE +674 -0
  42. beratools/core/algo_tiler.py +0 -428
  43. beratools/gui/__init__.py +0 -11
  44. beratools/gui/batch_processing_dlg.py +0 -513
  45. beratools/gui/map_window.py +0 -162
  46. beratools/tools/Beratools_r_script.r +0 -1120
  47. beratools/tools/Ht_metrics.py +0 -116
  48. beratools/tools/batch_processing.py +0 -136
  49. beratools/tools/canopy_threshold_relative.py +0 -672
  50. beratools/tools/canopycostraster.py +0 -222
  51. beratools/tools/fl_regen_csf.py +0 -428
  52. beratools/tools/forest_line_attributes.py +0 -408
  53. beratools/tools/line_grouping.py +0 -45
  54. beratools/tools/ln_relative_metrics.py +0 -615
  55. beratools/tools/r_cal_lpi_elai.r +0 -25
  56. beratools/tools/r_generate_pd_focalraster.r +0 -101
  57. beratools/tools/r_interface.py +0 -80
  58. beratools/tools/r_point_density.r +0 -9
  59. beratools/tools/rpy_chm2trees.py +0 -86
  60. beratools/tools/rpy_dsm_chm_by.py +0 -81
  61. beratools/tools/rpy_dtm_by.py +0 -63
  62. beratools/tools/rpy_find_cellsize.py +0 -43
  63. beratools/tools/rpy_gnd_csf.py +0 -74
  64. beratools/tools/rpy_hummock_hollow.py +0 -85
  65. beratools/tools/rpy_hummock_hollow_raster.py +0 -71
  66. beratools/tools/rpy_las_info.py +0 -51
  67. beratools/tools/rpy_laz2las.py +0 -40
  68. beratools/tools/rpy_lpi_elai_lascat.py +0 -466
  69. beratools/tools/rpy_normalized_lidar_by.py +0 -56
  70. beratools/tools/rpy_percent_above_dbh.py +0 -80
  71. beratools/tools/rpy_points2trees.py +0 -88
  72. beratools/tools/rpy_vegcoverage.py +0 -94
  73. beratools/tools/tiler.py +0 -48
  74. beratools/tools/zonal_threshold.py +0 -144
  75. beratools-0.2.3.dist-info/METADATA +0 -108
  76. beratools-0.2.3.dist-info/RECORD +0 -74
  77. beratools-0.2.3.dist-info/entry_points.txt +0 -2
  78. beratools-0.2.3.dist-info/licenses/LICENSE +0 -22
@@ -12,44 +12,38 @@ Description:
12
12
 
13
13
  The purpose of this script is to provide main GUI functions.
14
14
  """
15
+
15
16
  import json
16
17
  import os
18
+ import shlex
17
19
  import sys
18
20
  import webbrowser
19
21
  from pathlib import Path
20
- from re import compile
21
-
22
- from PyQt5 import QtCore, QtGui, QtWidgets
23
22
 
24
23
  import beratools.core.constants as bt_const
25
- import beratools.tools.common as bt_common
26
24
  from beratools.gui import bt_data
25
+ from beratools.utility import env_checks
27
26
  from beratools.gui.tool_widgets import ToolWidgets
28
27
 
29
- # A regular expression, to extract the % complete.
30
- progress_re = compile("Total complete: (\d+)%")
31
- bt = bt_data.BTData()
32
-
33
-
34
- def simple_percent_parser(output):
35
- """
36
- Match lines using the progress_re regex.
28
+ from PyQt5 import QtCore, QtGui, QtWidgets
37
29
 
38
- Return a single integer for the % progress.
39
- """
40
- m = progress_re.search(output)
41
- if m:
42
- pc_complete = m.group(1)
43
- return int(pc_complete)
30
+ bt = bt_data.BTData()
44
31
 
32
+ def extract_string_from_printout(str_print, str_extract):
33
+ str_array = shlex.split(str_print) # keep string in double quotes
34
+ str_array_enum = enumerate(str_array)
35
+ index = 0
36
+ for item in str_array_enum:
37
+ if str_extract in item[1]:
38
+ index = item[0]
39
+ break
40
+ str_out = str_array[index]
41
+ return str_out.strip()
45
42
 
46
43
  class _SearchProxyModel(QtCore.QSortFilterProxyModel):
47
-
48
44
  def setFilterRegExp(self, pattern):
49
45
  if isinstance(pattern, str):
50
- pattern = QtCore.QRegExp(
51
- pattern, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.FixedString
52
- )
46
+ pattern = QtCore.QRegExp(pattern, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.FixedString)
53
47
  super(_SearchProxyModel, self).setFilterRegExp(pattern)
54
48
 
55
49
  def _accept_index(self, idx):
@@ -77,7 +71,7 @@ class BTTreeView(QtWidgets.QWidget):
77
71
 
78
72
  # controls
79
73
  self.tool_search = QtWidgets.QLineEdit()
80
- self.tool_search.setPlaceholderText('Search...')
74
+ self.tool_search.setPlaceholderText("Search...")
81
75
 
82
76
  self.tags_model = _SearchProxyModel()
83
77
  self.tree_model = QtGui.QStandardItemModel()
@@ -110,7 +104,7 @@ class BTTreeView(QtWidgets.QWidget):
110
104
  self.tree_view.setFirstColumnSpanned(0, self.tree_view.rootIndex(), True)
111
105
  self.tree_view.setUniformRowHeights(True)
112
106
 
113
- self.tree_model.setHorizontalHeaderLabels(['Tools'])
107
+ self.tree_model.setHorizontalHeaderLabels(["Tools"])
114
108
  self.tree_sel_model = self.tree_view.selectionModel()
115
109
  self.tree_sel_model.selectionChanged.connect(self.tree_view_selection_changed)
116
110
 
@@ -146,9 +140,7 @@ class BTTreeView(QtWidgets.QWidget):
146
140
  QtGui.QIcon(os.path.join(bt_const.ASSETS_PATH, "close.gif")), toolbox
147
141
  )
148
142
  for j, tool in enumerate(sorted_tools[i]):
149
- child = QtGui.QStandardItem(
150
- QtGui.QIcon(os.path.join(bt_const.ASSETS_PATH, "tool.gif")), tool
151
- )
143
+ child = QtGui.QStandardItem(QtGui.QIcon(os.path.join(bt_const.ASSETS_PATH, "tool.gif")), tool)
152
144
  if i == 0 and j == 0:
153
145
  first_child = child
154
146
 
@@ -178,21 +170,19 @@ class BTTreeView(QtWidgets.QWidget):
178
170
  return
179
171
 
180
172
  if item.hasChildren():
181
- item.setIcon(QtGui.QIcon(os.path.join(bt_const.ASSETS_PATH, 'open.gif')))
173
+ item.setIcon(QtGui.QIcon(os.path.join(bt_const.ASSETS_PATH, "open.gif")))
182
174
 
183
175
  def tree_item_collapsed(self, index):
184
176
  source_index = self.tags_model.mapToSource(index)
185
177
  item = self.tree_model.itemFromIndex(source_index)
186
178
  if not item:
187
179
  return
188
-
180
+
189
181
  if item.hasChildren():
190
- item.setIcon(QtGui.QIcon(os.path.join(bt_const.ASSETS_PATH, 'close.gif')))
182
+ item.setIcon(QtGui.QIcon(os.path.join(bt_const.ASSETS_PATH, "close.gif")))
191
183
 
192
184
  def get_tool_index(self, tool_name):
193
- item = self.tree_model.findItems(
194
- tool_name, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive
195
- )
185
+ item = self.tree_model.findItems(tool_name, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive)
196
186
  if len(item) > 0:
197
187
  item = item[0]
198
188
 
@@ -201,13 +191,9 @@ class BTTreeView(QtWidgets.QWidget):
201
191
 
202
192
  def select_tool_by_index(self, index):
203
193
  proxy_index = self.tags_model.mapFromSource(index)
204
- self.tree_sel_model.select(
205
- proxy_index, QtCore.QItemSelectionModel.ClearAndSelect
206
- )
194
+ self.tree_sel_model.select(proxy_index, QtCore.QItemSelectionModel.ClearAndSelect)
207
195
  self.tree_view.expand(proxy_index.parent())
208
- self.tree_sel_model.setCurrentIndex(
209
- proxy_index, QtCore.QItemSelectionModel.Current
210
- )
196
+ self.tree_sel_model.setCurrentIndex(proxy_index, QtCore.QItemSelectionModel.Current)
211
197
 
212
198
  def select_tool_by_name(self, name):
213
199
  index = self.get_tool_index(name)
@@ -276,15 +262,15 @@ class BTSlider(QtWidgets.QWidget):
276
262
  self.slider.sliderMoved.connect(self.slider_moved)
277
263
 
278
264
  def slider_moved(self, value):
279
- bt.set_max_procs(value)
280
- QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), f'{value}')
265
+ bt.set_selected_cpu_cores(value)
266
+ QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), f"{value}")
281
267
  self.label.setText(self.generate_label_text())
282
268
 
283
269
  def generate_label_text(self, value=None):
284
270
  if not value:
285
271
  value = self.slider.value()
286
272
 
287
- return f'Use CPU Cores: {value:3d}'
273
+ return f"Use CPU Cores: {value:3d}"
288
274
 
289
275
 
290
276
  class BTListView(QtWidgets.QWidget):
@@ -303,8 +289,8 @@ class BTListView(QtWidgets.QWidget):
303
289
  btn_clear = QtWidgets.QPushButton()
304
290
  btn_delete.setIcon(delete_icon)
305
291
  btn_clear.setIcon(clear_icon)
306
- btn_delete.setToolTip('Delete selected tool history')
307
- btn_clear.setToolTip('clear all tool history')
292
+ btn_delete.setToolTip("Delete selected tool history")
293
+ btn_clear.setToolTip("clear all tool history")
308
294
  btn_delete.setFixedWidth(40)
309
295
  btn_clear.setFixedWidth(40)
310
296
 
@@ -363,11 +349,11 @@ class MainWindow(QtWidgets.QMainWindow):
363
349
  super().__init__()
364
350
 
365
351
  self.script_dir = os.path.dirname(os.path.realpath(__file__))
366
- self.title = 'BERA Tools'
352
+ self.title = "BERA Tools"
367
353
  self.setWindowTitle(self.title)
368
354
  self.working_dir = bt.work_dir
369
355
  self.tool_api = None
370
- self.tool_name = 'Centerline'
356
+ self.tool_name = "Centerline"
371
357
  self.recent_tool = bt.recent_tool
372
358
  if self.recent_tool:
373
359
  self.tool_name = self.recent_tool
@@ -396,7 +382,7 @@ class MainWindow(QtWidgets.QMainWindow):
396
382
 
397
383
  # group box for tree view
398
384
  tree_box = QtWidgets.QGroupBox()
399
- tree_box.setTitle('Tools available')
385
+ tree_box.setTitle("Tools available")
400
386
  tree_layout = QtWidgets.QVBoxLayout()
401
387
  tree_layout.addWidget(self.tree_view)
402
388
  tree_box.setLayout(tree_layout)
@@ -410,7 +396,7 @@ class MainWindow(QtWidgets.QMainWindow):
410
396
  tool_history_box = QtWidgets.QGroupBox()
411
397
  tool_history_layout = QtWidgets.QVBoxLayout()
412
398
  tool_history_layout.addWidget(self.tool_history)
413
- tool_history_box.setTitle('Tool history')
399
+ tool_history_box.setTitle("Tool history")
414
400
  tool_history_box.setLayout(tool_history_layout)
415
401
 
416
402
  # left layout
@@ -422,35 +408,35 @@ class MainWindow(QtWidgets.QMainWindow):
422
408
  self.left_layout.addWidget(tool_history_box)
423
409
 
424
410
  # top buttons
425
- label = QtWidgets.QLabel(f'{self.tool_name}')
426
- label.setFont(QtGui.QFont('Consolas', 14))
427
- self.btn_advanced = QtWidgets.QPushButton('Show Advanced Options')
411
+ label = QtWidgets.QLabel(f"{self.tool_name}")
412
+ label.setFont(QtGui.QFont("Consolas", 14))
413
+ self.btn_advanced = QtWidgets.QPushButton("Show Advanced Options")
428
414
  self.btn_advanced.setFixedWidth(180)
429
- btn_help = QtWidgets.QPushButton('help')
430
- btn_code = QtWidgets.QPushButton('Code')
431
- btn_help.setFixedWidth(250)
432
- btn_code.setFixedWidth(100)
415
+ btn_help = QtWidgets.QPushButton("help")
416
+ btn_help.setFixedWidth(100)
433
417
 
434
418
  self.btn_layout_top = QtWidgets.QHBoxLayout()
435
419
  self.btn_layout_top.setAlignment(QtCore.Qt.AlignRight)
436
420
  self.btn_layout_top.addWidget(label)
437
421
  self.btn_layout_top.addStretch(1)
438
422
  self.btn_layout_top.addWidget(self.btn_advanced)
439
- self.btn_layout_top.addWidget(btn_code)
423
+ self.btn_layout_top.addWidget(btn_help)
440
424
 
441
425
  # ToolWidgets
442
426
  tool_args = bt.get_bera_tool_args(self.tool_name)
443
427
  self.tool_widget = ToolWidgets(self.recent_tool, tool_args, bt.show_advanced)
444
428
 
445
429
  # bottom buttons
446
- slider = BTSlider(bt.max_procs, bt.max_cpu_cores)
447
- btn_default_args = QtWidgets.QPushButton('Load Default Arguments')
448
- self.btn_run = QtWidgets.QPushButton('Run')
449
- btn_cancel = QtWidgets.QPushButton('Cancel')
430
+ slider = BTSlider(bt.selected_cpu_cores, bt.max_cpu_cores)
431
+ btn_default_args = QtWidgets.QPushButton("Load Default Arguments")
432
+ self.btn_run = QtWidgets.QPushButton("Run")
433
+ btn_cancel = QtWidgets.QPushButton("Cancel")
434
+ btn_exit = QtWidgets.QPushButton("Exit")
450
435
  btn_default_args.setFixedWidth(150)
451
436
  slider.setFixedWidth(250)
452
437
  self.btn_run.setFixedWidth(120)
453
438
  btn_cancel.setFixedWidth(120)
439
+ btn_exit.setFixedWidth(120)
454
440
 
455
441
  btn_layout_bottom = QtWidgets.QHBoxLayout()
456
442
  btn_layout_bottom.setAlignment(QtCore.Qt.AlignRight)
@@ -459,17 +445,18 @@ class MainWindow(QtWidgets.QMainWindow):
459
445
  btn_layout_bottom.addWidget(slider)
460
446
  btn_layout_bottom.addWidget(self.btn_run)
461
447
  btn_layout_bottom.addWidget(btn_cancel)
448
+ btn_layout_bottom.addWidget(btn_exit)
462
449
 
463
450
  self.top_right_layout = QtWidgets.QVBoxLayout()
464
451
  self.top_right_layout.addLayout(self.btn_layout_top)
465
452
  self.top_right_layout.addWidget(self.tool_widget)
466
453
  self.top_right_layout.addLayout(btn_layout_bottom)
467
- tool_widget_grp = QtWidgets.QGroupBox('Tool')
454
+ tool_widget_grp = QtWidgets.QGroupBox("Tool")
468
455
  tool_widget_grp.setLayout(self.top_right_layout)
469
456
 
470
457
  # Text widget
471
458
  self.text_edit = QtWidgets.QPlainTextEdit()
472
- self.text_edit.setFont(QtGui.QFont('Consolas', 9))
459
+ self.text_edit.setFont(QtGui.QFont("Consolas", 9))
473
460
  self.text_edit.setReadOnly(True)
474
461
  self.print_about()
475
462
 
@@ -494,15 +481,33 @@ class MainWindow(QtWidgets.QMainWindow):
494
481
  # signals and slots
495
482
  self.btn_advanced.clicked.connect(self.show_advanced)
496
483
  btn_help.clicked.connect(self.show_help)
497
- btn_code.clicked.connect(self.view_code)
498
484
  btn_default_args.clicked.connect(self.load_default_args)
499
485
  self.btn_run.clicked.connect(self.start_process)
500
486
  btn_cancel.clicked.connect(self.stop_process)
487
+ btn_exit.clicked.connect(self.close)
501
488
 
502
489
  widget = QtWidgets.QWidget(self)
503
490
  widget.setLayout(page_layout)
504
491
  self.setCentralWidget(widget)
505
492
 
493
+ def closeEvent(self, event):
494
+ self.cancel_op = True
495
+ if self.process is None:
496
+ window_msg="Are you sure you want to quit?"
497
+ else:
498
+ window_msg="Work in progress. Are you sure you want to quit?"
499
+
500
+ reply= QtWidgets.QMessageBox.question(self,'Confirmation:',window_msg,
501
+ QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
502
+ QtWidgets.QMessageBox.StandardButton.No)
503
+
504
+ if reply == QtWidgets.QMessageBox.StandardButton.Yes:
505
+ if self.process is not None:
506
+ self.process.kill()
507
+ event.accept()
508
+ else:
509
+ event.ignore()
510
+
506
511
  def set_tool(self, tool=None):
507
512
  if tool:
508
513
  self.tool_name = tool
@@ -539,19 +544,33 @@ class MainWindow(QtWidgets.QMainWindow):
539
544
 
540
545
  def show_help(self):
541
546
  # open the user manual section for the current tool
542
- webbrowser.open_new_tab(self.get_current_tool_parameters()['tech_link'])
547
+ tech_link = self.get_current_tool_parameters()["tech_link"]
548
+ if isinstance(tech_link, list):
549
+ if tech_link:
550
+ tech_link = tech_link[0]
551
+ else:
552
+ tech_link = ""
553
+ webbrowser.open_new_tab(str(tech_link))
543
554
 
544
555
  def print_about(self):
545
556
  self.text_edit.clear()
546
557
  self.print_to_output(bt.about())
558
+ self.show_startup_warnings()
559
+
560
+ def show_startup_warnings(self):
561
+ warning_message = env_checks.get_gdal_proj_env_warning()
562
+ if warning_message:
563
+ self.print_line_to_output("")
564
+ self.print_line_to_output("[Warning]")
565
+ self.print_line_to_output(warning_message)
547
566
 
548
567
  def print_license(self):
549
568
  self.text_edit.clear()
550
569
  self.print_to_output(bt.license())
551
570
 
552
571
  def update_procs(self, value):
553
- max_procs = int(value)
554
- bt.set_max_procs(max_procs)
572
+ selected_cpu_cores = int(value)
573
+ bt.set_selected_cpu_cores(selected_cpu_cores)
555
574
 
556
575
  def print_to_output(self, text):
557
576
  self.text_edit.moveCursor(QtGui.QTextCursor.End)
@@ -560,7 +579,7 @@ class MainWindow(QtWidgets.QMainWindow):
560
579
 
561
580
  def print_line_to_output(self, text, tag=None):
562
581
  self.text_edit.moveCursor(QtGui.QTextCursor.End)
563
- self.text_edit.insertPlainText(text + '\n')
582
+ self.text_edit.insertPlainText(text + "\n")
564
583
  self.text_edit.moveCursor(QtGui.QTextCursor.End)
565
584
 
566
585
  def show_advanced(self):
@@ -573,35 +592,33 @@ class MainWindow(QtWidgets.QMainWindow):
573
592
 
574
593
  self.set_tool()
575
594
 
576
- def view_code(self):
577
- webbrowser.open_new_tab(self.get_current_tool_parameters()['tech_link'])
578
595
 
579
596
  def custom_callback(self, value):
580
597
  """Define custom callback that deals with tool output."""
581
598
  value = str(value)
582
599
  value.strip()
583
- if value != '':
600
+ if value != "":
584
601
  # remove esc string which origin is unknown
585
- rm_str = '\x1b[0m'
602
+ rm_str = "\x1b[0m"
586
603
  if rm_str in value:
587
- value = value.replace(rm_str, '')
604
+ value = value.replace(rm_str, "")
588
605
 
589
606
  if "%" in value:
590
607
  try:
591
- str_progress = bt_common.extract_string_from_printout(value, '%')
608
+ str_progress = extract_string_from_printout(value, "%")
592
609
 
593
610
  # remove progress string
594
- value = value.replace(str_progress, '').strip()
611
+ value = value.replace(str_progress, "").strip()
595
612
  progress = float(str_progress.replace("%", "").strip())
596
613
  self.progress_bar.setValue(int(progress))
597
614
  except ValueError as e:
598
615
  print("custom_callback: Problem parsing data into number: ", e)
599
616
  except Exception as e:
600
617
  print(e)
601
- elif 'PROGRESS_LABEL' in value:
602
- str_label = bt_common.extract_string_from_printout(value, 'PROGRESS_LABEL')
603
- value = value.replace(str_label, '').strip() # remove progress string
604
- value = value.replace('"', '')
618
+ elif "PROGRESS_LABEL" in value:
619
+ str_label = extract_string_from_printout(value, "PROGRESS_LABEL")
620
+ value = value.replace(str_label, "").strip() # remove progress string
621
+ value = value.replace('"', "")
605
622
  str_label = str_label.replace("PROGRESS_LABEL", "").strip()
606
623
  self.progress_label.setText(str_label)
607
624
 
@@ -617,12 +634,12 @@ class MainWindow(QtWidgets.QMainWindow):
617
634
  def start_process(self):
618
635
  args = self.tool_widget.get_widgets_arguments()
619
636
  if not args:
620
- print('Please check the parameters.')
637
+ print("Please check the parameters.")
621
638
  return
622
639
 
623
640
  self.print_line_to_output("")
624
- self.print_line_to_output(f'Starting tool {self.tool_name} ... \n')
625
- self.print_line_to_output('-----------------------------')
641
+ self.print_line_to_output(f"Starting tool {self.tool_name} ... \n")
642
+ self.print_line_to_output("-----------------------------")
626
643
  self.print_line_to_output("Tool arguments:")
627
644
  self.print_line_to_output(json.dumps(args, indent=4))
628
645
  self.print_line_to_output("")
@@ -635,7 +652,7 @@ class MainWindow(QtWidgets.QMainWindow):
635
652
  if type(args[key]) is not str:
636
653
  args[key] = str(args[key])
637
654
 
638
- tool_type, tool_args = bt.run_tool(self.tool_api, args, self.custom_callback)
655
+ tool_type, tool_args = bt.prepare_tool_run(self.tool_api, args, self.custom_callback)
639
656
 
640
657
  if self.process is None: # No process running.
641
658
  self.print_line_to_output(f"Tool {self.tool_name} started")
@@ -646,7 +663,7 @@ class MainWindow(QtWidgets.QMainWindow):
646
663
  self.process.stateChanged.connect(self.handle_state)
647
664
 
648
665
  # Clean up once complete.
649
- self.process.finished.connect(self.process_finished)
666
+ self.process.finished.connect(self.process_finished)
650
667
  self.process.start(tool_type, tool_args)
651
668
 
652
669
  while self.process is not None:
@@ -667,10 +684,6 @@ class MainWindow(QtWidgets.QMainWindow):
667
684
  data = self.process.readAllStandardError()
668
685
  stderr = bytes(data).decode("utf8")
669
686
 
670
- # Extract progress if it is in the data.
671
- progress = simple_percent_parser(stderr)
672
- if progress:
673
- self.progress_bar.setValue(progress)
674
687
  self.message(stderr)
675
688
 
676
689
  def handle_stdout(self):
@@ -688,11 +701,11 @@ class MainWindow(QtWidgets.QMainWindow):
688
701
  QtCore.QProcess.Running: "Running",
689
702
  }
690
703
  state_name = states[state]
691
- if state_name == 'Not running':
704
+ if state_name == "Not running":
692
705
  self.btn_run.setEnabled(True)
693
706
  if self.cancel_op:
694
- self.message('Tool operation canceled')
695
- elif state_name == 'Starting':
707
+ self.message("Tool operation canceled")
708
+ elif state_name == "Starting":
696
709
  self.btn_run.setEnabled(False)
697
710
 
698
711
  def process_finished(self):
@@ -704,7 +717,32 @@ class MainWindow(QtWidgets.QMainWindow):
704
717
 
705
718
  def runner():
706
719
  app = QtWidgets.QApplication(sys.argv)
720
+
721
+ # Use absolute path for icons (works with embedded Python on Windows)
722
+ assets_path = Path(__file__).parent / "assets"
723
+ app.setWindowIcon(QtGui.QIcon(str(assets_path / "BERALogo.ico")))
707
724
  window = MainWindow()
708
725
  window.setMinimumSize(1024, 768)
709
726
  window.show()
710
- app.exec()
727
+
728
+ # Cross-platform tray icon (Windows: .ico, macOS: .png)
729
+ import platform
730
+ if platform.system() == "Darwin":
731
+ tray_icon_path = assets_path / "BERALogo.png"
732
+ else:
733
+ tray_icon_path = assets_path / "BERALogo.ico"
734
+ tray_icon = QtWidgets.QSystemTrayIcon(QtGui.QIcon(str(tray_icon_path)), app)
735
+ tray_icon.setToolTip("BERA Tools")
736
+ tray_icon.show()
737
+
738
+ def signal_ready():
739
+ ready_flag = os.getenv("BERA_SPLASH_READY")
740
+ if ready_flag:
741
+ try:
742
+ from pathlib import Path
743
+ Path(ready_flag).touch()
744
+ except Exception:
745
+ pass
746
+
747
+ QtCore.QTimer.singleShot(100, signal_ready)
748
+ app.exec_()
beratools/gui/main.py CHANGED
@@ -12,15 +12,12 @@ Description:
12
12
 
13
13
  Main entry point of BERA Tools.
14
14
  """
15
- import sys
16
- from pathlib import Path
17
-
18
- if __name__ == "__main__":
19
- current_file = Path(__file__).resolve()
20
- btool_dir = current_file.parents[2]
21
- sys.path.insert(0, btool_dir.as_posix())
22
15
 
23
16
  from beratools.gui.bt_gui_main import runner
24
17
 
18
+
19
+ def gui_main():
20
+ runner()
21
+
25
22
  if __name__ == "__main__":
26
23
  runner()