Hello. I am using python tkinter to create interface for my application. I have made a very bad spaghetti code initially and managed to get it to work without using the mainloop. I would like to improve my previous code and get my program to work properly with tkinter mainloop.
I have created an application class :
i use some global variables inside my class because I have other functions outside the class that access to those variables.
my main code starts like that:
As you can see from the function above, i am calling Application_intro function which is handles my main GUI. One of the GUI widgets is the "new operation" button:
After pressing that button, the code executed is:
As you can see from the code above, the function deletes all previous canvas elements and calls new Application_intro. I just want to delete whatever happened during the previous task and start a fresh task. A fresg task is what I consider an idle state, The application intro creates a text entry and sits there untill I write something in the text box and press enter key
The issue is:
I can see that canvas.delete("all") properly deletes all canvas widgets and Application_Intro code is being called to generate a new widgets HOWEVER, it does not seem to start a "fresh" task. It continues to execute where its left
For example, if I am in the middle of a task:
The code below is my task:
I am not gonna go into details about what this code does, but it waits for me to "initiate" the next pick.That is being done when I toggle a sensor on ESP32 device. When this task is started, it prints me the message ("wait for next command"). When I execute new_operation() which supposed to clear canvas and start a fresh task, it continues to print "wait for next command" but that should not be the case. Becuase I want to cancel the current task and just go to the idle state where I the program waits for my textbox input
In short, I want to be able to press a button which would clear whole GUI, and and basically start a "new" tkinter GUI. But my current function does not stop the current mainloop even though it clears canvas and creates a new instance of Application_intro
I have created an application class :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
class Application(tk.Frame): flag_ota_button = 0 def __init__( self ,master = None ): super ().__init__(master) self .master = master def Create_canvas( self ,canvas_width,canvas_height): global canvas canvas = tk.Canvas( self .master,bg = 'ivory2' ,width = canvas_width,height = canvas_height) def Application_Intro( self ): print ( "starting new app" ) global label_RED global entry_input # define this as global so it can be read in the callback function #canvas = tk.Canvas(self.master,bg='ivory2',width=canvas_width,height=canvas_height) label1 = tk.Label(canvas,text = "SKENUOKITE BARKODA(GUID) ARBA DAIKTO RIVILINI KODA:" ,font = 'Helvetica 16 bold' ,bg = 'ivory2' ,foreground = "red" ) entry_input = tk.Entry(canvas) # entry = guid entry_input.focus_set() end_program_button = tk.Button(canvas, text = "Baigti" ,font = 'Helvetica 12 bold' , width = 20 , height = 2 , command = self .end_program) restart_program_button = tk.Button(canvas, text = "Restartuoti" ,font = 'Helvetica 12 bold' , width = 20 , height = 2 , command = self .Restart) ota_button = tk.Button(canvas, text = "Atnaujinti" ,font = 'Helvetica 12 bold' , width = 20 , height = 2 , command = self .OTA_gui) remove_item_button = tk.Button(canvas, text = "Isvalyti dezute" ,font = 'Helvetica 12 bold' , width = 20 , height = 2 , command = self .Remove_item_gui) new_operation_button = tk.Button(canvas, text = "Nauja operacija" ,font = 'Helvetica 12 bold' , width = 20 , height = 2 , command = self .New_operation) canvas.create_text( 960 , 20 ,text = "PICK TO LIGHT" ,font = 'Helvetica 16 bold' ) label_RED = canvas.create_window( 960 , 75 ,window = label1) self .user_input_entry = canvas.create_window( 960 , 125 ,window = entry_input) canvas.create_window( 1210 , 200 ,window = ota_button) canvas.create_window( 960 , 200 ,window = remove_item_button) canvas.create_window( 710 , 200 ,window = new_operation_button) canvas.create_window( 1210 , 400 ,window = end_program_button) canvas.create_window( 710 , 400 ,window = restart_program_button) entry_input.bind( '<Return>' ,read_user_input) canvas.pack() def picking_gui( self ): self .devices = {} device_list,serial_list = count_unique_devices(myConnection) print ( "the list of devices=" ,device_list) img_red_original = Image. open ( "camera_red.png" ) img_red_original = img_red_original.resize(( 50 , 50 ),Image.ANTIALIAS) self .img_red = ImageTk.PhotoImage(img_red_original) img_green_original = Image. open ( "camera_green.png" ) img_green_original = img_green_original.resize(( 50 , 50 ),Image.ANTIALIAS) self .img_green = ImageTk.PhotoImage(img_green_original) red_image = canvas.create_image( 100 , 50 ,image = self .img_red,anchor = 'nw' ) self .update_RED_label(label_RED, 960 , 75 , "Imkite daiktus is nurodytu dezuciu" ) gaminio_kodas_text = "Gaminio kodas=" + user_input gaminio_serial_text = "Serial=" + komplektacijos_Serial gaminio_IMEI_Text = "IMEI=" + komplektacijos_Imei canvas.create_text( 960 , 400 ,text = gaminio_kodas_text,font = 'Helvetica 12 bold' ) canvas.create_text( 960 , 425 ,text = gaminio_serial_text,font = 'Helvetica 12 bold' ) canvas.create_text( 960 , 450 ,text = gaminio_IMEI_Text,font = 'Helvetica 12 bold' ) for x in range ( 0 , len (device_list)): self .devices[device_list[x]] = canvas.create_rectangle( 20 + (x * 110 ), 550 , 110 + (x * 110 ), 640 ,fill = 'red' ) canvas.create_text( 65 + (x * 110 ), 585 ,text = device_list[x],font = 'Helvetica 12 bold' ,fill = "yellow" ) canvas.create_text( 65 + (x * 110 ), 605 ,text = serial_list[x],font = 'Helvetica 10 bold' ,fill = "yellow" ) if (x> 15 ): self .devices[device_list[x]] = canvas.create_rectangle( 20 + (x * 110 ), 650 , 110 + (x * 110 ), 740 ,fill = 'red' ) canvas.create_text( 55 + (x * 90 ), 585 ,text = device_list[x],font = 'Helvetica 12 bold' ,fill = "yellow" ) canvas.create_text( 65 + (x * 110 ), 605 ,text = serial_list[x],font = 'Helvetica 10 bold' ,fill = "yellow" ) canvas.pack() def update_rectangle_color_initial( self ,device_name): canvas.itemconfig( self .devices[device_name],fill = "green" ) self .last_device = device_name def update_rectangle_color_last( self ): canvas.itemconfig( self .devices[ self .last_device],fill = "red" ) def update_rectangle_color( self ,device_name): canvas.itemconfig( self .devices[ self .last_device],fill = "red" ) canvas.itemconfig( self .devices[device_name],fill = "green" ) self .last_device = device_name def refresh_app( self ,delay): #refresh_variables() canvas.delete( "all" ) app.after(delay, lambda : self .Application_Intro()) def update_MAIN_label( self ,label,x,y,text1): global label_MAIN canvas.delete(label_MAIN) label_MAIN = canvas.create_text(x,y,text = text1,font = "Helvetica 14 bold" ) def update_RED_label( self ,label,x,y,text1): global label_RED canvas.delete(label_RED) label_RED = canvas.create_text(x,y,text = text1,fill = "red" ,font = 'Helvetica 16 bold' ) def make_new_textbox( self ,text_input): global label_RED global new_textbox_entry canvas.delete(label_RED) canvas.delete( self .user_input_entry) label = tk.Label(canvas,text = text_input,bg = 'ivory2' ,foreground = "red" ,font = 'Helvetica 16 bold' ) new_textbox_entry = tk.Entry(canvas) new_textbox_entry.focus_set() label_RED = canvas.create_window( 960 , 75 ,window = label) self .user_input_entry = canvas.create_window( 960 , 125 ,window = new_textbox_entry) new_textbox_entry.bind( '<Return>' ,read_user_new_textbox) def end_program( self ): print ( "end program" ) def Restart( self ): print ( "restart" ) restart_devices(myConnection, "RESTART" ) self .refresh_app( 1000 ) def OTA_gui( self ): print ( "OTA gui" ) def Remove_item_gui( self ): print ( "remove item gui" ) def New_operation( self ): print ( "new_operation" ) restart_devices(myConnection, "reset" ) canvas.delete( "all" ) app.after( 1000 , self .Application_Intro) print ( "new operation end" ) def OTA_gui( self ): self .devices = [] if ( self .flag_ota_button % 2 ! = 0 ): canvas.delete(ota_label) canvas.delete(ota_choices) canvas.delete(ota_button) self .flag_ota_button + = 1 return else : self .devices = [ "device1" , "device2" , "device3" , "device4" , "device5" , "device6" , "device7" , "device8" , "device9" ] device_list,serial_list = count_unique_devices(myConnection) #get list of unique device names from MYSQL DB print ( "device list OTA=" ,device_list) print ( "sertial list OTA=" ,serial_list) label = tk.Label(canvas,text = "Pasirinkti kuria dezute atnaujinti" ,font = 'Helvetica 12 bold' ) choices = ttk.Combobox(canvas,values = self .devices) choices.current( 0 ) button = tk.Button(canvas,text = "Patvirtinti" ,command = lambda : self .device_confirmed(choices, "ota" )) self .ota_label = canvas.create_window( 960 , 250 ,window = label) self .ota_choices = canvas.create_window( 960 , 300 ,window = choices) self .ota_button = canvas.create_window( 960 , 350 ,window = button) self .flag_ota_button + = 1 def device_confirmed( self ,choices,status): device = choices.get() if (status = = "ota" ): print ( "device=" ,device) mqttc.publish(device + "/status" , "OTA" ) canvas.delete( self .ota_label) canvas.delete( self .ota_choices) canvas.delete( self .ota_button) else : publish_mqtt_json_item_inside(" ",device," "," ") canvas.delete(remove_item_label) canvas.delete(remove_item_choices) canvas.delete(remove_item_button) remove_item_from_device(myConnection,device) |
my main code starts like that:
1 2 3 4 5 6 7 8 9 |
master = tk.Tk() app = Application(master = master) app.Create_canvas( 1920 , 1080 ) app.Application_Intro() app.mainloop() |
After pressing that button, the code executed is:
1 2 3 4 5 6 7 8 9 10 11 |
def New_operation( self ): print ( "new_operation" ) restart_devices(myConnection, "reset" ) canvas.delete( "all" ) app.after( 1000 , self .Application_Intro) print ( "new operation end" ) |
The issue is:
I can see that canvas.delete("all") properly deletes all canvas widgets and Application_Intro code is being called to generate a new widgets HOWEVER, it does not seem to start a "fresh" task. It continues to execute where its left
For example, if I am in the middle of a task:
The code below is my task:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
def handle_next_pick(): print ( "handling next pick" ) global item_number global flag_next_device print ( "pick counter =" ,pick_counter) if (pick_counter < Counter_global): if (flag_next_device> 0 ): print ( "flag_next_device is higher than 1" ) if (item_number > 0 ): #only check if it divides by counter if its higher then 0 to avoid glitch if (item_number % Counter_global_temp = = 0 ): item_number = item_number - (Counter_global_temp) app.update_rectangle_color(Device_global_temp[item_number]) string = Device_global_temp[item_number] + "/" + "status" mqttc.publish(string, "ACTIVE" , 1 ) flag_next_device = 0 if (update_DB = = 1 ): update_DB_Quantity(myConnection) else : print ( "Dont need to update DB" ) app.after( 100 ,handle_next_pick) else : app.after( 100 ,handle_next_pick) print ( "wait for next command" ) else : app.after( 1000 ,handle_end_of_picking) |
In short, I want to be able to press a button which would clear whole GUI, and and basically start a "new" tkinter GUI. But my current function does not stop the current mainloop even though it clears canvas and creates a new instance of Application_intro