Python Tkinter滚动条摇晃滚动 [英] Python Tkinter Scrollbar Shaky Scrolling
问题描述
简介: 我的Python Tkinter应用程序设计为在侧面具有滚动条,以便在调整窗口大小时仍可以通过滚动条查看该应用程序.为此,我将所有内容的框架放在画布中,并使用滚动条控制画布.在调整窗口大小时,我有一个名为resizeCanvas的函数,它可以调整画布的大小.
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:
- Tkinter根
- 框架(只是画布的容器)
- 画布(滚动条已连接到此画布)
- 框架(内容在此框架中)
- Tkinter root
- frame (just a container for the canvas)
- canvas (scroll bar is attached to this canvas)
- frame (content is in this frame)
注意:Python 2.7.2
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()
推荐答案
虽然可能还有其他错误,但是您有一个非常关键的缺陷:您正在为
<Configure>
事件创建绑定到self
的绑定.由于self
是根窗口的实例,因此您创建的每个小部件都将继承此绑定.您的resizeCanvas
方法实际上在启动时被调用了数百次,而在调整窗口大小时则被调用了数百次.While there may be other errors, you have one very critical flaw: you are creating a binding for the
<Configure>
event toself
. Becauseself
is the instance of the root window, every widget you create inherits this binding. YourresizeCanvas
method is literally being called hundreds of times at startup, and hundreds of times when the window is resized.您还存在在事件处理程序中调用
update
的问题.通常,这是不好的.实际上,这就像调用mainloop
一样,直到处理完所有事件后它才会返回.如果您的代码导致要处理更多事件(例如,重新配置窗口并导致触发<configure>
事件),则最终将导致递归循环.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 callingmainloop
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.您可能需要删除对
self.update()
的调用,并且/或者将它们替换为危险程度较小的self.update_idletasks()
.您还需要删除self
上的<Configure>
绑定,或者在方法中需要检查导致事件触发的窗口小部件(即:检查event.widget是根窗口).You probably need to remove the calls to
self.update()
and/or replace them with the less dangerousself.update_idletasks()
. You also need to either remove the<Configure>
binding onself
, 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).这篇关于Python Tkinter滚动条摇晃滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- canvas (scroll bar is attached to this canvas)
- frame (just a container for the canvas)
- 画布(滚动条已连接到此画布)
- 框架(只是画布的容器)