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

Mik*_*SMT 5 python themes tkinter scrollbar

因此,在几小时后或阅读帖子并查看 tkinter 的文档后,我发现在 Windows 机器上,由于滚动条直接从 Windows 获取其主题,tkinter 滚动条的颜色选项将不起作用。我的问题是默认主题的颜色确实与我的程序冲突,我试图找到一个解决方案,它不涉及导入不同的 GUI 包,例如 PyQt(我在工作时无法访问 pip,所以这是一个问题获取新包)

除了使用单独的包之外,任何人都可以向我指出一些有关如何编写自己的侧边栏以滚动文本小部件的文档。到目前为止,我所发现的甚至接近我想要做的就是对这个问题的答案。(使用 ttk 样式更改 tkinter 中滚动条的外观

从我可以看到的例子只是改变了滚动条的背景,因此我仍然无法使用这个例子。我在用于配置样式的行之一上出错。

    style.configure("My.Horizontal.TScrollbar", *style.configure("Horizontal.TScrollbar"))
TypeError: configure() argument after * must be an iterable, not NoneType
Run Code Online (Sandbox Code Playgroud)

不知道如何处理这个错误,因为我只是在关注用户的例子,我不确定为什么它对他们有用而不对我有用。

到目前为止我尝试过的是:

我如何创建我的文本框和滚动条。

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
Run Code Online (Sandbox Code Playgroud)

遵循此处的文档我的以下尝试似乎在 Windows 机器上没有做任何事情。正如我在其他帖子中所读到的,这与滚动条从 Windows 本地获取其主题有关。

vScrollBar.config(bg = mainBGcolor)
vScrollBar['activebackground'] = mainBGcolor
hScrollBar.config(bg = mainBGcolor)
hScrollBar['activebackground'] = mainBGcolor
Run Code Online (Sandbox Code Playgroud)

我想这一切都归结为:

是否可以创建我自己的侧边栏(我可以根据主题更改颜色)而无需导入其他 python 包?如果是这样,我应该从哪里开始或者有人可以将我链接到文档,因为我的搜索总是接缝引导我回到Tkinter 滚动条信息。由于这些 config() 选项适用于 linux,因此它们不适用于 Windows。

Jam*_*ent 6

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

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()
Run Code Online (Sandbox Code Playgroud)

我已经留下了我的评论,对于我的生活,我似乎无法通过单击和拖动拇指来正常工作,但它是一个具有以下功能的简单滚动条:

  • 可以着色的向上和向下按钮
  • 可以单独着色的拇指和槽
  • 跟踪可滚动小部件中的移动
  • 拇指随着滚动区域的大小调整大小

编辑

我已经修改了拇指代码来修复点击和拖动滚动:

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')
            outlin