0

I have the following python 2.7 snippet code from a much larger script.

def RefreshAction():
    execfile("SatTracker.py")

def CloseWindow():
    window.destroy()

button = tk.Frame(window)
button = tk.Button(text='Refresh Data', width=25,command=lambda:[CloseWindow(),RefreshAction()])
button.pack()
window.mainloop()

So my entire code produces a tkinter window with images, text and a button. Now the button is supposed to trigger the command to close the Tkinter window, then rerun the entire script, which it does just fine. When I click the refresh button again, something weird happens. The window doesn't close anymore, but the script still starts up, with this error though.

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\python2\lib\lib-tk\Tkinter.py", line 1541, in __call__
    return self.func(*args)
  File "SatTracker.py", line 294, in <lambda>
    button = tk.Button(text='Refresh Data', width=25,command=lambda:[CloseWindow(),RefreshAction()])
  File "C:/Users/Peter Kongstad/Dropbox/Geoscience/2. Masters/7. Semester/Delphini-1 - Software Part/GUI/Programming/SatTracker.py", line 291, in CloseWindow
    window.destroy()
  File "C:\ProgramData\Anaconda3\envs\python2\lib\lib-tk\Tkinter.py", line 1864, in destroy
    self.tk.call('destroy', self._w)
TclError: can't invoke "destroy" command:  application has been destroyed
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\python2\lib\lib-tk\Tkinter.py", line 1541, in __call__
    return self.func(*args)
  File "C:/Users/Peter Kongstad/Dropbox/Geoscience/2. Masters/7. Semester/Delphini-1 - Software Part/GUI/Programming/SatTracker.py", line 294, in <lambda>
    button = tk.Button(text='Refresh Data', width=25,command=lambda:[CloseWindow(),RefreshAction()])
  File "C:/Users/Peter Kongstad/Dropbox/Geoscience/2. Masters/7. Semester/Delphini-1 - Software Part/GUI/Programming/SatTracker.py", line 288, in RefreshAction
    execfile("SatTracker.py")
  File "SatTracker.py", line 297, in <module>
    window.mainloop()
  File "C:\ProgramData\Anaconda3\envs\python2\lib\lib-tk\Tkinter.py", line 1129, in mainloop
    self.tk.mainloop(n)
KeyboardInterrupt

Effectively stopping everything. Now I do not necessarily need it to close the old tkinter window, as long as when I click the button, the script runs again and starts updating the current window with the new updated data.

So either way to fix this is fine by me. Any suggestions?

Mars
  • 341
  • 1
  • 3
  • 12
  • You should be defining a method for your button's `command` option if there are multiple methods it needs to call, what you're doing is frowned upon. – Nae Dec 20 '17 at 21:11
  • I've updated it now. Sorry about that. I'm a real rookie at this python programming. So if my methods are bad, It is because I do not know better. If you can show me a better way. Then by all means, please do =) – Mars Dec 20 '17 at 21:16
  • Possible duplicate of [Restarting a self-updating python script](https://stackoverflow.com/questions/1750757/restarting-a-self-updating-python-script) – Nae Dec 20 '17 at 22:00
  • Why do you want to restart entire script? – Nae Dec 20 '17 at 22:01
  • 2
    First thing to do is stop using `lambda`. It is good for a certain type of problem but this isn't that type of problem. Create a proper function so that you can more easiliy debug it. – Bryan Oakley Dec 20 '17 at 22:06
  • I need to restart the whole thing cause it gets a TLE code from a website, then breaks that code up into smaller bits, which are then translated into precise current latitude and longitude coordinates of a satellite. Then these long/lat coordinates are projected onto a 3D globe and a 2D map, showing its current location. Then I run those coordinates against a Google Chrome webdriver that opens a live weathermap and google earth with these coordinates and snaps the images based on the long/lat. At the end all of these images are cropped, resized and snuck neatly into a Tkinter. – Mars Dec 20 '17 at 23:15
  • @Kongstad To my knowledge there isn't any reason to restart the script as opposed to using main function concept still. You can make the script do all those things from starting over, you can destroy, or even update your GUI based on those. Would you think it would be infeasible to do that for some reason? – Nae Dec 21 '17 at 11:55

2 Answers2

1

Don't destroy your application root window. If you destroy a window, its child widgets can no longer be used. Create a root window with Tk() and later use that one for calling .mainloop(). Then make the window you want a Toplevel one. You may close that one and recreate it again anytime.

Also I don't understand why you want to run again your whole script. You just want to call code that recreates the Toplevel (maybe that is because you don't have the Toplevel...)

progmatico
  • 4,714
  • 1
  • 16
  • 27
  • I need to restart the whole thing cause it gets a TLE code from a website, then breaks that code up into smaller bits, which are then translated into precise current latitude and longitude coordinates of a satellite. Then these long/lat coordinates are projected onto a 3D globe and a 2D map, showing its current location. Then I run those coordinates against a Google Chrome webdriver that opens a live weathermap and google earth with these coordinates and snaps the images based on the long/lat. At the end all of these images are cropped, resized and snuck neatly into a Tkinter. – Mars Dec 20 '17 at 23:16
  • You don't need to duplicate your comments. Comment the answer (that user is notified) and use '@' followed by the nickname of another user you want to notify. The original question poster is allways notified. – progmatico Dec 21 '17 at 23:28
  • I understand you need to restart the process, didn't doubt that. What I was saying is (and I think you already got it by now), that restarting some procedure doesn't imply that you restart a GUI. – progmatico Dec 21 '17 at 23:29
  • 1
    @Kongstad: Your GUI (presentation) should be decoupled from your business code (your process). The GUI is just something you get input from and update with results. You might even do everything with a Python script for the command-line, and generate image files without building a GUI. And be able to later build a GUI and glue it with your earlier code, if you wish. So for a GUI to restart a process, that does not imply the restart of the GUI itself. – progmatico Dec 21 '17 at 23:29
1

Based on this answer & comments the following script restarts script when button is pressed:

import os
import sys

import tkinter as tk

window = tk.Tk()

def restart():
    #CloseWindow()
    RefreshAction()

def RefreshAction():
    os.execl(sys.executable, *([sys.executable]+sys.argv))

def CloseWindow():
    window.destroy()
    #quit()

button = tk.Frame(window)
button = tk.Button(text='Refresh Data', width=25,command=restart)
button.pack()
window.mainloop()

Though on my end it errors as my username has space in it. All in all you should go like below code unless you have global variables or something:

import tkinter as tk

def restart(root):
    CloseWindow(root)
    RefreshAction()

def RefreshAction():
    main()

def CloseWindow(root):
    root.destroy()

def main():
    window = tk.Tk()
    button = tk.Frame(window)
    button = tk.Button(text='Refresh Data', width=25,
                        command= lambda arg=window: restart(arg))
    button.pack()
    window.mainloop()

main()

Also you probably want to see this question & answers about how to structure your tkinter programs. I use a very similar template to that of Bryan's.

Nae
  • 14,209
  • 7
  • 52
  • 79
  • I don't agree in following OP intention of restart its own whole script. But I will give +1 for your help in suggesting the 2nd link on tkinter program structure. – progmatico Dec 21 '17 at 23:36