I am trying to create a scrolled list built from a custom widget -- similar to a listbox but with multiple entry fields per line (in the real application, there are validation routines associated with each entry field).
The attached example demonstrates several problems, the most severe of which is a strange jerky behavior on Windows (not happening on Linux): gently dragging the scroll bar slider to a position where the 7th row (of 10) should be displayed at the top causes jumping between rows 6 and 8. Anybody seen something like this? Any suggestions? Thanks. ===== #! /usr/bin/python # (tested on Windows XP, with Python 2.7 and Tcl/Tk 8.5.2 built entirely # from sources; on Linux using Ubuntu 9.04 distributed packages -- Python # 2.6.2) # Example of a scrolled list bulit from a custom widget # # type1: an Entry field with a StringVar built-in # type2: another Entry field, without the built-in StringVar # frm: a Frame build from two type1's and one type2, with some Labels # testlist: a scrolling list of frm's # testcase: build a 3 high testlist with 10 frm's (scrollable) # Issues: # # 1. Moving the slider slightly gives jerky motion due to # redisplaying. To reduce this I check for a small "moveto" # that doesn't change the displayed row and only set the # scrollbar (slider) without redisplaying the list elements. # # 2. on Linux, moving the slider on the scroll bar to the top # sometimes gives a negative "moveto", which I force to 0, or a # "moveto" greater than 1.0, which I force to 1.0. This # doesn't happen on Windows. # # 3. on Windows, using "pack" as shown causes the root window to flash # quite a bit when scrolling (presumably due to the "grid_forget"s # briefly redisplaying the window with fewer rows. This isn't # visible on Linux. As a workaround on Windows I use a Canvas # which hides the flashing. # # 4. (MOST SEVERE) On Windows, with or without the Canvas widget, # moving the slider slowly hits a glitch when row #7 would be # the top row displayed. The slider jumps between a pair of # values (sometimes 0.56 and 0.7, sometimes other values), # causing the window to flash rapidly. I cannot get the slider # to position row #7 at the top -- it jumps to row #8 or row #6 # (or even lower). This does not happen on Linux -- scrolling # is smooth, no glitches. from Tkinter import * import sys class type1(Entry): def __init__(self,master, v='xxx'): self.v = StringVar() self.v.set(v) Entry.__init__(self, master, textvariable=self.v) class type2(Entry): def __init__(self,master, **kw): Entry.__init__(self,master,**kw) class frm(Frame): def __init__(self, master, **kw): Frame.__init__(self) self.x1 = type1(self,**kw) self.x1.pack(side=LEFT) self.fx2 = Frame(self) Label(self, text=' X2').pack(in_=self.fx2,side=LEFT) self.v2 = StringVar() self.v2.set('yy') self.x2 = type2(self,width=5,textvariable=self.v2) self.x2.pack(in_=self.fx2,side=LEFT) self.fx2.pack(side=LEFT) self.fx3 = Frame(self) Label(self,text=' X3').pack(in_=self.fx3,side=LEFT) self.x3 = type1(self, v='zzz') self.x3.pack(in_=self.fx3,side=LEFT) self.fx3.pack(side=LEFT) class testlist(Frame): def __init__(self, master, maxdisp=3, **kw): Frame.__init__(self,master) self.sb = Scrollbar(self, command=self.scroll) self.sb.grid(in_=self,row=0,column=1,rowspan=maxdisp,sticky=N+S) self.maxdisp = maxdisp self.frms = [frm(self,**kw)] self.active = 0 self.frms[0].grid(in_=self,row=0,column=0) def setsb(self,start,mv): dispfrac = self.maxdisp / float(len(self.frms)) startfrac = float(start) / len(self.frms) if mv == None: self.sb.set(startfrac, min(1, startfrac+dispfrac)) else: self.sb.set(mv, min(1, mv+dispfrac)) def redisp(self, start, mv=None): for xx in self.frms[self.active:self.maxdisp+self.active]: xx.grid_forget() yy = 0 for xx in self.frms[start:self.maxdisp+start]: xx.grid(in_=self,row=yy,column=0) yy += 1 self.setsb(start,mv) # print start, startfrac, min(1, startfrac+dispfrac) self.active = start def addentry(self, **kw): self.frms.append(frm(self,**kw)) # perhaps move to the end of the list ? self.redisp(self.active) def scroll(self,*xx): op, cnt = xx[0], xx[1] mv = None if op == "scroll": if xx[2] == 'pages': amt = self.maxdisp * int(xx[1]) else: amt = int(xx[1]) newstart = self.active + amt else: mv = min(1.0,max(0.0,float(cnt))) newstart = int(len(self.frms) * mv) print cnt, mv, newstart newstart = min(newstart, len(self.frms) - self.maxdisp) newstart = max(0, newstart) if newstart <> self.active: self.redisp(newstart,mv=mv) else: self.setsb(newstart,mv) def testcase(master): tt = testlist(master,v='1') for xx in range(2,11): tt.addentry(v=str(xx)) return tt if (__name__ == '__main__'): rr = Tk() try: arg1=sys.argv[1] except IndexError: arg1=None if arg1 == 'c': # use Canvas cc=Canvas(rr,height=300,width=600) cc.pack() tt = testcase(cc) tt.place(x=10, y=10) else: # just pack into root tt = testcase(rr) tt.pack() rr.mainloop() _______________________________________________ Tkinter-discuss mailing list Tkinter-discuss@python.org http://mail.python.org/mailman/listinfo/tkinter-discuss