繁体   English   中英

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

[英]Other option for colored scrollbar in tkinter based program?

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

除了使用单独的包之外,任何人都可以向我指出一些关于如何编写自己的侧边栏以滚动文本小部件的文档。 到目前为止,我发现的所有内容都接近我想要做的就是回答这个问题。 使用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()

所以我已经修复了计算,以确定新滚动到位置的位置,并从轨道和释放事件的拇指标签上的绑定更改为整个画布上的绑定,因此如果用户快速滚动,绑定仍然会释放鼠标时释放。
我已经注释掉光标离开画布时的绑定,所以行为更接近地模仿现有滚动条,但是如果你希望它在鼠标离开小部件时停止滚动,则可以重新启用。
至于创建两个类,上面修改的代码允许您使用orient关键字,因此您可以删除此类(使用样式更改)代替默认滚动条,如底部示例所示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM