Skip to content

Commit 05bacc8

Browse files
authored
Merge pull request #233 from mbway/status_bar
Status bar messages to indicate rendering and viewing progress
2 parents ec30579 + 8454b5e commit 05bacc8

File tree

6 files changed

+97
-60
lines changed

6 files changed

+97
-60
lines changed

cq_editor/main_window.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import sys
22

3-
from PyQt5.QtWidgets import (QLabel, QMainWindow, QToolBar, QDockWidget, QAction)
3+
from typing import Optional
4+
from PyQt5.QtCore import pyqtSlot
5+
from PyQt5.QtWidgets import (QApplication, QLabel, QMainWindow, QToolBar, QDockWidget, QAction)
46
from logbook import Logger
57
import cadquery as cq
68

@@ -65,6 +67,8 @@ def __init__(self,parent=None, filename=None):
6567

6668
self.restoreComponentState()
6769

70+
self.on_idle()
71+
6872
def closeEvent(self,event):
6973

7074
self.saveWindow()
@@ -202,7 +206,7 @@ def prepare_toolbar(self):
202206
self.toolbar = QToolBar('Main toolbar',self,objectName='Main toolbar')
203207

204208
for c in self.components.values():
205-
add_actions(self.toolbar,c.toolbarActions())
209+
add_actions(self.toolbar, c.toolbarActions())
206210

207211
self.addToolBar(self.toolbar)
208212

@@ -213,18 +217,25 @@ def prepare_statusbar(self):
213217

214218
def prepare_actions(self):
215219

220+
self.components['debugger'].sigRenderStarted \
221+
.connect(self.on_render_start)
216222
self.components['debugger'].sigRendered\
217223
.connect(self.components['object_tree'].addObjects)
218224
self.components['debugger'].sigTraceback\
219225
.connect(self.components['traceback_viewer'].addTraceback)
226+
self.components['debugger'].sigRendered \
227+
.connect(lambda _: self.on_idle())
228+
self.components['debugger'].sigTraceback \
229+
.connect(lambda _: self.on_idle())
230+
220231
self.components['debugger'].sigLocals\
221232
.connect(self.components['variables_viewer'].update_frame)
222233
self.components['debugger'].sigLocals\
223234
.connect(self.components['console'].push_vars)
224235

225-
self.components['object_tree'].sigObjectsAdded[list]\
226-
.connect(self.components['viewer'].display_many)
227-
self.components['object_tree'].sigObjectsAdded[list,bool]\
236+
self.components['object_tree'].sigObjectsAdded[list, list]\
237+
.connect(lambda objects, names: self.components['viewer'].display_many(objects, None, names))
238+
self.components['object_tree'].sigObjectsAdded[list, bool, list]\
228239
.connect(self.components['viewer'].display_many)
229240
self.components['object_tree'].sigItemChanged.\
230241
connect(self.components['viewer'].update_item)
@@ -239,6 +250,8 @@ def prepare_actions(self):
239250

240251
self.components['viewer'].sigObjectSelected\
241252
.connect(self.components['object_tree'].handleGraphicalSelection)
253+
self.components['viewer'].sigDisplayProgress \
254+
.connect(self.on_display_progress)
242255

243256
self.components['traceback_viewer'].sigHighlightLine\
244257
.connect(self.components['editor'].go_to_line)
@@ -344,6 +357,27 @@ def handle_filename_change(self, fname):
344357
new_title = fname if fname else "*"
345358
self.setWindowTitle(f"{self.name}: {new_title}")
346359

347-
if __name__ == "__main__":
360+
def on_idle(self):
361+
self.components['debugger'].set_rendering_state(False)
362+
self.set_status_message('Idle', '#000000')
348363

