1010from typing import Dict
1111import platform
1212import os
13+ import numpy
1314
1415if USE_OPENCV :
1516 # import cv2
2021
2122SCRIPT_DIR = os .path .dirname (os .path .realpath (__file__ ))
2223
24+ PLATFORM_ICON_PATH = None
2325if platform .system () == 'Windows' :
24- sg . set_global_icon ( f'{ SCRIPT_DIR } /assets/icon.ico' )
26+ PLATFORM_ICON_PATH = f'{ SCRIPT_DIR } /assets/icon.ico'
2527else :
26- sg .set_global_icon (f'{ SCRIPT_DIR } /assets/icon.png' )
28+ PLATFORM_ICON_PATH = f'{ SCRIPT_DIR } /assets/icon.png'
29+
30+ # Apply icon globally
31+ sg .set_global_icon (PLATFORM_ICON_PATH )
2732
2833CONF_TEXT_POE = ['ipTypeText' , 'ipText' , 'maskText' , 'gatewayText' , 'dnsText' , 'dnsAltText' , 'networkTimeoutText' , 'macText' ]
2934CONF_INPUT_POE = ['staticBut' , 'dynamicBut' , 'ip' , 'mask' , 'gateway' , 'dns' , 'dnsAlt' , 'networkTimeout' , 'mac' ]
@@ -38,27 +43,35 @@ def PrintException():
3843 filename = f .f_code .co_filename
3944 print ('Exception in {}, line {}; {}' .format (filename , lineno , exc_obj ))
4045
41- def check_ip (s : str , req = True ):
46+ def Popup (msg , window ):
47+ main_window_location = window .CurrentLocation ()
48+ main_window_size = window .size
49+ # Calculate the centered location for the new window
50+ centered_location = (main_window_location [0 ] + (main_window_size [0 ] - 50 ) // 2 ,
51+ main_window_location [1 ] + (main_window_size [1 ] - 50 ) // 2 )
52+ return sg .Popup (msg , location = centered_location )
53+
54+ def check_ip (window , s : str , req = True ):
4255 if s == "" :
4356 return not req
4457 spl = s .split ("." )
4558 if len (spl ) != 4 :
46- sg . Popup ("Wrong IP format.\n Value should be similar to 255.255.255.255" )
59+ Popup ("Wrong IP format.\n Value should be similar to 255.255.255.255" , window = window )
4760 return False
4861 for num in spl :
4962 if 255 < int (num ):
50- sg . Popup ("Wrong IP format.\n Values can not be above 255!" )
63+ Popup ("Wrong IP format.\n Values can not be above 255!" , window = window )
5164 return False
5265 return True
5366
54- def check_mac (s ):
67+ def check_mac (window , s ):
5568 if s .count (":" ) != 5 :
56- sg . Popup ("Wrong MAC format.\n Value should be similar to FF:FF:FF:FF:FF:FF" )
69+ Popup ("Wrong MAC format.\n Value should be similar to FF:FF:FF:FF:FF:FF" , window = window )
5770 return False
5871 for i in s .split (":" ):
5972 for j in i :
6073 if j > "F" or (j < "A" and not j .isdigit ()) or len (i ) != 2 :
61- sg . Popup ("Wrong MAC format.\n Value should be similar to FF:FF:FF:FF:FF:FF" )
74+ Popup ("Wrong MAC format.\n Value should be similar to FF:FF:FF:FF:FF:FF" , window = window )
6275 return False
6376 return True
6477
@@ -113,7 +126,7 @@ def wait(self):
113126
114127
115128class SelectIP :
116- def __init__ (self ):
129+ def __init__ (self , window ):
117130 self .ok = False
118131 layout = [
119132 [sg .Text ("Specify the custom IP of the OAK PoE\n camera you want to connect to" )],
@@ -124,15 +137,24 @@ def __init__(self):
124137 [sg .Submit (), sg .Cancel ()],
125138 ]
126139 self .window = sg .Window ("Specify IP" , layout , size = (300 ,110 ), modal = True , finalize = True )
140+
141+ main_window_location = window .CurrentLocation ()
142+ main_window_size = window .size
143+ new_window_size = self .window .size
144+ # Calculate the centered location for the new window
145+ centered_location = (main_window_location [0 ] + (main_window_size [0 ] - new_window_size [0 ]) // 2 ,
146+ main_window_location [1 ] + (main_window_size [1 ] - new_window_size [1 ]) // 2 )
147+ self .window .move (* centered_location )
148+
127149 def wait (self ):
128150 event , values = self .window .Read ()
129151 self .window .close ()
130- if str (event ) == "Cancel" or values is None or not check_ip (values ["ip" ]):
152+ if str (event ) == "Cancel" or values is None or not check_ip (self . window , values ["ip" ]):
131153 return False , ""
132154 return True , values ["ip" ]
133155
134156class SearchDevice :
135- def __init__ (self ):
157+ def __init__ (self , window ):
136158 self .infos = []
137159 layout = [
138160 [sg .Text ("Select an OAK camera you would like to connect to." , font = ('Arial' , 10 , 'bold' ))],
@@ -156,12 +178,22 @@ def __init__(self):
156178 [sg .Button ('Search' , size = (15 , 2 ), font = ('Arial' , 10 , 'bold' ))],
157179 ]
158180 self .window = sg .Window ("Select Device" , layout , size = (550 ,375 ), modal = True , finalize = True )
181+
182+ main_window_location = window .CurrentLocation ()
183+ main_window_size = window .size
184+ new_window_size = self .window .size
185+ # Calculate the centered location for the new window
186+ centered_location = (main_window_location [0 ] + (main_window_size [0 ] - new_window_size [0 ]) // 2 ,
187+ main_window_location [1 ] + (main_window_size [1 ] - new_window_size [1 ]) // 2 )
188+ self .window .move (* centered_location )
189+
159190 self .search_devices ()
160191
161192 def search_devices (self ):
162193 self .infos = dai .XLinkConnection .getAllConnectedDevices ()
163194 if not self .infos :
164- sg .Popup ("No devices found." )
195+ pass
196+ # sg.Popup("No devices found.")
165197 else :
166198 rows = []
167199 for info in self .infos :
@@ -257,43 +289,43 @@ def factoryReset(device: dai.DeviceInfo, type: dai.DeviceBootloader.Type):
257289
258290def connectAndStartStreaming (dev ):
259291
260- # OpenCV
261- if USE_OPENCV :
292+ with dai .Device (dev ) as d :
262293 # Create pipeline
263294 pipeline = dai .Pipeline ()
295+ # OpenCV
296+ if USE_OPENCV :
297+ camRgb = pipeline .create (dai .node .ColorCamera )
298+ camRgb .setIspScale (1 ,3 )
299+ videnc = pipeline .create (dai .node .VideoEncoder )
300+ videnc .setDefaultProfilePreset (camRgb .getFps (), videnc .Properties .Profile .MJPEG )
301+ xout = pipeline .create (dai .node .XLinkOut )
302+ xout .setStreamName ("mjpeg" )
303+ camRgb .video .link (videnc .input )
304+ videnc .bitstream .link (xout .input )
264305
265- camRgb = pipeline .create (dai .node .ColorCamera )
266- camRgb .setIspScale (1 ,3 )
267- videnc = pipeline .create (dai .node .VideoEncoder )
268- videnc .setDefaultProfilePreset (camRgb .getFps (), videnc .Properties .Profile .MJPEG )
269- xout = pipeline .create (dai .node .XLinkOut )
270- xout .setStreamName ("mjpeg" )
271- camRgb .video .link (videnc .input )
272- videnc .bitstream .link (xout .input )
273-
274- with dai .Device (pipeline , dev ) as d :
275306 while not d .isClosed ():
276307 mjpeg = d .getOutputQueue ('mjpeg' ).get ()
277308 frame = cv2 .imdecode (mjpeg .getData (), cv2 .IMREAD_UNCHANGED )
278309 cv2 .imshow ('Color Camera' , frame )
279310 if cv2 .waitKey (1 ) == ord ('q' ):
280311 cv2 .destroyWindow ('Color Camera' )
281312 break
282- else :
283- # Create pipeline (no opencv)
284- pipeline = dai .Pipeline ()
285- camRgb = pipeline .create (dai .node .ColorCamera )
286- camRgb .setIspScale (1 ,3 )
287- camRgb .setPreviewSize (camRgb .getIspSize ())
288- camRgb .setColorOrder (camRgb .Properties .ColorOrder .RGB )
289-
290- xout = pipeline .create (dai .node .XLinkOut )
291- xout .input .setQueueSize (2 )
292- xout .input .setBlocking (False )
293- xout .setStreamName ("color" )
294- camRgb .preview .link (xout .input )
295-
296- with dai .Device (pipeline , dev ) as d :
313+ else :
314+ camRgb = pipeline .create (dai .node .ColorCamera )
315+ camRgb .setIspScale (1 ,3 )
316+ firstSensor = d .getConnectedCameraFeatures ()[0 ]
317+ camRgb .setPreviewSize (firstSensor .width // 3 , firstSensor .height // 3 )
318+ camRgb .setColorOrder (camRgb .Properties .ColorOrder .RGB )
319+
320+ xout = pipeline .create (dai .node .XLinkOut )
321+ xout .input .setQueueSize (2 )
322+ xout .input .setBlocking (False )
323+ xout .setStreamName ("color" )
324+ camRgb .preview .link (xout .input )
325+
326+ # Start pipeline
327+ d .startPipeline (pipeline )
328+
297329 frame = d .getOutputQueue ('color' , 2 , False ).get ()
298330 width , height = frame .getWidth (), frame .getHeight ()
299331
@@ -540,7 +572,7 @@ class DeviceManager:
540572
541573 def __init__ (self ) -> None :
542574 self .window = sg .Window (title = "Device Manager" ,
543- icon = "assets/icon.png" ,
575+ icon = PLATFORM_ICON_PATH ,
544576 layout = layout ,
545577 size = (645 , 380 ),
546578 finalize = True # So we can do First search for devices
@@ -556,7 +588,7 @@ def isPoE(self) -> bool:
556588 return self .bl .getType () == dai .DeviceBootloader .Type .NETWORK
557589 except Exception as ex :
558590 PrintException ()
559- sg . Popup (f'{ ex } ' )
591+ Popup (f'{ ex } ' , self . window )
560592
561593 def isUsb (self ) -> bool :
562594 return not self .isPoE ()
@@ -577,7 +609,7 @@ def run(self) -> None:
577609 device = self .device
578610 if deviceStateTxt (device .state ) == "BOOTED" :
579611 # device is already booted somewhere else
580- sg . Popup ("Device is already booted somewhere else!" )
612+ Popup ("Device is already booted somewhere else!" , self . window )
581613 else :
582614 self .resetGui ()
583615 self .bl = connectToDevice (device )
@@ -588,7 +620,7 @@ def run(self) -> None:
588620 self .window .Element ('progress' ).update ("No device selected." )
589621 elif event == "Search" :
590622 self .getDevices () # Re-search devices for dropdown
591- selDev = SearchDevice ()
623+ selDev = SearchDevice (window = self . window )
592624 di = selDev .wait ()
593625 if di is not None :
594626 self .resetGui ()
@@ -601,7 +633,7 @@ def run(self) -> None:
601633 self .getConfigs ()
602634 self .unlockConfig ()
603635 elif event == "Specify IP" :
604- select = SelectIP ()
636+ select = SelectIP (window = self . window )
605637 ok , ip = select .wait ()
606638 if ok :
607639 self .resetGui ()
@@ -655,14 +687,14 @@ def run(self) -> None:
655687 print ("Factory reset cancelled." )
656688
657689 elif event == "Flash configuration" :
658- self .flashConfig ()
659- self .getConfigs ()
660- self .resetGui ()
661- if self .isUsb ():
662- self .unlockConfig ()
663- else :
664- self .devices .clear ()
665- self .window .Element ('devices' ).update ("Search for devices" , values = [])
690+ if self .flashConfig () is not None :
691+ self .getConfigs ()
692+ self .resetGui ()
693+ if self .isUsb ():
694+ self .unlockConfig ()
695+ else :
696+ self .devices .clear ()
697+ self .window .Element ('devices' ).update ("Search for devices" , values = [])
666698 elif event == "Clear configuration" :
667699 self .clearConfig ()
668700 self .getConfigs ()
@@ -677,17 +709,17 @@ def run(self) -> None:
677709 confJson = self .bl .readConfigData ()
678710 sg .popup_scrolled (confJson , title = 'Configuration' )
679711 except Exception as ex :
680- sg . popup (f'No existing config to view ({ ex } )' )
712+ Popup (f'No existing config to view ({ ex } )' , self . window )
681713
682714 elif event == "Flash application" :
683715 file = sg .popup_get_file ("Select .dap file" , file_types = (('DepthAI Application Package' , '*.dap' ), ('All Files' , '*.* *' )))
684716 flashFromFile (file , self .bl )
685717 elif event == "Remove application" :
686718 try :
687719 self .bl .flashClear ()
688- sg . popup (f'Successfully removed application' )
720+ Popup (f'Successfully removed application' , self . window )
689721 except Exception as ex :
690- sg .popup (f"Couldn't remove application ({ ex } )" )
722+ sg .popup (f"Couldn't remove application ({ ex } )" , self . window )
691723
692724 elif event .startswith ("_unique_configBtn" ):
693725 self .window ['-COL1-' ].update (visible = False )
@@ -713,7 +745,7 @@ def run(self) -> None:
713745
714746 elif event == "recoveryMode" :
715747 if recoveryMode (self .bl ):
716- sg . Popup (f'Device successfully put into USB recovery mode.' )
748+ Popup (f'Device successfully put into USB recovery mode.' , self . window )
717749 # Device will reboot, close previous and reset GUI
718750 self .closeDevice ()
719751 self .resetGui ()
@@ -872,6 +904,12 @@ def resetGui(self):
872904 self .window .Element ('commit' ).update ("-version-" )
873905 self .window .Element ('devState' ).update ("-state-" )
874906
907+ # Move back to 'About' page
908+ self .window ['-COL1-' ].update (visible = True )
909+ self .window ['-COL2-' ].update (visible = False )
910+ self .window ['-COL3-' ].update (visible = False )
911+ self .window ['-COL4-' ].update (visible = False )
912+
875913 def closeDevice (self ):
876914 if self .bl is not None :
877915 self .bl .close ()
@@ -885,7 +923,7 @@ def getDevices(self):
885923 deviceInfos = dai .XLinkConnection .getAllConnectedDevices ()
886924 if not deviceInfos :
887925 self .window .Element ('devices' ).update ("No devices" )
888- sg .Popup ("No devices found." )
926+ # sg.Popup("No devices found.")
889927 else :
890928 for deviceInfo in deviceInfos :
891929 deviceTxt = deviceInfo .getMxId ()
@@ -896,7 +934,7 @@ def getDevices(self):
896934 self .window .Element ('devices' ).update ("Select device" , values = listedDevices )
897935 except Exception as ex :
898936 PrintException ()
899- sg . Popup (f'{ ex } ' )
937+ Popup (f'{ ex } ' , window = self . window )
900938
901939 def flashConfig (self ):
902940 values = self .values
@@ -912,22 +950,22 @@ def flashConfig(self):
912950 try :
913951 if self .isPoE :
914952 if self .values ['staticBut' ]:
915- if check_ip (values ['ip' ]) and check_ip (values ['mask' ]) and check_ip (values ['gateway' ], req = False ):
953+ if check_ip (self . window , values ['ip' ]) and check_ip (self . window , values ['mask' ]) and check_ip (self . window , values ['gateway' ], req = False ):
916954 conf .setStaticIPv4 (values ['ip' ], values ['mask' ], values ['gateway' ])
917955 else :
918956 raise Exception ('IP or Mask missing using static IP configuration' )
919957 else :
920- if check_ip (values ['ip' ], req = False ) and check_ip (values ['mask' ], req = False ) and check_ip (values ['gateway' ], req = False ):
958+ if check_ip (self . window , values ['ip' ], req = False ) and check_ip (self . window , values ['mask' ], req = False ) and check_ip (self . window , values ['gateway' ], req = False ):
921959 conf .setDynamicIPv4 (values ['ip' ], values ['mask' ], values ['gateway' ])
922960
923961 conf .setDnsIPv4 (values ['dns' ], values ['dnsAlt' ])
924962 if values ['networkTimeout' ] != "" :
925963 if int (values ['networkTimeout' ]) >= 0 :
926964 conf .setNetworkTimeout (timedelta (seconds = int (values ['networkTimeout' ]) / 1000 ))
927965 else :
928- sg . Popup ("Values can not be negative!" )
966+ Popup ("Values can not be negative!" , window = self . window )
929967 if values ['mac' ] != "" :
930- if check_mac (values ['mac' ]):
968+ if check_mac (self . window , values ['mac' ]):
931969 conf .setMacAddress (values ['mac' ])
932970 else :
933971 conf .setMacAddress ('00:00:00:00:00:00' )
@@ -936,29 +974,34 @@ def flashConfig(self):
936974 if int (values ['usbTimeout' ]) >= 0 :
937975 conf .setUsbTimeout (timedelta (seconds = int (values ['usbTimeout' ]) / 1000 ))
938976 else :
939- sg . Popup ("Values can not be negative!" )
977+ Popup ("Values can not be negative!" , window = self . window )
940978 if values ['usbSpeed' ] != "" :
941979 conf .setUsbMaxSpeed (getattr (dai .UsbSpeed , values ['usbSpeed' ]))
942980
943981 success , error = self .bl .flashConfig (conf )
944982 if not success :
945- sg .Popup (f"Flashing failed: { error } " )
983+ Popup (f"Flashing failed: { error } " , window = self .window )
984+ return False
946985 else :
947- sg .Popup ("Flashing successful." )
986+ Popup ("Flashing successful." , window = self .window )
987+ return True
988+
948989 except Exception as ex :
949990 PrintException ()
950- sg .Popup (f'{ ex } ' )
991+ Popup (f'{ ex } ' , window = self .window )
992+
993+ return None
951994
952995 def clearConfig (self ):
953996 try :
954997 success , error = self .bl .flashConfigClear ()
955998 if not success :
956- sg . Popup (f"Clearing configuration failed: { error } " )
999+ Popup (f"Clearing configuration failed: { error } " , window = self . window )
9571000 else :
958- sg . Popup ("Successfully cleared configuration." )
1001+ Popup ("Successfully cleared configuration." , window = self . window )
9591002 except Exception as ex :
9601003 PrintException ()
961- sg . Popup (f'{ ex } ' )
1004+ Popup (f'{ ex } ' , window = self . window )
9621005
9631006
9641007app = DeviceManager ()
0 commit comments