因此,经过数小时或阅读文章并查看tkinter的文档后,我发现在Windows机器上,tkinter滚动条的颜色选项将无法使用,因为滚动条直接从Windows获取其主题。我的问题是默认主题的颜色确实与我的程序冲突,因此我试图找到一种不涉及导入其他GUI程序包(例如PyQt)的解决方案(我无法在工作中访问pip,所以这是一个问题以获取新软件包)
除了使用单独的程序包之外,任何人都可以向我介绍一些有关如何编写自己的侧边栏以滚动文本小部件的文档。到目前为止,我所发现的甚至与我想做的事情都差不多的一个答案是这个问题。使用ttk样式更改tkinter中滚动条的外观
从我可以看到的示例来看,只是更改了滚动条的背景,因此我仍然无法使用该示例。我在用于配置样式的行之一上出现错误。
style.configure("My.Horizontal.TScrollbar", *style.configure("Horizontal.TScrollbar")) TypeError: configure() argument after * must be an iterable, not NoneType
不知道该如何处理该错误,因为我只是在遵循用户示例,因此我不确定它为什么对他们有用,但对我却不起作用。
到目前为止,我尝试过的是:
我如何创建文本框以及与之配套的滚动条。
root.text = Text(root, undo = True) root.text.grid(row = 0, column = 1, columnspan = 1, rowspan = 1, padx =(5,5), pady =(5,5), sticky = W+E+N+S) root.text.config(bg = pyFrameColor, fg = "white", font=('times', 16)) root.text.config(wrap=NONE) vScrollBar = tkinter.Scrollbar(root, command=root.text.yview) hScrollBar = tkinter.Scrollbar(root, orient = HORIZONTAL, command=root.text.xview) vScrollBar.grid(row = 0, column = 2, columnspan = 1, rowspan = 1, padx =1, pady =1, sticky = E+N+S) hScrollBar.grid(row = 1 , column = 1, columnspan = 1, rowspan = 1, padx =1, pady =1, sticky = S+W+E) root.text['yscrollcommand'] = vScrollBar.set root.text['xscrollcommand'] = hScrollBar.set
按照此处的文档在Windows机器上,我的以下尝试似乎无济于事。正如我在其他文章中所读到的,这与滚动条从Windows本地获取其主题有关。
vScrollBar.config(bg = mainBGcolor) vScrollBar['activebackground'] = mainBGcolor hScrollBar.config(bg = mainBGcolor) hScrollBar['activebackground'] = mainBGcolor
我想这可以归结为:
是否可以创建我自己的侧边栏(可以根据主题更改颜色)而无需导入其他python包?如果是这样,我应该从哪里开始,或者有人可以将我链接到文档,因为我的搜索总是会把我带回到Tkinter滚动条信息。由于这些config()选项适用于Linux,因此它们不适用于Windows。
不是一个完整的答案,但是您是否考虑过创建类似自己的滚动条:
import tkinter as tk class MyScrollbar(tk.Canvas): def __init__(self, master, *args, **kwargs): if 'width' not in kwargs: kwargs['width'] = 10 if 'bd' not in kwargs: kwargs['bd'] = 0 if 'highlightthickness' not in kwargs: kwargs['highlightthickness'] = 0 self.command = kwargs.pop('command') tk.Canvas.__init__(self, master, *args, **kwargs) self.elements = { 'button-1':None, 'button-2':None, 'trough':None, 'thumb':None} self._oldwidth = 0 self._oldheight = 0 self._sb_start = 0 self._sb_end = 1 self.bind('<Configure>', self._resize) self.tag_bind('button-1', '<Button-1>', self._button_1) self.tag_bind('button-2', '<Button-1>', self._button_2) self.tag_bind('trough', '<Button-1>', self._trough) self._track = False self.tag_bind('thumb', '<ButtonPress-1>', self._thumb_press) self.tag_bind('thumb', '<ButtonRelease-1>', self._thumb_release) self.tag_bind('thumb', '<Leave>', self._thumb_release) self.tag_bind('thumb', '<Motion>', self._thumb_track) def _sort_kwargs(self, kwargs): for key in kwargs: if key in ['buttontype', 'buttoncolor', 'troughcolor', 'thumbcolor', 'thumbtype']: self._scroll_kwargs[key] = kwargs.pop(key) # add to custom dict and remove from canvas dict return kwargs def _resize(self, event): width = self.winfo_width() height = self.winfo_height() # print("canvas: (%s, %s)" % (width, height)) if self.elements['button-1']: # exists if self._oldwidth != width: self.delete(self.elements['button-1']) self.elements['button-1'] = None else: pass if not self.elements['button-1']: # create self.elements['button-1'] = self.create_oval((0,0,width, width), fill='#006cd9', outline='#006cd9', tag='button-1') if self.elements['button-2']: # exists coords = self.coords(self.elements['button-2']) if self._oldwidth != width: self.delete(self.elements['button-2']) self.elements['button-2'] = None elif self._oldheight != height: self.move(self.elements['button-2'], 0, height-coords[3]) else: pass if not self.elements['button-2']: # create self.elements['button-2'] = self.create_oval((0,height-width,width, height), fill='#006cd9', outline='#006cd9', tag='button-2') if self.elements['trough']: # exists coords = self.coords(self.elements['trough']) if (self._oldwidth != width) or (self._oldheight != height): self.delete(self.elements['trough']) self.elements['trough'] = None else: pass if not self.elements['trough']: # create self.elements['trough'] = self.create_rectangle((0,int(width/2),width, height-int(width/2)), fill='#00468c', outline='#00468c', tag='trough') self.set(self._sb_start, self._sb_end) # hacky way to redraw thumb self.tag_raise('thumb') # ensure thumb always on top of trough self._oldwidth = width self._oldheight = height def _button_1(self, event): self.command('scroll', -1, 'pages') return 'break' def _button_2(self, event): self.command('scroll', 1, 'pages') return 'break' def _trough(self, event): width = self.winfo_width() height = self.winfo_height() size = (self._sb_end - self._sb_start) / 1 thumbrange = height - width thumbsize = int(thumbrange * size) thumboffset = int(thumbrange * self._sb_start) + int(width/2) thumbpos = int(thumbrange * size / 2) + thumboffset if event.y < thumbpos: self.command('scroll', -1, 'pages') elif event.y > thumbpos: self.command('scroll', 1, 'pages') return 'break' def _thumb_press(self, event): print("thumb press: (%s, %s)" % (event.x, event.y)) self._track = True def _thumb_release(self, event): print("thumb release: (%s, %s)" % (event.x, event.y)) self._track = False def _thumb_track(self, event): if self._track: # print("*"*30) print("thumb: (%s, %s)" % (event.x, event.y)) width = self.winfo_width() height = self.winfo_height() # print("window size: (%s, %s)" % (width, height)) size = (self._sb_end - self._sb_start) / 1 # print('size: %s' % size) thumbrange = height - width # print('thumbrange: %s' % thumbrange) thumbsize = int(thumbrange * size) # print('thumbsize: %s' % thumbsize) clickrange = thumbrange - thumbsize # print('clickrange: %s' % clickrange) thumboffset = int(thumbrange * self._sb_start) + int(width/2) # print('thumboffset: %s' % thumboffset) thumbpos = int(thumbrange * size / 2) + thumboffset # print("mouse point: %s" % event.y) # print("thumbpos: %s" % thumbpos) point = (event.y - (width/2) - (thumbsize/2)) / clickrange # point = (event.y - (width / 2)) / (thumbrange - thumbsize) # print(event.y - (width/2)) # print(point) if point < 0: point = 0 elif point > 1: point = 1 # print(point) self.command('moveto', point) return 'break' def set(self, *args): oldsize = (self._sb_end - self._sb_start) / 1 self._sb_start = float(args[0]) self._sb_end = float(args[1]) size = (self._sb_end - self._sb_start) / 1 width = self.winfo_width() height = self.winfo_height() if oldsize != size: self.delete(self.elements['thumb']) self.elements['thumb'] = None thumbrange = height - width thumbsize = int(thumbrange * size) thumboffset = int(thumbrange * self._sb_start) + int(width/2) if not self.elements['thumb']: # create self.elements['thumb'] = self.create_rectangle((0, thumboffset,width, thumbsize+thumboffset), fill='#4ca6ff', outline='#4ca6ff', tag='thumb') else: # move coords = self.coords(self.elements['thumb']) if (thumboffset != coords[1]): self.move(self.elements['thumb'], 0, thumboffset-coords[1]) return 'break' if __name__ == '__main__': root = tk.Tk() lb = tk.Listbox(root) lb.pack(side='left', fill='both', expand=True) for num in range(0,100): lb.insert('end', str(num)) sb = MyScrollbar(root, width=50, command=lb.yview) sb.pack(side='right', fill='both', expand=True) lb.configure(yscrollcommand=sb.set) root.mainloop()
我已经留下我的评论了,为了我一生,我似乎无法单击并拖动拇指以使其正常工作,但是它具有以下功能的简单滚动条:
我已经修改了拇指代码,以修复单击和拖动滚动:
import tkinter as tk class MyScrollbar(tk.Canvas): def __init__(self, master, *args, **kwargs): self._scroll_kwargs = { 'command':None, 'orient':'vertical', 'buttontype':'round', 'buttoncolor':'#006cd9', 'troughcolor':'#00468c', 'thumbtype':'rectangle', 'thumbcolor':'#4ca6ff', } kwargs = self._sort_kwargs(kwargs) if self._scroll_kwargs['orient'] == 'vertical': if 'width' not in kwargs: kwargs['width'] = 10 elif self._scroll_kwargs['orient'] == 'horizontal': if 'height' not in kwargs: kwargs['height'] = 10 else: raise ValueError if 'bd' not in kwargs: kwargs['bd'] = 0 if 'highlightthickness' not in kwargs: kwargs['highlightthickness'] = 0 tk.Canvas.__init__(self, master, *args, **kwargs) self.elements = { 'button-1':None, 'button-2':None, 'trough':None, 'thumb':None} self._oldwidth = 0 self._oldheight = 0 self._sb_start = 0 self._sb_end = 1 self.bind('<Configure>', self._resize) self.tag_bind('button-1', '<Button-1>', self._button_1) self.tag_bind('button-2', '<Button-1>', self._button_2) self.tag_bind('trough', '<Button-1>', self._trough) self._track = False self.tag_bind('thumb', '<ButtonPress-1>', self._thumb_press) self.bind('<ButtonRelease-1>', self._thumb_release) # self.bind('<Leave>', self._thumb_release) self.bind('<Motion>', self._thumb_track) def _sort_kwargs(self, kwargs): to_remove = [] for key in kwargs: if key in [ 'buttontype', 'buttoncolor', 'buttonoutline', 'troughcolor', 'troughoutline', 'thumbcolor', 'thumbtype', 'thumboutline', 'command', 'orient']: self._scroll_kwargs[key] = kwargs[key] # add to custom dict to_remove.append(key) for key in to_remove: del kwargs[key] return kwargs def _get_colour(self, element): if element in self._scroll_kwargs: # if element exists in settings return self._scroll_kwargs[element] if element.endswith('outline'): # if element is outline and wasn't in settings return self._scroll_kwargs[element.replace('outline', 'color')] # fetch default for main element def _width(self): return self.winfo_width() - 2 # return width minus 2 pixes to ensure fit in canvas def _height(self): return self.winfo_height() - 2 # return height minus 2 pixes to ensure fit in canvas def _resize(self, event): width = self._width() height = self._height() if self.elements['button-1']: # exists # delete element if vertical scrollbar and width changed # or if horizontal and height changed, signals button needs to change if (((self._oldwidth != width) and (self._scroll_kwargs['orient'] == 'vertical')) or ((self._oldheight != height) and (self._scroll_kwargs['orient'] == 'horizontal'))): self.delete(self.elements['button-1']) self.elements['button-1'] = None if not self.elements['button-1']: # create size = width if (self._scroll_kwargs['orient'] == 'vertical') else height rect = (0,0,size, size) fill = self._get_colour('buttoncolor') outline = self._get_colour('buttonoutline') if (self._scroll_kwargs['buttontype'] == 'round'): self.elements['button-1'] = self.create_oval(rect, fill=fill, outline=outline, tag='button-1') elif (self._scroll_kwargs['buttontype'] == 'square'): self.elements['button-1'] = self.create_rectangle(rect, fill=fill, outline=outline, tag='button-1') if self.elements['button-2']: # exists coords = self.coords(self.elements['button-2']) # delete element if vertical scrollbar and width changed # or if horizontal and height changed, signals button needs to change if (((self._oldwidth != width) and (self._scroll_kwargs['orient'] == 'vertical')) or ((self._oldheight != height) and (self._scroll_kwargs['orient'] == 'horizontal'))): self.delete(self.elements['button-2']) self.elements['button-2'] = None # if vertical scrollbar and height changed button needs to move elif ((self._oldheight != height) and (self._scroll_kwargs['orient'] == 'vertical')): self.move(self.elements['button-2'], 0, height-coords[3]) # if horizontal scrollbar and width changed button needs to move elif ((self._oldwidth != width) and (self._scroll_kwargs['orient'] == 'horizontal')): self.move(self.elements['button-2'], width-coords[2], 0) if not self.elements['button-2']: # create if (self._scroll_kwargs['orient'] == 'vertical'): rect = (0,height-width,width, height) elif (self._scroll_kwargs['orient'] == 'horizontal'): rect = (width-height,0,width, height) fill = self._get_colour('buttoncolor') outline = self._get_colour('buttonoutline') if (self._scroll_kwargs['buttontype'] == 'round'): self.elements['button-2'] = self.create_oval(rect, fill=fill, outline=outline, tag='button-2') elif (self._scroll_kwargs['buttontype'] == 'square'): self.elements['button-2'] = self.create_rectangle(rect, fill=fill, outline=outline, tag='button-2') if self.elements['trough']: # exists coords = self.coords(self.elements['trough']) # delete element whenever width or height changes if (self._oldwidth != width) or (self._oldheight != height): self.delete(self.elements['trough']) self.elements['trough'] = None if not self.elements['trough']: # create if (self._scroll_kwargs['orient'] == 'vertical'): rect = (0, int(width/2), width, height-int(width/2)) elif (self._scroll_kwargs['orient'] == 'horizontal'): rect = (int(height/2), 0, width-int(height/2), height) fill = self._get_colour('troughcolor') outline = self._get_colour('troughoutline') self.elements['trough'] = self.create_rectangle(rect, fill=fill, outline=outline, tag='trough') self.set(self._sb_start, self._sb_end) # hacky way to redraw thumb without moving it self.tag_raise('thumb') # ensure thumb always on top of trough self._oldwidth = width self._oldheight = height def _button_1(self, event): command = self._scroll_kwargs['command'] if command: command('scroll', -1, 'pages') return 'break' def _button_2(self, event): command = self._scroll_kwargs['command'] if command: command('scroll', 1, 'pages') return 'break' def _trough(self, event): # print('trough: (%s, %s)' % (event.x, event.y)) width = self._width() height = self._height() coords = self.coords(self.elements['trough']) if (self._scroll_kwargs['orient'] == 'vertical'): trough_size = coords[3] - coords[1] elif (self._scroll_kwargs['orient'] == 'horizontal'): trough_size = coords[2] - coords[0] # print('trough size: %s' % trough_size) size = (self._sb_end - self._sb_start) / 1 if (self._scroll_kwargs['orient'] == 'vertical'): thumbrange = height - width elif (self._scroll_kwargs['orient'] == 'horizontal'): thumbrange = width - height thumbsize = int(thumbrange * size) if (self._scroll_kwargs['orient'] == 'vertical'): thumboffset = int(thumbrange * self._sb_start) + int(width/2) elif (self._scroll_kwargs['orient'] == 'horizontal'): thumboffset = int(thumbrange * self._sb_start) + int(height/2) thumbpos = int(thumbrange * size / 2) + thumboffset command = self._scroll_kwargs['command'] if command: if (((self._scroll_kwargs['orient'] == 'vertical') and (event.y < thumbpos)) or ((self._scroll_kwargs['orient'] == 'horizontal') and (event.x < thumbpos))): command('scroll', -1, 'pages') elif (((self._scroll_kwargs['orient'] == 'vertical') and (event.y > thumbpos)) or ((self._scroll_kwargs['orient'] == 'horizontal') and (event.x > thumbpos))): command('scroll', 1, 'pages') return 'break' def _thumb_press(self, event): self._track = True def _thumb_release(self, event): self._track = False def _thumb_track(self, event): # print('track') if self._track: width = self._width() height = self._height() # print("window size: (%s, %s)" % (width, height)) size = (self._sb_end - self._sb_start) / 1 coords = self.coords(self.elements['trough']) # print('trough coords: %s' % coords) if (self._scroll_kwargs['orient'] == 'vertical'): trough_size = coords[3] - coords[1] thumbrange = height - width elif (self._scroll_kwargs['orient'] == 'horizontal'): trough_size = coords[2] - coords[0] thumbrange = width - height # print('trough size: %s' % trough_size) thumbsize = int(thumbrange * size) if (self._scroll_kwargs['orient'] == 'vertical'): pos = max(min(trough_size, event.y - coords[1] - (thumbsize/2)), 0) elif (self._scroll_kwargs['orient'] == 'horizontal'): pos = max(min(trough_size, event.x - coords[0] - (thumbsize/2)), 0) # print('pos: %s' % pos) point = pos / trough_size # print('point: %s' % point) command = self._scroll_kwargs['command'] if command: command('moveto', point) return 'break' def set(self, *args): # print('set: %s' % str(args)) oldsize = (self._sb_end - self._sb_start) / 1 self._sb_start = float(args[0]) self._sb_end = float(args[1]) size = (self._sb_end - self._sb_start) / 1 width = self._width() height = self._height() if oldsize != size: self.delete(self.elements['thumb']) self.elements['thumb'] = None if (self._scroll_kwargs['orient'] == 'vertical'): thumbrange = height - width thumboffset = int(thumbrange * self._sb_start) + int(width/2) elif (self._scroll_kwargs['orient'] == 'horizontal'): thumbrange = width - height thumboffset = int(thumbrange * self._sb_start) + int(height/2) thumbsize = int(thumbrange * size) if not self.elements['thumb']: # create if (self._scroll_kwargs['orient'] == 'vertical'): rect = (0, thumboffset,width, thumbsize+thumboffset) elif (self._scroll_kwargs['orient'] == 'horizontal'): rect = (thumboffset, 0, thumbsize+thumboffset, height) fill = self._get_colour('thumbcolor') outline = self._get_colour('thumboutline') if (self._scroll_kwargs['thumbtype'] == 'round'): self.elements['thumb'] = self.create_oval(rect, fill=fill, outline=outline, tag='thumb') elif (self._scroll_kwargs['thumbtype'] == 'rectangle'): self.elements['thumb'] = self.create_rectangle(rect, fill=fill, outline=outline, tag='thumb') else: # move coords = self.coords(self.elements['thumb']) if (self._scroll_kwargs['orient'] == 'vertical'): if (thumboffset != coords[1]): self.move(self.elements['thumb'], 0, thumboffset-coords[1]) elif (self._scroll_kwargs['orient'] == 'horizontal'): if (thumboffset != coords[1]): self.move(self.elements['thumb'], thumboffset-coords[0], 0) return 'break' if __name__ == '__main__': root = tk.Tk() root.grid_rowconfigure(1, weight=1) root.grid_columnconfigure(1, weight=1) root.grid_rowconfigure(3, weight=1) root.grid_columnconfigure(3, weight=1) lb = tk.Listbox(root) lb.grid(column=1, row=1, sticky="nesw") for num in range(0,100): lb.insert('end', str(num)*100) sby1 = MyScrollbar(root, width=50, command=lb.yview) sby1.grid(column=2, row=1, sticky="nesw") sby2 = MyScrollbar(root, width=50, command=lb.yview, buttontype='square', thumbtype='round') sby2.grid(column=4, row=1, sticky="nesw") sbx1 = MyScrollbar(root, height=50, command=lb.xview, orient='horizontal', buttoncolor='red', thumbcolor='orange', troughcolor='green') sbx1.grid(column=1, row=2, sticky="nesw") sbx2 = MyScrollbar(root, height=50, command=lb.xview, orient='horizontal', thumbtype='round') sbx2.grid(column=1, row=4, sticky="nesw") def x_set(*args): sbx1.set(*args) sbx2.set(*args) def y_set(*args): sby1.set(*args) sby2.set(*args) lb.configure(yscrollcommand=y_set, xscrollcommand=x_set) root.mainloop()
因此,我已经修复了计算方法,以计算出新滚动位置的位置,并将其从用于track和release事件的thumb标签上的绑定更改为整个画布上的绑定,因此,如果用户快速滚动,该绑定仍放开鼠标时释放。 我已经注释掉了光标离开画布时的绑定,因此该行为更接近于现有的滚动条,但是如果希望鼠标离开小部件时停止滚动,则可以重新启用该行为。 至于创建两个类,上面的修改后的代码使您可以使用orient关键字,这样就可以删除该类(具有样式更改)来代替默认滚动条,如底部的示例所示。
orient