349-
pass
364+
@pyqtSlot()
365+
def on_render_start(self):
366+
self.components['debugger'].set_rendering_state(True)
367+
self.set_status_message('Rendering...', '#ff0000')
368+
369+
@pyqtSlot(int, int, str)
370+
def on_display_progress(self, current: int, total: int, name: Optional[str]):
371+
if current == total:
372+
self.on_idle()
373+
else:
374+
message = f'Displaying Shape {current + 1} / {total}'
375+
if name:
376+
message += f' ({name})'
377+
self.set_status_message(message, '#0000ff')
378+
379+
def set_status_message(self, message: str, color: str):
380+
self.statusBar().showMessage(message)
381+
self.statusBar().setStyleSheet(f'color: {color}')
382+
# required because rendering is currently done on the main thread
383+
QApplication.processEvents()

cq_editor/widgets/debugger.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class Debugger(QObject,ComponentMixin):
111111
])
112112

113113

114+
sigRenderStarted = pyqtSignal()
114115
sigRendered = pyqtSignal(dict)
115116
sigLocals = pyqtSignal(dict)
116117
sigTraceback = pyqtSignal(object,str)
@@ -263,6 +264,7 @@ def _cleanup_locals(self,module,injected_names):
263264

264265
@pyqtSlot(bool)
265266
def render(self):
267+
self.sigRenderStarted.emit()
266268

267269
seed(59798267586177)
268270
if self.preferences['Reload CQ']:
@@ -294,6 +296,11 @@ def render(self):
294296
sys.last_traceback = exc_info[-1]
295297
self.sigTraceback.emit(exc_info, cq_script)
296298

299+
def set_rendering_state(self, rendering):
300+
render_action = self._actions['Run'][0]
301+
render_action.setCheckable(rendering)
302+
render_action.setChecked(rendering)
303+
297304
@property
298305
def breakpoints(self):
299306
return [ el[0] for el in self.get_breakpoints()]

cq_editor/widgets/object_tree.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class ObjectTree(QWidget,ComponentMixin):
9797
{'name': 'Clear all before each run', 'type': 'bool', 'value': True},
9898
{'name': 'STL precision','type': 'float', 'value': .1}])
9999

100-
sigObjectsAdded = pyqtSignal([list],[list,bool])
100+
sigObjectsAdded = pyqtSignal([list, list],[list, bool, list])
101101
sigObjectsRemoved = pyqtSignal(list)
102102
sigCQObjectSelected = pyqtSignal(object)
103103
sigAISObjectsSelected = pyqtSignal(list)
@@ -201,6 +201,7 @@ def addLines(self):
201201

202202
origin = (0,0,0)
203203
ais_list = []
204+
names = []
204205

205206
for name,color,direction in zip(('X','Y','Z'),
206207
('red','lawngreen','blue'),
@@ -214,8 +215,9 @@ def addLines(self):
214215
ais=line))
215216

216217
ais_list.append(line)
218+
names.append(name)
217219

218-
self.sigObjectsAdded.emit(ais_list)
220+
self.sigObjectsAdded.emit(ais_list, names)
219221

220222
def _current_properties(self):
221223

@@ -248,6 +250,7 @@ def addObjects(self,objects,clean=False,root=None):
248250
self.removeObjects()
249251

250252
ais_list = []
253+
names = []
251254

252255
#remove empty objects
253256
objects_f = {k:v for k,v in objects.items() if not is_obj_empty(v.shape)}
@@ -266,13 +269,14 @@ def addObjects(self,objects,clean=False,root=None):
266269

267270
if child.properties['Visible']:
268271
ais_list.append(ais)
269-
272+
names.append(name)
273+
270274
root.addChild(child)
271275

272276
if request_fit_view:
273-
self.sigObjectsAdded[list,bool].emit(ais_list,True)
277+
self.sigObjectsAdded[list, bool, list].emit(ais_list, True, names)
274278
else:
275-
self.sigObjectsAdded[list].emit(ais_list)
279+
self.sigObjectsAdded[list, list].emit(ais_list, names)
276280

277281
@pyqtSlot(object,str,object)
278282
def addObject(self,obj,name='',options=None):
@@ -281,15 +285,15 @@ def addObject(self,obj,name='',options=None):
281285

282286
root = self.CQ
283287

