【问题标题】:How do I simulate a Scrollbar in tkInter Canvas如何在 tkInter Canvas 中模拟滚动条
【发布时间】:2019-08-04 17:23:17
【问题描述】:

我正在专门用 Python 3.7 tkInter 创建一个游戏启动器,我想制作自己的风格 Scrollbar(在 Windows 10(版本 1903)中)。

我尝试添加一个隐藏的Scrollbar,并且隐藏有效,但我无法模拟它:

    def scroll(self, i, reqHeight, vbarValue):
        print("vbarValue", vbarValue)
        value = -i / 1.4
        a1 = int(self.canvass.coords(self.scroll2)[1]) == 5
        a2 = value > 0
        a = not(a1 ^ a2)

        b1 = ((self.canvass.coords(self.scroll2)[3] > self.cHeight))
        b2 = value < 0
        b = not(b1 ^ b2)
        print(value, value < 0)
        print(a1, 5)
        print("====")
        print(a1, a2)
        print(a)
        print("----")
        print(b1, b2)
        print(b)
        print("====\n\n")
        print("OK")
        x1, y1, x2, y2 = self.canvass.coords(self.scroll2)
        _y1, _y2 = vbarValue
        print("1:",y1, y2)
        print("2:",_y1, _y2)
        print("3:",(_y2 - _y1) / 2 - y2)
        print("4:",(_y1 + (_y2 - _y1) / 120) * self.cHeight)
        print("5:",(_y1 + (_y2 - _y1) / 120) * self.cHeight - (y2 / y1))
        print("6:",((_y2 - _y1) / 120) * self.cHeight - y2* -i)
        print("7:",(_y1 + (_y2 - _y1) / 120))
        value = (_y1 + (_y2 - _y1) / 120) * self.cHeight / (y1 / y2)
        print("8:",(y2 / y1))
        # value = value - (y1 / y2)

        print("Dynamic Canvas Region Height:")
        print("DCRH:", self.cHeight)


        print("Value: %s", value)
        self.canvass.move(self.scroll2, 0, -y2)
        self.canvass.move(self.scroll2, 0, value)
        print("coords: %s" % self.canvass.coords(self.scroll2))
        print("reqHeight: %s" % reqHeight)

事件:



    def _bound_to_mousewheel(self, event):  # <Enter> Event
        self.canv.bind_all("<MouseWheel>", self._on_mousewheel)

    def _unbound_to_mousewheel(self, event):  # <Leave> Event
        self.canv.unbind_all("<MouseWheel>")

    def _on_mousewheel(self, event):  # <Configure> Event
        self.canv.yview_scroll(int(-1 * (event.delta / 120)), "units")
        self.scrollCommand(int(-1 * (event.delta / 120)), self.scrollwindow.winfo_reqheight(), self.vbar.get())

    def _configure_window(self, event):
        # update the scrollbars to match the size of the inner frame
        size = (self.scrollwindow.winfo_reqwidth(), self.scrollwindow.winfo_reqheight()+1)
        self.canv.config(scrollregion='0 0 %s %s' % size)
        # if self.scrollwindow.winfo_reqwidth() != self.canv.winfo_width():
        #     # update the canvas's width to fit the inner frame
        #     # self.canv.config(width=self.scrollwindow.winfo_reqwidth())
        # if self.scrollwindow.winfo_reqheight() != self.canv.winfo_height():
        #     # update the canvas's width to fit the inner frame
        #     # self.canv.config(height=self.scrollwindow.winfo_reqheight())

顺便说一句,self.scrollCommand(...) 和第一个代码上的滚动是一样的。

我希望得到canvas.move method 的一些xy 输出。

【问题讨论】:

  • 不清楚你在问什么。你为什么要创建一个隐藏的滚动条?你说你想模拟一个滚动条,但我没有看到真正模拟滚动条的代码。您是否在问如何在没有可见滚动条的情况下使用鼠标滚轮?你不需要隐藏的滚动条来做到这一点。

标签: python-3.x tkinter scrollbar tkinter-canvas


【解决方案1】:

如何在 Tkinter Canvas 中模拟滚动条

