1

Immediately during the first ~1 sec after I've landed on a new page in my GUI, I'm seeing numerous visual glitches before the window arrives at the proper layout (before/after screenshots below).

UPDATE:

The below code will give the desired error. I feel like the initial frame is being merged with the next frame upon calling the show_frame function, and posit that the initial frame must actually be manually hidden from view before preloading (hiding/loading) the next frame. Any help on this is greatly appreciated - and thank you to all who have taken a look so far.

import tkinter as Tk
from tkinter import font as tkfont
from tkinter import filedialog, ttk #browse directories; widget support   

class Manifold(Tk.Tk):
    def __init__(self, *args, **kwargs):
        Tk.Tk.__init__(self, *args, **kwargs)
    #custom font options:
        self.title1_font = tkfont.Font(family='Helvetica',size=13) #normal type
#customized ttk GUI theme:
        GUItheme = ttk.Style()
        GUItheme.theme_use('alt')

        container = Tk.Frame(self) 
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F,geometry,title,options,wait in zip((StartPage,PageOne),
                                    ("532x279","528x270"),
                                    ("","Experimental Data"),
                                    ((False,False),(True,False)),
                                    (100,100)):
            page_name = F.__name__
            frame = F(container, self)
            self.frames[page_name] = (frame,geometry,title,options,wait) #puts all pages in stacked order
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame("StartPage")

    def show_frame(self, page_name): #show a frame for the given page name
        frame, geometry, title, options, wait = self.frames[page_name]
        self.geometry(geometry) #changes between window sizes
        self.title(title) #changes titles of windows
        self.resizable(*options) #changes ability of user to resize each window
        self.withdraw()
        frame.tkraise() #raises window to top
        self.after(wait,self.deiconify) #preload page before viewing
        self.update_idletasks()

class StartPage(Tk.Frame):
    def __init__(self, parent, controller):
        Tk.Frame.__init__(self, parent)
        self.controller = controller
        self.configure(background='black') #set window background color

    #page one button:
        button1 = ttk.Button(self, text="Page 1",
                         command=lambda: controller.show_frame("PageOne"))
        button1.grid(row=0,column=0,sticky='W')

class PageOne(Tk.Frame):
    def __init__(self, parent, controller):
        Tk.Frame.__init__(self, parent)
        self.controller = controller
        self.configure(background='gray15') #set window background color
        self.grid_columnconfigure(0,weight=1000) #for resizing window horizontally

    #average volume filename:
        self.label1 = Tk.Label(self, text="Average Volume Filename:",fg="gray90",bg="gray25",font=controller.title1_font)
        self.label1.grid(row=0,column=0,ipadx=10,ipady=0,sticky='W')
        self.label1.config(height=1)
        self.entry1 = Tk.Entry(self,textvariable=Tk.StringVar(),highlightbackground="black",width=50)
        self.entry1.grid(row=1,column=0,sticky="WE")
        self.entry1.insert(0," .mrc, .spi")

        self.entry1.configure(state="disabled") #prevent typing
        self.browse1 = ttk.Button(self,text="Browse",
                             command=self.browse_button1)
        self.browse1.grid(row=1,column=1,sticky='W')

    #gathers volume input:
    def browse_button1(self): 
        self.entry1.configure(state="normal") #unlocks typing for program
        self.label1.config(fg="gray90",bg='gray25') #standard colors, or reset on additional wrong input
        content_initial = self.entry1.get()

if __name__ == "__main__":
    app = Manifold()
    app.mainloop()

Page One: enter image description here

Transition: enter image description here

Page Two:

enter image description here

evambivalence
  • 113
  • 1
  • 12

3 Answers3

1

Okay, I've figured it out. Using my above code (in the Update section of my initial question), I made two changes. The first is right under the main Class structure:

class Manifold(Tk.Tk):
    def __init__(self, *args, **kwargs):
        Tk.Tk.__init__(self, *args, **kwargs)
    #preload windows (avoids watching widgets load in real time):
        self.withdraw() #hide window
        self.after(0,self.deiconify) #unhide window asap

The second is in the show_frame loop, with the preceding wait tuple (within the chunk starting with for F, geometry,etc. set to (10,10,10,10):

    def show_frame(self, page_name): #show a frame for the given page name
        frame, geometry, title, scaling, wait = self.frames[page_name]
        self.quit() #seems to be important in exiting out of previous window entirely
        self.geometry(geometry) #changes between window sizes
        self.title(title) #changes titles of windows
        self.resizable(*scaling) #changes ability of user to resize each window
        self.withdraw()
        frame.tkraise() #raises window to top
        self.after(wait,self.deiconify) #preload page before viewing
        self.update_idletasks()

If anyone else ends up running into something like this, I hope this helps!

evambivalence
  • 113
  • 1
  • 12
0

Hide your Toplevel or Tk instance(s) that is/are glitchy until after a certain amount of time has passed:

try:                        # In order to be able to import tkinter for
    import tkinter as tk    # either in python 2 or in python 3
except:
    import Tkinter as tk


def hide(*toplevel_or_tk_instance_s_):
    for each in toplevel_or_tk_instance_s_:
        each.withdraw()


def show(*toplevel_or_tk_instance_s_):
    for each in toplevel_or_tk_instance_s_:
        each.iconify()
        each.deiconify()


if __name__ == '__main__':
    root = tk.Tk()
    my_toplevel1 = tk.Toplevel(root, bg='red')
    my_toplevel2 = tk.Toplevel(root, bg='green')
    hide(root, my_toplevel1, my_toplevel2)
    root.after(1000, show, root)
    root.after(2000, show, my_toplevel1)
    root.after(3000, show, my_toplevel2)
    root.mainloop()
Nae
  • 14,209
  • 7
  • 52
  • 79
  • I'm unable to get this to work in my hierarchy. I've added an update to my above post though, inspired by your advice. It's working (no buttons moving around), but I'm now getting a half-second of a crazy tv-fuzz flash on each page turn (the visual glitch looks like a mix between the menu screen and the screen appearing at the same time, in stripes). Ugh. – evambivalence Jan 29 '18 at 00:43
0

Remove all calls to update until your are ready for your UI to be visible, and don't use after(0, ...) because the zero means that code will run before tkinter has had a chance to process any other events, including requests to redraw portions of the screen.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thanks for the advice. I've updated my code and attached specific pictures of the new error, "tv static" between pages. It looks like the previous frame is literally merging in some weird combination with the next frame. How could I write this loop in my top level Class to first completely remove page one before showing page two? @Bryan Oakley – evambivalence Jan 29 '18 at 13:32
  • 1
    @evambivalence: unless you provide us code that can duplicate the behavior, all we can do is guess. – Bryan Oakley Jan 29 '18 at 13:35
  • Very true - I've added a portion of my code above that will fully duplicate the error. Please let me know if you're not seeing the effect on your screen. @Bryan Oakley – evambivalence Jan 29 '18 at 14:02