当前位置: 首页 > 知识库问答 >
问题:

如何在tkinter中为框架创建自定义滚动条

彭洲
2023-03-14

我一直在为我的项目使用tkinter滚动条。但是,当我使用属性troughcolor时,滚动条的颜色不会改变。所以,我想为tkinter和python制作一个自定义滚动条,可以用来在框架中滚动。然后我将添加颜色到这个自定义滚动条。有没有办法这样做?下面是我的代码:

root=Tk()
container = ttk.Frame(root)
canvas = Canvas(container, highlightbackground="black", highlightthickness=1, bg="black", width=400, height=600)
scrollbar = Scrollbar(container, orient="vertical", command=canvas.yview, troughcolor="red")
scrollable_frame = ttk.Frame(canvas)
scrollable_frame.bind(
    "<Configure>",
    lambda e: canvas.configure(
        scrollregion=canvas.bbox("all")
    )
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
container.pack()
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
root.mainloop()

共有1个答案

公良鸿风
2023-03-14

基于Bryan Oakley的评论和他在SO上的各种帖子,以及我几天的工作,这里是这个问题的一个相当完整的答案。此滚动条是可配置的,当不需要时可以选择隐藏。我是一个初学者,这可以提高,但它似乎工作还可以。我留下了我的评论,以及布莱恩·奥克利的原始帖子,他在那里展示了这段代码的一部分。这对我来说并不容易做到,这些评论可能会帮助人们更好地理解它。我几周前写了这段代码,到目前为止运行良好。

import tkinter as tk

'''
    Much of this code was elucidated by Bryan Oakley on StackOverflow.com. 
    Without his explanations and examples, I would not have figured out how to 
    create a configurable Tkinter scrollbar. Any mistakes in this code are mine 
    of course.

    I didn't add the little arrows at the ends of the trough.
'''

class Scrollbar(tk.Canvas):
    '''
        A scrollbar is gridded as a sibling of what it's scrolling.
    '''

    def __init__(self, parent, orient='vertical', hideable=False, **kwargs):
        print('kwargs is', kwargs)

        '''
            kwargs is {
                'width': 17, 
                'command': <bound method YView.yview of 
                    <widgets.Text object .!canvas.!frame.!frame.!text>>}

            https://stackoverflow.com/questions/15411107
            You can use dict.pop:... delete an item in a dictionary only if the given key exists... not certain if key exists in the dictionary...

                mydict.pop("key", None)

            ...if the second argument, None is not given, KeyError is raised if the key is not in the dictionary. Providing the second argument prevents the conditional exception... the second argument to .pop() is what it returns if the key is not found. 
        '''

        self.command = kwargs.pop('command', None)
        print('self.command is', self.command)
        tk.Canvas.__init__(self, parent, **kwargs)

        self.orient = orient
        self.hideable = hideable

        self.new_start_y = 0
        self.new_start_x = 0
        self.first_y = 0
        self.first_x = 0

        self.slidercolor = 'steelblue'
        self.troughcolor = 'lightgray'

        self.config(bg=self.troughcolor, bd=0, highlightthickness=0)

        # coordinates are irrelevant; they will be recomputed
        #   in the 'set' method
        self.create_rectangle(
            0, 0, 1, 1, 
            fill=self.slidercolor, 
            width=2, # this is border width
            outline='teal', 
            tags=('slider',))
        self.bind('<ButtonPress-1>', self.move_on_click)

        self.bind('<ButtonPress-1>', self.start_scroll, add='+')
        self.bind('<B1-Motion>', self.move_on_scroll)
        self.bind('<ButtonRelease-1>', self.end_scroll)

    def set(self, lo, hi):
        '''
            For resizing & repositioning the slider. The hideable
            scrollbar portion is by Fredrik Lundh, one of Tkinter's authors.
        '''

        lo = float(lo)
        hi = float(hi)

        if self.hideable is True:
            if lo <= 0.0 and hi >= 1.0:
                self.grid_remove()
                return
            else:
                self.grid()

        height = self.winfo_height()
        width = self.winfo_width()

        if self.orient == 'vertical':
            x0 = 2
            y0 = max(int(height * lo), 0)
            x1 = width - 2
            y1 = min(int(height * hi), height)
        # This was the tricky part of making a horizontal scrollbar 
        #   when I already knew how to make a vertical one.
        #   You can't just change all the "height" to "width"
        #   and "y" to "x". You also have to reverse what x0 etc 
        #   are equal to, comparing code in if and elif. Till that was
        #   done, everything worked but the horizontal scrollbar's 
        #   slider moved up & down.
        elif self.orient == 'horizontal':
            x0 = max(int(width * lo), 0)
            y0 = 2
            x1 = min(int(width * hi), width)
            y1 = height

        self.coords('slider', x0, y0, x1, y1)
        self.x0 = x0
        self.y0 = y0
        self.x1 = x1
        self.y1 = y1

    def move_on_click(self, event):
        if self.orient == 'vertical':
            # don't scroll on click if mouse pointer is w/in slider
            y = event.y / self.winfo_height()
            if event.y < self.y0 or event.y > self.y1:
                self.command('moveto', y)
            # get starting position of a scrolling event
            else:
                self.first_y = event.y
        elif self.orient == 'horizontal':
            # do nothing if mouse pointer is w/in slider
            x = event.x / self.winfo_width()
            if event.x < self.x0 or event.x > self.x1:
                self.command('moveto', x)
            # get starting position of a scrolling event
            else:
                self.first_x = event.x

    def start_scroll(self, event):
        if self.orient == 'vertical':
            self.last_y = event.y 
            self.y_move_on_click = int(event.y - self.coords('slider')[1])
        elif self.orient == 'horizontal':
            self.last_x = event.x 
            self.x_move_on_click = int(event.x - self.coords('slider')[0])

    def end_scroll(self, event):
        if self.orient == 'vertical':
            self.new_start_y = event.y
        elif self.orient == 'horizontal':
            self.new_start_x = event.x

    def move_on_scroll(self, event):

        # Only scroll if the mouse moves a few pixels. This makes
        #   the click-in-trough work right even if the click smears
        #   a little. Otherwise, a perfectly motionless mouse click
        #   is the only way to get the trough click to work right.
        #   Setting jerkiness to 5 or more makes very sloppy trough
        #   clicking work, but then scrolling is not smooth. 3 is OK.

        jerkiness = 3

        if self.orient == 'vertical':
            if abs(event.y - self.last_y) < jerkiness:
                return
            # scroll the scrolled widget in proportion to mouse motion
            #   compute whether scrolling up or down
            delta = 1 if event.y > self.last_y else -1
            #   remember this location for the next time this is called
            self.last_y = event.y
            #   do the scroll
            self.command('scroll', delta, 'units')
            # afix slider to mouse pointer
            mouse_pos = event.y - self.first_y
            if self.new_start_y != 0:
                mouse_pos = event.y - self.y_move_on_click
            self.command('moveto', mouse_pos/self.winfo_height()) 
        elif self.orient == 'horizontal':
            if abs(event.x - self.last_x) < jerkiness:
                return
            # scroll the scrolled widget in proportion to mouse motion
            #   compute whether scrolling left or right
            delta = 1 if event.x > self.last_x else -1
            #   remember this location for the next time this is called
            self.last_x = event.x
            #   do the scroll
            self.command('scroll', delta, 'units')
            # afix slider to mouse pointer
            mouse_pos = event.x - self.first_x
            if self.new_start_x != 0:
                mouse_pos = event.x - self.x_move_on_click
            self.command('moveto', mouse_pos/self.winfo_width()) 

    def colorize(self):
        print('colorize')
        self.slidercolor = 'blue'
        self.troughcolor = 'bisque'
        self.config(bg=self.troughcolor)

if __name__ == '__main__':

    def resize_scrollbar():
        root.update_idletasks()  
        canvas.config(scrollregion=canvas.bbox('all')) 

    def resize_window():
        root.update_idletasks()
        page_x = content.winfo_reqwidth()
        page_y = content.winfo_reqheight()
        root.geometry('{}x{}'.format(page_x, page_y))

    root = tk.Tk()
    root.config(bg='yellow')
    root.grid_columnconfigure(0, weight=1)
    root.grid_rowconfigure(0, weight=1)
    root.grid_rowconfigure(1, weight=0)

    canvas = tk.Canvas(root, bg='tan')
    canvas.grid(column=0, row=0, sticky='news')

    content = tk.Frame(canvas)
    content.grid_columnconfigure(0, weight=1)
    content.grid_rowconfigure(0, weight=1)

    ysb_canv = Scrollbar(root, width=24, hideable=True, command=canvas.yview)
    xsb_canv = Scrollbar(root, height=24, hideable=True, command=canvas.xview, orient='horizontal')
    canvas.config(yscrollcommand=ysb_canv.set, xscrollcommand=xsb_canv.set)

    frame = tk.Frame(content)
    frame.grid_columnconfigure(0, weight=0)
    frame.grid_rowconfigure(0, weight=1)

    text = tk.Text(frame, bd=0)
    ysb_txt = Scrollbar(frame, width=17, command=text.yview)

    text.config(yscrollcommand=ysb_txt.set)

    space = tk.Frame(content, width=1200, height=500)

    ysb_canv.grid(column=1, row=0, sticky='ns')
    xsb_canv.grid(column=0, row=1, sticky='ew')
    frame.grid(column=0, row=0, sticky='news')
    text.grid(column=0, row=0)
    ysb_txt.grid(column=1, row=0, sticky='ns')
    space.grid(column=0, row=1)

    with open(__file__, 'r') as f:
        text.insert('end', f.read())

    canvas.create_window(0, 0, anchor='nw', window=content)

    resize_scrollbar()
    resize_window()

    root.mainloop()
 类似资料:
  • 问题内容: 我的目标是向具有多个标签的框架添加垂直滚动条。一旦框架内的标签超过框架的高度,滚动条应自动启用。搜索之后,我发现了这个有用的帖子。根据该帖子,我了解到要实现我想要的功能(如果我错了,请纠正我,我是一个初学者),我必须先创建一个,然后在该框架内创建一个并将滚动条粘贴到该框架上好。之后,创建另一个框架并将其作为窗口对象放在画布内。所以,我终于想出了这个: 我做对了吗?有没有更好/更聪明的方

  • 本文向大家介绍如何使用CSS创建自定义滚动条?,包括了如何使用CSS创建自定义滚动条?的使用技巧和注意事项,需要的朋友参考一下 要使用CSS创建自定义滚动条,代码如下- 示例

  • 问题内容: 我想知道如何在Facebook上制作自定义滚动条。 是只有CSS还是一些JavaScript? 如果可以,我可以对代码的外观有所了解吗? 此问题特定于Facebook滚动条样式,而不是如何简单地具有自定义滚动条 问题答案: 使用样式类似于滚动条的div来捕获单击和拖动事件。与这些事件相关的是滚动另一个div的内容的方法,该div设置为任意高度,并且通常具有css规则的overflow:

  • 我想创建一个如下所示的自定义对话框 我试过以下几件事。 > 我创建了AlertDialog.Builder的子类,并使用了自定义标题和自定义内容视图,但结果不是预期的。 另一个尝试是子类DialogFragment并自定义onCreateDialog中的对话框,但结果并不像预期的那样。 然后我尝试使用一个普通的对话框类。结果不如预期。 在这三种情况下,问题是当我忽略标题视图时,对话框的大小不像预期

  • 问题内容: 我的目标是让滚动条位于全屏窗口的右侧,允许用户在各种不同的小部件(例如标签和按钮)中上下滚动。从我在该站点上看到的其他答案中,我得出的结论是,必须将滚动条分配给画布才能使其正常运行,我已经尝试将其包含在代码中,但还没有太多成功。 以下代码显示了到目前为止我已完成的工作的简化版本: 运行此代码时,我面临两个问题: 一种是滚动条处于非活动状态,并且不允许用户向下滚动以查看其余文本。 另一个

  • 问题内容: 我在JFrame上有一个按钮,当单击该按钮时,我希望对话框弹出并带有多个文本区域供用户输入。我一直在四处寻找解决方法,但是我一直感到困惑。有人可以帮忙吗? 问题答案: 如果您不需要太多自定义行为,则JOptionPane可以节省大量时间。它负责OK / Cancel选项的放置和本地化,并且是一种无需定义自己的类即可显示自定义对话框的快捷方法。大多数情况下,JOptionPane中的“