滚动条有一个定义明确的界面。要模拟滚动条,您需要做的就是实现这个接口。这很容易通过创建一个具有以下属性的类来完成:

  • 您需要定义set 方法,当正在滚动的小部件想要更新滚动条时调用该方法
  • 您需要添加鼠标绑定来调用由滚动条控制的小部件的yview 方法(或xview,如果创建水平小部件)。

如果你做了这两件事,你的滚动条可以像内置滚动条一样使用。

对于此答案的其余部分,我将假设您要模拟垂直滚动条。模拟水平滚动条的工作方式相同,但您处理的是“左”和“右”,而不是“顶部”和“底部”。

定义set方法

set 方法将使用两个分数调用。 The canonical documentation 是这样描述的:

此命令由滚动条的关联小部件调用,以告知滚动条有关小部件中的当前视图。该命令有两个参数,每个参数都是 0 到 1 之间的实数分数。分数描述了在相关小部件中可见的文档范围。例如,如果 first 为 0.2,last 为 0.4,则表示窗口中可见的文档的第一部分是通过文档的 20%,最后可见的部分是通过文档的 40%。

定义绑定

等式的另一半是当用户与滚动条交互以滚动另一个小部件时。发生这种情况的方式是滚动条应该调用要控制的小部件的yview 命令(例如:画布、文本、列表框等)。

您必须传递给命令的第一个参数是字符串“moveto”或“scroll”。

  • 如果是“moveto”,第二个参数是一个分数,表示从顶部滚动的数量。这通常在单击滚动条时调用,以立即将滚动条移动到新位置

  • 如果是“scroll”,第二个参数是一个表示数量的整数,第三个参数是字符串“units”或“pages”。 “单位”的定义是指yscrollincrement 选项的值。 “pages”代表窗口高度的 9/10。这通常在将鼠标拖到滚动条上时调用。

每个可滚动小部件的选项都在手册页中详细说明。

示例

以下是一个使用文本小部件的示例,以便您可以看到,当您键入时,滚动条正确地增长和缩小。如果您单击滚动条中的任意位置,它将滚动到文档中的该点。

为使示例简短,此代码不处理滚动条的拖动,它硬编码了许多可能应该可配置的值。重点是要说明模拟滚动条所需要做的就是创建一个类,该类具有set 方法并调用已连接小部件的yviewxview 方法。

一、滚动条类

import tkinter as tk

class CustomScrollbar(tk.Canvas):
    def __init__(self, parent, **kwargs):
        self.command = kwargs.pop("command", None)
        tk.Canvas.__init__(self, parent, **kwargs)

        # coordinates are irrelevant; they will be recomputed
        # in the 'set' method
        self.create_rectangle(0,0,1,1, fill="red", tags=("thumb",))
        self.bind("<ButtonPress-1>", self.on_click)

    def set(self, first, last):
        first = float(first)
        last = float(last)
        height = self.winfo_height()
        x0 = 2
        x1 = self.winfo_width()-2
        y0 = max(int(height * first), 0)
        y1 = min(int(height * last), height)
        self.coords("thumb", x0, y0, x1, y1)

    def on_click(self, event):
        y = event.y / self.winfo_height()
        self.command("moveto", y)

在程序中使用类

您可以像使用原生滚动条一样使用此类:实例化它,并将命令设置为可滚动小部件的yview 命令。

此示例使用文本小部件,因此您可以在键入时看到滚动条更新,但完全相同的代码适用于 Canvas 或任何其他可滚动窗口。

root = tk.Tk()

text = tk.Text(root)
sb = CustomScrollbar(root, width=20, command=text.yview)
text.configure(yscrollcommand=sb.set)

sb.pack(side="right", fill="y")
text.pack(side="left", fill="both", expand=True)

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

root.mainloop()

【讨论】:

  • 感谢您的帮助,我的程序运行良好。而且我还添加了 Foreground fg 选项
  • redraw 方法有必要吗?如果是这样,什么/什么时候应该调用.redraw
  • @TheLizzard:没有。我不知道它为什么在那里。我已经删除了。
猜你喜欢
  • 2021-05-23
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 2021-11-13
  • 1970-01-01
  • 1970-01-01
  • 2020-07-07
  • 2018-04-25
相关资源
最近更新 更多