[Tkinter] Spawn sub-window with button press - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [Tkinter] Spawn sub-window with button press (/thread-13702.html) |
Spawn sub-window with button press - malonn - Oct-27-2018 Hey, I'm trying to open a sub-window on top of my main window for a specific time, then close it. I'm going the tkinter.Toplevel route for this and plan on destroying it with subwindow.destroy() . But I keep getting an error that I have no idea what to do with. Code:class MainWindow(ttk.Frame): def __init__(self, parent): ttk.Frame.__init__(self) ttk.Frame.grid(self, column=0, row=0, sticky=('N', 'E', 'S', 'W')) ttk.Frame.columnconfigure(self, 0, weight=1) ttk.Frame.rowconfigure(self, 0, weight=1) self.mem_exists = tkinter.StringVar() self.reg_scan = tkinter.StringVar() self.widgets() def widgets(self): self.mem_labl1 = ttk.Label(self, text='Memory Dumps:') self.mem_labl1.grid(column=0, row=0) self.mem_labl2 = ttk.Label(self, textvariable=self.mem_exists) self.mem_labl2.grid(column=0, row=1) self.mem_buton = ttk.Button(self, text='Delete', command=self.delete_dmps) self.mem_buton.grid(column=0, row=2) self.dmp_separ = ttk.Separator(self, orient='horizontal') self.dmp_separ.grid(column=0, row=3, rowspan=1, sticky=('EW')) self.scn_labl1 = ttk.Label(self, text='Scan for Dumps') self.scn_labl1.grid(column=0, row=4) self.scn_buton = ttk.Button(self, text='Scan', command=self.scan_pressed) self.scn_buton.grid(column=0, row=5) self.vrt_separ = ttk.Separator(self, orient='vertical') self.vrt_separ.grid(column=1, row=0, rowspan=6, sticky=('NS')) self.reg_labl1 = ttk.Label(self, text='Unneeded Keys') self.reg_labl1.grid(column=2, row=0) self.reg_labl2 = ttk.Label(self, textvariable=self.reg_scan) self.reg_labl2.grid(column=2, row=1) self.reg_buton = ttk.Button(self, text='Delete', command=self.delete_keys) self.reg_buton.grid(column=2, row=2) ...... def scan_pressed(self): self.sub_win = tkinter.Toplevel(self, root) <----- ERROR HERE self.write_dumps() size = self.get_size() self.mem_exists.set(f'{size} KB of memory dumps found!')When I press the button (scn_buton) I want it to spawn a little window with a label that says "scanning..." (or something of that nature), but it gives me this error: Exception in Tkinter callback Traceback (most recent call last): File "C:\Python37\lib\tkinter\__init__.py", line 1705, in __call__ return self.func(*args) File "c:\users\mark\downloads\practice_gui_2.py", line 83, in scan_pressed self.sub_win = tkinter.Toplevel(self, root) File "C:\Python37\lib\tkinter\__init__.py", line 2334, in __init__ if wmkey in cnf: File "C:\Python37\lib\tkinter\__init__.py", line 1489, in cget return self.tk.call(self._w, 'cget', '-' + key) TypeError: can only concatenate str (not "int") to str I have no clue where to even begin on that one. Google results turn up things not related to tkinter. malonn Well, I figured out why the window wouldn't spawn. The fix was to remove the master from the call to Toplevel() like soself.sub_win = tkinter.Toplevel(self)but the window doesn't spawn until after the rest of the code for that method ("scan_pressed") is run. Why is that? The code calls os.walk which takes a while to complete and I want the second window to block it while it happens.
RE: Spawn sub-window with button press - woooee - Oct-27-2018 See if this makes more sense. It also runs without errors. import sys if 3 == sys.version_info[0]: ## 3.X is default if dual system import tkinter as tk ## Python 3.x from tkinter import ttk else: import Tkinter as tk ## Python 2.x import ttk class MainWindow(): def __init__(self, parent): self.parent=parent self.fr=ttk.Frame(parent) self.fr.grid(column=0, row=0, sticky='nsew') self.fr.columnconfigure(0, weight=1) self.fr.rowconfigure(0, weight=1) self.mem_exists = tk.StringVar() self.reg_scan = tk.StringVar() self.sub_win=None self.widgets() def delete_dmps(self): if self.sub_win: self.sub_win.destroy() def widgets(self): self.mem_labl1 = ttk.Label(self.fr, text='Memory Dumps:') self.mem_labl1.grid(column=0, row=0) self.mem_labl2 = ttk.Label(self.fr, textvariable=self.mem_exists) self.mem_labl2.grid(column=0, row=1) self.mem_buton = ttk.Button(self.fr, text='Delete', command=self.delete_dmps) self.mem_buton.grid(column=0, row=2) self.dmp_separ = ttk.Separator(self.fr, orient='horizontal') self.dmp_separ.grid(column=0, row=3, rowspan=1, sticky=('EW')) self.scn_labl1 = ttk.Label(self.fr, text='Scan for Dumps') self.scn_labl1.grid(column=0, row=4) self.scn_buton = ttk.Button(self.fr, text='Scan', command=self.scan_pressed) self.scn_buton.grid(column=0, row=5) self.vrt_separ = ttk.Separator(self.fr, orient='vertical') self.vrt_separ.grid(column=1, row=0, rowspan=6, sticky=('NS')) self.reg_labl1 = ttk.Label(self.fr, text='Unneeded Keys') self.reg_labl1.grid(column=2, row=0) self.reg_labl2 = ttk.Label(self.fr, textvariable=self.reg_scan) self.reg_labl2.grid(column=2, row=1) ## self.reg_buton = ttk.Button(self.fr, text='Delete', command=self.delete_keys) ## self.reg_buton.grid(column=2, row=2) def scan_pressed(self): self.sub_win = tk.Toplevel(self.parent) ##<----- ERROR HERE ## self.write_dumps() ## size = self.get_size() ## self.mem_exists.set(f'{size} KB of memory dumps found!') root=tk.Tk() MW=MainWindow(root) root.mainloop() RE: Spawn sub-window with button press - malonn - Oct-28-2018 I'm sorry, I didn't post the full program, just an excerpt of the problem I was having. Here's the full (working) program: import os import winreg import tkinter from tkinter import ttk import ctypes import sys class MainWindow(ttk.Frame): def __init__(self, parent): ttk.Frame.__init__(self) ttk.Frame.grid(self, column=0, row=0, sticky=('N', 'E', 'S', 'W')) ttk.Frame.columnconfigure(self, 0, weight=1) ttk.Frame.rowconfigure(self, 0, weight=1) self.mem_exists = tkinter.StringVar() self.reg_scan = tkinter.StringVar() self.widgets() def widgets(self): self.mem_labl1 = ttk.Label(self, text='Memory Dumps:') self.mem_labl1.grid(column=0, row=0) self.mem_labl2 = ttk.Label(self, textvariable=self.mem_exists) self.mem_labl2.grid(column=0, row=1) self.mem_buton = ttk.Button(self, text='Delete', command=self.delete_dmps) self.mem_buton.grid(column=0, row=2) self.dmp_separ = ttk.Separator(self, orient='horizontal') self.dmp_separ.grid(column=0, row=3, rowspan=1, sticky=('EW')) self.scn_labl1 = ttk.Label(self, text='Scan for Dumps') self.scn_labl1.grid(column=0, row=4) self.scn_buton = ttk.Button(self, text='Scan', command=self.scan_pressed) self.scn_buton.grid(column=0, row=5) self.vrt_separ = ttk.Separator(self, orient='vertical') self.vrt_separ.grid(column=1, row=0, rowspan=6, sticky=('NS')) self.reg_labl1 = ttk.Label(self, text='Unneeded Keys') self.reg_labl1.grid(column=2, row=0) self.reg_labl2 = ttk.Label(self, textvariable=self.reg_scan) self.reg_labl2.grid(column=2, row=1) self.reg_buton = ttk.Button(self, text='Delete', command=self.delete_keys) self.reg_buton.grid(column=2, row=2) def scan_dumps(self): '''Walks the OS drive and returns a dict with the path to and size of each .DMP file found on the drive.''' dumps = {} k = 0 for dpath, dname, fname in os.walk('C:\\'): for f_n in fname: if f_n.endswith('.dmp'): dumps[k] = [os.path.join(dpath, f_n), os.stat(os.path.join(dpath, f_n)).st_size] k += 1 return dumps def write_dumps(self): dumps = self.scan_dumps() with open(os.path.join(sys.path[0], 'practice_gui_2.ini'), 'w') as f: for key, value in dumps.items(): f.write(f'{value}\n') def read_dumps(self): dumps = {} k = 0 with open(os.path.join(sys.path[0], 'practice_gui_2.ini'), 'r') as f: for line in f: lst = line.split(',') pth = lst[0][2:-1] sze = lst[1][1:-2] dumps[k] = [os.path.normpath(pth), sze] k += 1 return dumps def get_size(self): '''Reads from ini created by "write_dumps()" and gets total size of all .DMP files to display in the GUI.''' dumps = self.read_dumps() tsize = 0 for key, value in dumps.items(): tsize += int(value[1]) tsize /= 1024 tsize = round(tsize) return format(tsize, ',') def scan_pressed(self): self.sub_win = tkinter.Toplevel(self) self.write_dumps() size = self.get_size() self.mem_exists.set(f'{size} KB of memory dumps found!') def get_profile_keys(self): '''Opens a specific registry key and creates a list of key + sub-key.''' reg_key = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Profiles' with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_key, 0, winreg.KEY_ALL_ACCESS | winreg.KEY_WOW64_64KEY) as k: ns = winreg.QueryInfoKey(k)[0] subs = [] for i in range(ns): subs.append(reg_key + '\\' + winreg.EnumKey(k, i)) return subs def get_newest_keys(self): '''Takes "get_profile_keys" and decodes the "DateCreated" registry value. Next finds the newest values per "DateCreated"and returns a list of the full registry key for the newest key(s).''' keys = self.get_profile_keys() stamp = [] stamps = {} for i in range(len(keys)): reg_key = keys[i] with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_key, 0, winreg.KEY_ALL_ACCESS) as k: val = winreg.QueryValueEx(k, 'DateCreated') stamp.append(int.from_bytes(val[0][:2], 'little')) stamp.append(int.from_bytes(val[0][2:4], 'little')) stamp.append(int.from_bytes(val[0][6:8], 'little')) stamp.append(int.from_bytes(val[0][8:10], 'little')) stamp.append(int.from_bytes(val[0][10:12], 'little')) stamp.append(int.from_bytes(val[0][12:14], 'little')) stamps[i] = stamp stamp = [] if len(stamps) == 1: return None elif len(stamps) == 2: z = 0 for x in stamps[0]: if x > stamps[1][z]: return keys[0] elif x < stamps[1][z]: return keys[1] z += 1 # add condition for more than two keys def delete_dmps(self): '''Walks the dict created by "scan_dumps()"and removes each file stored.''' dumps = self.read_dumps() for key, value in dumps.items(): path = value[0] os.remove(path) self.write_dumps() size = self.get_size() self.mem_exists.set(f'{size} KB of memory dumps found!') def delete_keys(self): '''Takes the list returned by "get_newest_keys" and deletes each key.''' delete = self.get_newest_keys() if delete is None: return None elif isinstance(delete, str): winreg.DeleteKey(winreg.HKEY_LOCAL_MACHINE, delete) else: for x in delete: winreg.DeleteKey(winreg.HKEY_LOCAL_MACHINE, x) def set_mem_label(self): size = self.get_size() self.mem_exists.set(f'{size} KB of memory dumps found!') def set_reg_label(self): keys = self.get_profile_keys() if len(keys) > 1: num = len(keys) - 1 self.reg_scan.set(f'{num} unwanted key(s) found!') else: self.reg_scan.set('No unwanted keys found!') if ctypes.windll.shell32.IsUserAnAdmin(): root = tkinter.Tk() gui = MainWindow(root) gui.set_mem_label() gui.set_reg_label() gui.mainloop() else: ctypes.windll.shell32.ShellExecuteW(None, 'runas', sys.executable, 'practice_gui_2.py', None, 1)It all works, I managed to get the sub-window to show. The problem is it doesn't show until after the rest of the method code runs. def scan_pressed(self): self.sub_win = tkinter.Toplevel(self) self.write_dumps() size = self.get_size() self.mem_exists.set(f'{size} KB of memory dumps found!')The first line in that code is to show the sub-window. Why doesn't the sub-window show first, then the rest of the code runs. I just want to window to pop up to distract the user while the methods are called (which could take time). Thanks for the help though,@woooee RE: Spawn sub-window with button press - malonn - Oct-28-2018 The problem has been solved through the use of wait_visibility() . Merely add this:def scan_pressed(self): self.sub_win = tkinter.Toplevel(self) self.sub_win.wait_visibility()and the window now opens before the rest of the code runs. malonn |