1
+ import time
2
+ import traceback
1
3
from kikit .defs import EDA_TEXT_HJUSTIFY_T , EDA_TEXT_VJUSTIFY_T
2
4
from pcbnewTransition import pcbnew , isV8
3
5
from kikit .panelize_ui_impl import loadPresetChain , obtainPreset , mergePresets
@@ -23,6 +25,7 @@ def run(self):
23
25
super ().run ()
24
26
except Exception as e :
25
27
self .exception = e
28
+ self .traceback = traceback .format_exc ()
26
29
27
30
def replaceExt (file , ext ):
28
31
return os .path .splitext (file )[0 ] + ext
@@ -46,41 +49,61 @@ def presetDifferential(source, target):
46
49
return result
47
50
48
51
49
- def transplateBoard (source , target ):
52
+ def transplateBoard (source , target , update = lambda x : None ):
53
+ CLEAR_MSG = "Clearing the old board in UI"
54
+ RENDER_MSG = "Rendering the new board in UI"
50
55
items = chain (
51
56
list (target .GetDrawings ()),
52
57
list (target .GetFootprints ()),
53
58
list (target .GetTracks ()),
54
59
list (target .Zones ()))
55
60
for x in items :
61
+ update (CLEAR_MSG )
56
62
target .Remove (x )
57
63
58
64
for x in list (target .GetNetInfo ().NetsByNetcode ().values ()):
65
+ update (CLEAR_MSG )
59
66
target .Remove (x )
60
67
68
+ update (RENDER_MSG )
69
+ target .SetProperties (source .GetProperties ())
70
+ update (RENDER_MSG )
71
+ target .SetPageSettings (source .GetPageSettings ())
72
+ update (RENDER_MSG )
73
+ target .SetTitleBlock (source .GetTitleBlock ())
74
+ if not isV8 ():
75
+ update (RENDER_MSG )
76
+ target .SetZoneSettings (source .GetZoneSettings ())
77
+
61
78
for x in source .GetDrawings ():
79
+ update (RENDER_MSG )
62
80
appendItem (target , x )
63
81
for x in source .GetFootprints ():
82
+ update (RENDER_MSG )
64
83
appendItem (target , x )
65
84
for x in source .GetTracks ():
85
+ update (RENDER_MSG )
66
86
appendItem (target , x )
67
87
for x in source .Zones ():
88
+ update (RENDER_MSG )
68
89
appendItem (target , x )
69
90
70
91
for n in [n for _ , n in source .GetNetInfo ().NetsByNetcode ().items ()]:
92
+ update (RENDER_MSG )
71
93
target .Add (n )
72
94
95
+ update (RENDER_MSG )
73
96
d = target .GetDesignSettings ()
74
97
d .CloneFrom (source .GetDesignSettings ())
75
98
76
- target .SetProperties (source .GetProperties ())
77
- target .SetPageSettings (source .GetPageSettings ())
78
- target .SetTitleBlock (source .GetTitleBlock ())
79
- if not isV8 ():
80
- target .SetZoneSettings (source .GetZoneSettings ())
99
+
81
100
82
101
def drawTemporaryNotification (board , sourceFilename ):
83
- bbox = findBoardBoundingBox (board )
102
+ try :
103
+ bbox = findBoardBoundingBox (board )
104
+ except Exception :
105
+ # If the output is empty...
106
+ bbox = pcbnew .BOX2I (pcbnew .VECTOR2I (0 , 0 ), pcbnew .VECTOR2I (0 , 0 ))
84
107
85
108
text = pcbnew .PCB_TEXT (board )
86
109
text .SetLayer (pcbnew .Margin )
@@ -278,6 +301,8 @@ def __init__(self, parent=None, board=None, preset=None):
278
301
279
302
self .board = board
280
303
self .dirty = False
304
+ self .progressDlg = None
305
+ self .lastPulse = time .time ()
281
306
282
307
topMostBoxSizer = wx .BoxSizer (wx .VERTICAL )
283
308
@@ -424,14 +449,49 @@ def OnResize(self):
424
449
def OnClose (self , event ):
425
450
self .EndModal (0 )
426
451
452
+ def _updatePanelizationProgress (self , message , force = False ):
453
+ self .phase = message
454
+ now = time .time ()
455
+
456
+ if now - self .lastPulse > 1 / 50 or force :
457
+ self .lastPulse = now
458
+ if self .progressDlg is not None :
459
+ self .progressDlg .Pulse (newmsg = f"Running KiKit: { self .phase } " )
460
+ if force :
461
+ self .progressDlg .Refresh ()
462
+ wx .GetApp ().Yield ()
463
+
464
+ def _panelizationRoutine (self , tempdir , input , panelFile , preset ):
465
+ panelize_ui .doPanelization (input , panelFile , preset )
466
+
467
+ # KiCAD 6 does something strange here, so we will load an empty
468
+ # file if we read it directly, but we can always make a copy and
469
+ # read that. Copying a file can be lengthy, so we will copy the
470
+ # file in a thread.
471
+ copyPanelName = os .path .join (tempdir , "panel-copy.kicad_pcb" )
472
+ shutil .copy (panelFile , copyPanelName )
473
+ try :
474
+ shutil .copy (replaceExt (panelFile , ".kicad_pro" ), replaceExt (copyPanelName , "kicad_pro" ))
475
+ shutil .copy (replaceExt (panelFile , ".kicad_prl" ), replaceExt (copyPanelName , "kicad_prl" ))
476
+ except FileNotFoundError :
477
+ # We don't care if we didn't manage to copy the files
478
+ pass
479
+ self .temporary_panel = pcbnew .LoadBoard (copyPanelName )
480
+
481
+ def _pulseWhilePcbnewRefresh (self ):
482
+ while not self .refreshDone :
483
+ time .sleep (1 / 50 )
484
+ self ._updatePanelizationProgress ("Pcbnew is updating the preview" )
485
+
486
+
427
487
def OnPanelize (self , event ):
428
488
with tempfile .TemporaryDirectory (prefix = "kikit" ) as dirname :
429
489
try :
430
- progressDlg = wx .ProgressDialog (
431
- "Running kikit" , "Running kikit, please wait " ,
490
+ self . progressDlg = wx .ProgressDialog (
491
+ "Running kikit" , f "Running KiKit: " ,
432
492
parent = self )
433
- progressDlg . Show ( )
434
- progressDlg .Pulse ()
493
+ self . _updatePanelizationProgress ( "Starting up" )
494
+ self . progressDlg .Show ()
435
495
436
496
args = self .kikitArgs ()
437
497
preset = obtainPreset ([], ** args )
@@ -458,43 +518,38 @@ def OnPanelize(self, event):
458
518
dlg .ShowModal ()
459
519
dlg .Destroy ()
460
520
return
461
- thread = ExceptionThread (target = panelize_ui .doPanelization ,
462
- args = (input , panelFile , preset ))
521
+
522
+ # We run as much as possible in a separate thread to not stall
523
+ # the UI...
524
+ thread = ExceptionThread (target = self ._panelizationRoutine ,
525
+ args = (dirname , input , panelFile , preset ))
463
526
thread .daemon = True
464
527
thread .start ()
465
528
while True :
466
- progressDlg . Pulse ( )
467
- thread .join (timeout = 1 )
529
+ self . _updatePanelizationProgress ( "Panelization" )
530
+ thread .join (timeout = 1 / 50 )
468
531
if not thread .is_alive ():
469
532
break
470
- if thread .exception and not isinstance ( thread . exception , NonFatalErrors ) :
533
+ if thread .exception :
471
534
raise thread .exception
472
- # KiCAD 6 does something strange here, so we will load
473
- # an empty file if we read it directly, but we can always make
474
- # a copy and read that:
475
- copyPanelName = os .path .join (dirname , "panel-copy.kicad_pcb" )
476
- shutil .copy (panelFile , copyPanelName )
477
- try :
478
- shutil .copy (replaceExt (panelFile , ".kicad_pro" ), replaceExt (copyPanelName , "kicad_pro" ))
479
- shutil .copy (replaceExt (panelFile , ".kicad_prl" ), replaceExt (copyPanelName , "kicad_prl" ))
480
- except FileNotFoundError :
481
- # We don't care if we didn't manage to copy the files
482
- pass
483
- panel = pcbnew .LoadBoard (copyPanelName )
484
- transplateBoard (panel , self .board )
535
+
536
+ # ...however, transplate board and pcbnew.Refresh has to happen
537
+ # in the main thread
538
+ transplateBoard (self .temporary_panel , self .board , self ._updatePanelizationProgress )
485
539
drawTemporaryNotification (self .board , panelFile )
540
+ self ._updatePanelizationProgress ("Pcbnew will now refresh panel, the UI might freeze" , force = True )
541
+ pcbnew .Refresh ()
542
+ self ._updatePanelizationProgress ("Done" , force = True )
486
543
self .dirty = True
487
- if thread .exception :
488
- raise thread .exception
489
544
except Exception as e :
490
545
dlg = wx .MessageDialog (
491
546
None , f"Cannot perform:\n \n { e } " , "Error" , wx .OK )
492
547
dlg .ShowModal ()
493
548
dlg .Destroy ()
494
549
finally :
495
- progressDlg .Hide ()
496
- progressDlg .Destroy ()
497
- pcbnew . Refresh ()
550
+ self . progressDlg .Hide ()
551
+ self . progressDlg .Destroy ()
552
+ self . progressDlg = None
498
553
499
554
def populateInitialValue (self , initialPreset = None ):
500
555
preset = loadPresetChain ([":default" ])
0 commit comments