284-
ais,shape_display = make_AIS(obj, options)
288+
ais, shape_display = make_AIS(obj, options)
285289

286290
root.addChild(ObjectTreeItem(name,
287291
shape=obj,
288292
shape_display=shape_display,
289293
ais=ais,
290294
sig=self.sigObjectPropertiesChanged))
291295

292-
self.sigObjectsAdded.emit([ais])
296+
self.sigObjectsAdded.emit([ais], [name])
293297

294298
@pyqtSlot(list)
295299
@pyqtSlot()
@@ -313,7 +317,7 @@ def stashObjects(self,action : bool):
313317
self.removeObjects()
314318
self.CQ.addChildren(self._stash)
315319
ais_list = [el.ais for el in self._stash]
316-
self.sigObjectsAdded.emit(ais_list)
320+
self.sigObjectsAdded.emit(ais_list, [''] * len(ais_list))
317321

318322
@pyqtSlot()
319323
def removeSelected(self):

cq_editor/widgets/traceback_viewer.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ def __init__(self,parent):
3636

3737
self.tree = TracebackTree(self)
3838
self.current_exception = QLabel(self)
39-
self.current_exception.setStyleSheet(\
40-
"QLabel {color : red; }");
39+
self.current_exception.setStyleSheet("QLabel {color : red; }");
4140

4241
layout(self,
4342
(self.current_exception,

cq_editor/widgets/viewer.py

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from PyQt5.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication, QAction
22

3+
from typing import Optional, List
34
from PyQt5.QtCore import pyqtSlot, pyqtSignal
45
from PyQt5.QtGui import QIcon
56

@@ -47,6 +48,7 @@ class OCCViewer(QWidget,ComponentMixin):
4748
IMAGE_EXTENSIONS = 'png'
4849

4950
sigObjectSelected = pyqtSignal(list)
51+
sigDisplayProgress = pyqtSignal(int, int, str)
5052

5153
def __init__(self,parent=None):
5254

@@ -179,31 +181,23 @@ def clear(self):
179181
context.PurgeDisplay()
180182
context.RemoveAll(True)
181183

182-
def _display(self,shape):
183-
184-
ais = make_AIS(shape)
185-
self.canvas.context.Display(shape,True)
186-
187-
self.displayed_shapes.append(shape)
188-
self.displayed_ais.append(ais)
189-
190-
#self.canvas._display.Repaint()
191-
192184
@pyqtSlot(object)
193-
def display(self,ais):
194-
195-
context = self._get_context()
196-
context.Display(ais,True)
197-
198-
if self.preferences['Fit automatically']: self.fit()
185+
def display(self, ais):
186+
self.display_many([ais])
199187

200188
@pyqtSlot(list)
201-
@pyqtSlot(list,bool)
202-
def display_many(self,ais_list,fit=None):
189+
@pyqtSlot(list, bool, list)
190+
def display_many(self, ais_list, fit: Optional[bool] = None, names: Optional[List] = None):
191+
if names is None:
192+
names = [None] * len(ais_list)
193+
assert len(ais_list) == len(names)
203194

204195
context = self._get_context()
205-
for ais in ais_list:
206-
context.Display(ais,True)
196+
num_objects = len(ais_list)
197+
for i, (ais, name) in enumerate(zip(ais_list, names)):
198+
self.sigDisplayProgress.emit(i, num_objects, name)
199+
context.Display(ais, True)
200+
self.sigDisplayProgress.emit(num_objects, num_objects, None)
207201

208202
if self.preferences['Fit automatically'] and fit is None:
209203
self.fit()
@@ -223,7 +217,8 @@ def update_item(self,item,col):
223217
def remove_items(self,ais_items):
224218

225219
ctx = self._get_context()
226-
for ais in ais_items: ctx.Erase(ais,True)
220+
for ais in ais_items:
221+
ctx.Erase(ais,True)
227222

228223
@pyqtSlot()
229224
def redraw(self):

0 commit comments

Comments
 (0)