Reputation: 33
Introduction: My Python Tkinter application is designed to have a scrollbar on the side so that if the window is resized, the application can still be viewed via the scrollbar. I do this by putting a frame with all my content inside a canvas, with a scrollbar controlling the canvas. On window resize, I have a function called resizeCanvas which resizes the canvas.
Problem: After I resize the window, the scrollbar sort of works but it seems to jump around and fidget like its having a seizure. However, at initialization the scroll bar works smoothly. So resizing the window seems to be the problem. Any suggestions on why the scroll bar is behaving like this?
Application Hierarchy:
NOTE: Python 2.7.2
Code Snippit:
myframe=Frame(root,width=winx,height=winy)
myframe.place(x=0,y=0)
canvas = Tkinter.Canvas(myframe,width=winx,height=winy)
frame = Frame(canvas,width=winx,height=winy)
myscrollbar=Scrollbar(myframe,orient="vertical")
myscrollbar.configure(command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side="left",fill="y")
canvas.pack(side="left")
canvas.create_window((0,0),window=frame,anchor='nw')
frame.bind("<Configure>", initializeCanvas)
bind("<Configure>", resizeCanvas)
def initializeCanvas(self, event):
canvas.configure(scrollregion=self.canvas.bbox("all"),width=winx,height=winy)
def resizeCanvas(self, event):
update()
cwidth = self.winfo_width()
cheight = self.winfo_height()
canvas.config(width=cwidth, height=cheight)
Entire Code:
import sys
#external python files are in the includes folder
sys.path.insert(0, 'includes')
import webbrowser
import os
import Tkinter
import tkFileDialog
import tkMessageBox
import ConfigParser
from ttk import *
import tkFont
import Tix
# configurations held in this variable
config = ConfigParser.RawConfigParser()
# pady padding for create buttons
py = 15
# padx padding for create buttons
px = 5
# padding on left side for indenting elements
indentx = 25
winx = 815
winy = 515
# array to hold features
FEATURES =['']
# wrapper class for GUI
class AppTk(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.settings = "./settings/settings.cfg"
self.initialize_gui()
self.minsize(winx, 100)
sizex = winx
sizey = winy
posx = 100
posy = 100
self.wm_geometry("%dx%d+%d+%d" % (sizex, sizey, posx, posy))
try:
self.iconbitmap('./imgs/favicon.ico')
except Exception, e:
print "\n****Error occurred (GUI_MBD_File_Creator.init): favicon not found!"
# Setup grid of elements in GUI
# action: on start of application
def initialize_gui(self):
# ----------------------------------------------------
# START Settings frame initialization
# ----------------------------------------------------
self.myframe=Frame(self,width=winx,height=winy)
self.myframe.place(x=0,y=0)
self.canvas = Tkinter.Canvas(self.myframe,width=winx,height=winy)
self.frame = Frame(self.canvas,width=winx,height=winy)
self.myscrollbar=Scrollbar(self.myframe,orient="vertical")
self.myscrollbar.configure(command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.myscrollbar.set)
self.myscrollbar.pack(side="left",fill="y")
self.canvas.pack(side="left")
self.canvas.create_window((0,0),window=self.frame,anchor='nw')
self.frame.bind("<Configure>",self.initializeCanvas)
self.bind("<Configure>",self.resizeCanvas)
frameFont = tkFont.Font(size=13, weight=tkFont.BOLD)
self.frameSettings = Tkinter.LabelFrame(self.frame, text="Settings: fill these out first", relief="groove", borderwidth="3", font=frameFont)
self.frameSettings.grid(sticky="EW", padx=px, pady=(5,15))
labelSpreadsheet = Label(self.frameSettings, text="1. Spreadsheet Path:")
labelSpreadsheet.grid(row=0, column=0, sticky='W', padx=(indentx,0))
variableSpreadsheet = Tkinter.StringVar()
self.entrySpreadsheet = Entry(self.frameSettings, textvariable=variableSpreadsheet, width=90, state=Tkinter.DISABLED)
self.entrySpreadsheet.grid(row=0, column=1, sticky='W', padx=px, pady=5)
self.entrySpreadsheet.svar = variableSpreadsheet
buttonSpreadsheet = Button(self.frameSettings, text="Browse...")
buttonSpreadsheet.grid(row=0, column=2, sticky='W', padx=(0,10))
labelPath = Label(self.frameSettings, text="2. Root Save Path:")
labelPath.grid(row=1, column=0, sticky='W', padx=(indentx,0))
variablePath = Tkinter.StringVar()
self.entryPath = Entry(self.frameSettings, textvariable=variablePath, width=90, state=Tkinter.DISABLED)
self.entryPath.grid(row=1, column=1, sticky='W', padx=px, pady=(5,10))
self.entryPath.svar = variablePath
buttonPath = Button(self.frameSettings, text="Browse...")
buttonPath.grid(row=1, column=2, sticky='W', padx=(0,10), pady=(0,5))
# ----------------------------------------------------
# START Creation Menu frame initialization
# ----------------------------------------------------
self.frameCreationIndividual = Tkinter.LabelFrame(self.frame, text="Feature Files Creation Menu", relief="groove", borderwidth="3", font=frameFont)
self.frameCreationIndividual.grid(sticky="EW", padx=px, pady=(5,15))
labelReq = Label(self.frameCreationIndividual, text="3. Feature(s):")
labelReq.grid(row=0, column=0, sticky='NW', pady=(5,0), padx=(indentx,15))
self.scrollbarReq = Scrollbar(self.frameCreationIndividual)
self.scrollbarReq.grid(row=1, column=3, rowspan=16, sticky="NSW", pady=(0,15),padx=(0,20))
variableSelectAll = Tkinter.IntVar()
self.checkSelectAll = Checkbutton(self.frameCreationIndividual, text = "Select All", variable = variableSelectAll, onvalue = 1, offvalue = 0)
self.checkSelectAll.grid(row=0, column=1, columnspan=2, sticky='NE', padx=px, pady=(5,0))
self.checkSelectAll.svar = variableSelectAll
labelReq = Label(self.frameCreationIndividual, text="4. Files:")
labelReq.grid(row=0, column=5, sticky='NW', pady=(5,0), padx=15)
variableIndividualFFS = Tkinter.IntVar()
self.checkIndividualFFS = Checkbutton(self.frameCreationIndividual, text = "Create Feature File", variable = variableIndividualFFS, onvalue = 1, offvalue = 0)
self.checkIndividualFFS.grid(row=1, column=5, sticky='NW', padx=15)
self.checkIndividualFFS.svar = variableIndividualFFS
variableIndividualSFS = Tkinter.IntVar()
self.checkIndividualSFS = Checkbutton(self.frameCreationIndividual, text = "Create SubFeature Files", variable = variableIndividualSFS, onvalue = 1, offvalue = 0)
self.checkIndividualSFS.grid(row=2, column=5, sticky='NW', padx=15)
self.checkIndividualSFS.svar = variableIndividualSFS
variableIndividualDO = Tkinter.IntVar()
self.checkIndividualDO = Checkbutton(self.frameCreationIndividual, text = "Create Doc Outline", variable = variableIndividualDO, onvalue = 1, offvalue = 0)
self.checkIndividualDO.grid(row=3, column=5, sticky='NW', padx=15)
self.checkIndividualDO.svar = variableIndividualDO
variableIndividualDWR = Tkinter.IntVar()
self.checkIndividualDWR = Checkbutton(self.frameCreationIndividual, text = "Create Doc With Requirements", variable = variableIndividualDWR, onvalue = 1, offvalue = 0)
self.checkIndividualDWR.grid(row=4, column=5, sticky='NW', padx=(15,30))
self.checkIndividualDWR.svar = variableIndividualDWR
self.buttonIndividualAll = Button(self.frameCreationIndividual, text="Create...", width=43)
self.buttonIndividualAll.grid(row=1, column=6, rowspan=4, sticky='NESW', padx=px)
# ----------------------------------------------------
# START Entire System Creation frame initialization
# ----------------------------------------------------
self.frameCreationSystem = Tkinter.LabelFrame(self.frame, text="System Creation Menu", relief="groove", borderwidth="3", font=frameFont)
self.frameCreationSystem.grid(sticky="EW", padx=px, pady=15)
self.buttonLAIF = Button(self.frameCreationSystem, text="Create Layers/App Integration Files", width=35)
self.buttonLAIF.grid(row=11, column=0, sticky='NESW', ipady=5, padx=(indentx,0), pady=(16,8))
self.buttonDO = Button(self.frameCreationSystem, text="Create Entire Doc Outline")
self.buttonDO.grid(row=12, column=0, sticky='NESW', ipady=5, padx=(indentx,0), pady=(8,10))
# ----------------------------------------------------
# START Feature Tab Creation Frame initialization
# ----------------------------------------------------
self.frameCreationNew = Tkinter.LabelFrame(self.frame, text="Feature Tab Creation Menu", relief="groove", borderwidth="3", font=frameFont)
self.frameCreationNew.grid(sticky="EW", padx=px, pady=(15,5))
labelIssueSpreadsheet = Label(self.frameCreationNew, text="2. Feature Spreadsheet Path:")
labelIssueSpreadsheet.grid(row=0, column=0, sticky='W', padx=(indentx,0))
variableIssueSpreadsheet = Tkinter.StringVar()
self.entryIssueSpreadsheet = Entry(self.frameCreationNew, textvariable=variableIssueSpreadsheet, width=83, state=Tkinter.DISABLED)
self.entryIssueSpreadsheet.grid(row=0, column=1, sticky='W', padx=px, pady=5)
self.entryIssueSpreadsheet.svar = variableIssueSpreadsheet
buttonIssueSpreadsheet = Button(self.frameCreationNew, text="Browse...")
buttonIssueSpreadsheet.grid(row=0, column=2, sticky='W')
labelFeatureTab = Label(self.frameCreationNew, text="3. Feature Name:")
labelFeatureTab.grid(row=1, column=0, sticky='W', padx=(indentx,0))
variableFeatureTab = Tkinter.StringVar()
self.entryFeatureTab = Entry(self.frameCreationNew, textvariable=variableFeatureTab, width=83)
self.entryFeatureTab.grid(row=1, column=1, sticky='W', padx=px, pady=5)
self.entryFeatureTab.svar = variableFeatureTab
labelFeatureAbbrv = Label(self.frameCreationNew, text="4. Feature Abbreviation:")
labelFeatureAbbrv.grid(row=2, column=0, sticky='W', padx=(indentx,0))
variableFeatureAbbrv = Tkinter.StringVar()
self.entryFeatureAbbrv = Entry(self.frameCreationNew, textvariable=variableFeatureAbbrv, width=83)
self.entryFeatureAbbrv.grid(row=2, column=1, sticky='W', padx=px, pady=5)
self.entryFeatureAbbrv.svar = variableFeatureAbbrv
self.buttonNewFeature = Button(self.frameCreationNew, text="Create Feature Tab", width=35)
self.buttonNewFeature.grid(row=3, column=0, columnspan =2, sticky='NWS', ipady=5, pady=(8,10), padx=(indentx,0))
# ----------------------------------------------------
# START general purpose methods
# ----------------------------------------------------
def initializeCanvas(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"),width=winx,height=winy)
def resizeCanvas(self, event):
self.update()
cwidth = self.winfo_width()
cheight = self.winfo_height()
self.canvas.config(scrollregion=self.canvas.bbox("all"), width=cwidth, height=cheight)
# ----------------------------------------------------
# Initialize application
# ----------------------------------------------------
if __name__ == "__main__":
app = AppTk(None)
app.title("MBD File Creator")
app.mainloop()
Upvotes: 3
Views: 2596
Reputation: 2394
Try first to save the canvas.create_window
to a variable as following:
self.windows_item = canvas.create_window((0,0),window=frame,anchor='nw')
then at the end of resizeCanvas
to call the following method:
def update(self):
"Update the canvas and the scrollregion"
self.update_idletasks()
canvas.config(scrollregion=canvas.bbox(self.windows_item))
Hope it helps!
Most of it found here: https://stackoverflow.com/a/47985165/2160507
Upvotes: -1
Reputation: 385870
While there may be other errors, you have one very critical flaw: you are creating a binding for the <Configure>
event to self
. Because self
is the instance of the root window, every widget you create inherits this binding. Your resizeCanvas
method is literally being called hundreds of times at startup, and hundreds of times when the window is resized.
You also have the problem where you are calling update
in an event handler. As a general rule, this is bad. In effect, it's like calling mainloop
in that it will not return until all events are processed. If your code causes more events to be processed (such as reconfiguring a window and causing the <configure>
event to fire, you end up in a recursive loop.
You probably need to remove the calls to self.update()
and/or replace them with the less dangerous self.update_idletasks()
. You also need to either remove the <Configure>
binding on self
, or in the method you need to check for which widget caused the event to fire (ie: check that the event.widget is the root window).
Upvotes: 1