基于tkinter的程序中彩色滚动条的其他选项?

发布于 2021-01-29 14:10:22

因此,经过数小时或阅读文章并查看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。

关注者
0
被浏览
96
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    不是一个完整的答案,但是您是否考虑过创建类似自己的滚动条:

    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关键字,这样就可以删除该类(具有样式更改)来代替默认滚动条,如底部的示例所示。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看