Python Forum
[Tkinter] Spawn sub-window with button press
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Spawn sub-window with button press
#1
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 so
self.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.
Reply
#2
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()
Reply
#3
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
Reply
#4
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
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Interaction between Matplotlib window, Python prompt and TKinter window NorbertMoussy 3 342 Mar-17-2024, 09:37 AM
Last Post: deanhystad
  Centering and adding a push button to a grid window, TKinter Edward_ 15 4,377 May-25-2023, 07:37 PM
Last Post: deanhystad
  [Tkinter] Clicking on the button crashes the TK window ODOshmockenberg 1 2,197 Mar-10-2022, 05:18 PM
Last Post: deanhystad
  .get() invoke after a button nested press iddon5 5 3,218 Mar-29-2021, 03:55 AM
Last Post: deanhystad
Question closing a "nested" window with a button in PySimpleGUI and repeating this process Robby_PY 9 13,414 Jan-18-2021, 10:21 PM
Last Post: Serafim
  tkinter touchscreen scrolling - button press makes unwanted scrolling nanok66 1 3,923 Dec-28-2020, 10:00 PM
Last Post: nanok66
  Closing window on button click not working kenwatts275 4 3,666 May-03-2020, 01:59 PM
Last Post: deanhystad
  Anytime I press the button, the result is depicted Jionni 2 2,185 Feb-24-2020, 10:08 AM
Last Post: Jionni
  tkinter window and turtle window error 1885 3 6,624 Nov-02-2019, 12:18 PM
Last Post: 1885
  [PySimpleGui] How to alter mouse click button of a standard submit button? skyerosebud 3 4,949 Jul-21-2019, 06:02 PM
Last Post: FullOfHelp

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020