簡體   English   中英

使用包幾何自動隱藏 Tkinter 畫布滾動條

[英]Autohide Tkinter canvas scrollbar with pack geometry

當不需要時,我需要有關自動隱藏 tkinter 滾動條的幫助。 我從 effbot.org 找到了這段代碼,它自動隱藏滾動條,但僅限於網格幾何。 在我的情況下,我沒有使用網格幾何。 這是我的代碼。

import Tkinter as tk
from Tkinter import *
import tkFont


class AutoScrollbar(Scrollbar):
    # a scrollbar that hides itself if it's not needed.  only
    # works if you use the grid geometry manager.
    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            # grid_remove is currently missing from Tkinter!
            self.tk.call("grid", "remove", self)
        else:
            self.grid()
        Scrollbar.set(self, lo, hi)
    def pack(self, **kw):
        raise TclError, "cannot use pack with this widget"
    def place(self, **kw):
        raise TclError, "cannot use place with this widget"


class MainWindow(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        w = 1200
        h = 650
        x = self.winfo_screenwidth()/2 - w/2
        y = self.winfo_screenheight()/2 - h/2
        self.geometry("%ix%i+%i+%i" % (w, h, x, y))

        self.mainTopFrame = Frame(self, height=75)
        self.mainTopFrame.pack(side=TOP, fill=X)
        self.canvas = Canvas(self, borderwidth=0, bg='#ffffff')
        self.mainBottomFrame = Frame(self.canvas, bg='#000000')
        self.yscroll = Scrollbar(self.canvas, orient=VERTICAL, command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.yscroll.set)
        self.canvas.pack(side=TOP, fill=BOTH, expand=1)
        self.yscroll.pack(side=RIGHT, fill=Y)
        self.canvas.create_window((4,4), window=self.mainBottomFrame, anchor=NW)
        self.mainBottomFrame.bind("<Configure>", self.onFrameConfigure)
        self.menuFrame = Frame(self.mainTopFrame, bg='#545454')
        self.menuFrame.pack(side=TOP, fill=BOTH, expand=True)

        self.container = Frame(self.mainBottomFrame)
        self.container.pack(side=TOP, fill=BOTH, expand=True)
        self.frames = {}
        for F in (MonitorPage, PlanPage, DataLogPage, HelpPage):
            self.frame = F(self.container, self)
            self.frames[F] = self.frame
            self.frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(MonitorPage)

    def show_frame(self, cont):
        self.frame = self.frames[cont]
        self.frame.tkraise()

    def onFrameConfigure(self, event):
        '''Reset the scroll region to encompass the inner frame'''
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

class AutoScrollbar(Scrollbar):
    # a scrollbar that hides itself if it's not needed.  only
    # works if you use the grid geometry manager.
    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            # grid_remove is currently missing from Tkinter!
            self.tk.call("grid", "remove", self)
        else:
            self.grid()
        Scrollbar.set(self, lo, hi)
    def pack(self, **kw):
        raise TclError, "cannot use pack with this widget"
    def place(self, **kw):
        raise TclError, "cannot use place with this widget"

class MonitorPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)


        self.labelFont = tkFont.Font(family="Fixedsys", size=15, weight=tkFont.BOLD)


        self.leftFrame0 = Frame(self, bg='#888888')
        self.leftFrame0.pack(side=LEFT, fill=BOTH)
        self.rightFrame0 = Frame(self, bg='#888888')
        self.rightFrame0.pack(side=RIGHT, fill=BOTH)
        self.upLftFrame0 = Frame(self.leftFrame0)
        self.upLftFrame0.pack(side=TOP, fill=BOTH, padx=10, pady=10)
        self.dnLftFrame0 = Frame(self.leftFrame0)
        self.dnLftFrame0.pack(side=BOTTOM, fill=BOTH, padx=10, pady=10)
        self.upLftLblFrame0 = tk.LabelFrame(self.upLftFrame0)
        self.upLftLblFrame0.pack(side=TOP, fill=BOTH, padx=5, pady=5)
        self.dnLftLblFrame0 = tk.LabelFrame(self.dnLftFrame0)
        self.dnLftLblFrame0.pack(side=BOTTOM, fill=BOTH, padx=5, pady=5)
        self.rtLblFrame0 = tk.LabelFrame(self.rightFrame0)
        self.rtLblFrame0.pack(side=TOP, fill=BOTH, padx=10, pady=10)



        self.label0 = Label(self.rtLblFrame0, height=40, width=70)
        self.label0.pack()
        self.label1 = Label(self.upLftLblFrame0, height=25, width=115)
        self.label1.pack()
        self.label2 = Label(self.dnLftLblFrame0, height=10, width=115)
        self.label2.pack()


class PlanPage(tk.Frame, MainWindow):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

class DataLogPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

class HelpPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)


if __name__ == '__main__':
    rungui = MainWindow()
    rungui.mainloop()

因此,即使在使用包幾何圖形時,我也想自動隱藏滾動條。我希望我的問題很清楚。 我對 python 和 tkinter 很陌生。

我改編了 effbot.org 的例子來打包方法:

from Tkinter import *

class AutoScrollbar(Scrollbar):
    # a scrollbar that hides itself if it's not needed.  only
    # works if you use the grid geometry manager.
    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            # grid_remove is currently missing from Tkinter!
            self.pack_forget()
        else:
            if self.cget("orient") == HORIZONTAL:
                self.pack(fill=X)
            else:
                self.pack(fill=Y)
        Scrollbar.set(self, lo, hi)
    def grid(self, **kw):
        raise TclError, "cannot use grid with this widget"
    def place(self, **kw):
        raise TclError, "cannot use place with this widget"

# create scrolled canvas

root = Tk()

hscrollbar = AutoScrollbar(root, orient=HORIZONTAL)
canvas = Canvas(root,
                xscrollcommand=hscrollbar.set)
canvas.pack(side=TOP, fill=BOTH, expand=True)
hscrollbar.pack()

hscrollbar.config(command=canvas.xview)

# make the canvas expandable
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)

# create canvas contents

frame = Frame(canvas)
frame.rowconfigure(1, weight=1)
frame.columnconfigure(1, weight=1)

rows = 5
for i in range(1,rows):
    for j in range(1,10):
        button = Button(frame, padx=7, pady=7, text="[%d,%d]" % (i,j))
        button.grid(row=i, column=j, sticky='news')

canvas.create_window(0, 0, anchor=NW, window=frame)

frame.update_idletasks()

canvas.config(scrollregion=canvas.bbox("all"))

root.mainloop()

我從 j_4321 改編了這個:

from functools import partial
import tkinter as tk


class AutoScrollbar(tk.Scrollbar):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)
        self.geometry_manager_add = lambda: None
        self.geometry_manager_forget = lambda: None

    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            self.geometry_manager_forget()
        else:
            self.geometry_manager_add()
        super().set(lo, hi)

    def grid(self, **kwargs):
        self.geometry_manager_add = partial(super().grid, **kwargs)
        self.geometry_manager_forget = super().grid_forget

    def pack(self, **kwargs):
        self.geometry_manager_add = partial(super().pack, **kwargs)
        self.geometry_manager_forget = super().pack_forget

    def place(self, **kwargs):
        self.geometry_manager_add = partial(super().place, **kwargs)
        self.geometry_manager_forget = super().place_forget

現在它適用於所有幾何管理器。 它會記住傳入的kwargs ,並在顯示滾動條時始終通過它們。

TheLizzard 的回答很好,但是當用戶調整大小到滾動條開始在可見和不可見之間擺動的區域時,它會受到鎖定。 這可以通過引入一個簡單的時間延遲來解決(我還添加了一點,以便用戶可以檢查滾動條的可見性,因為我沒有從 Scrollbar.info_ismapped() 獲得好的結果):

class AutoScrollbar(Scrollbar):
"""A subclass of `tkinter.Scrollbar` that automatically hides and shows itself as needed. If you need to
find out if an instance of this class is currently visible, check it's :py:attr:`visible` attribute.
"""
def __init__(self, master, **kwargs):
    ""
    super().__init__(master, **kwargs)
    self.geometry_manager_add = lambda: None # replaced by grid(), pack() or place()
    self.geometry_manager_forget = lambda: None
    self.visible:bool = False # needed because super().winfo_ismapped() doesn't work as expected... 
    "True iff the scrollbar is visible. Users should treat this as a read-only variable; it is only set by this class."
    self._delayTime = time.time()

@override
def set(self, lo, hi):
    if self._delay(): # do these checks only after a threshold time limit to prevent runaway oscilations in scrollbar visibility.
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            if self.visible:  # avoid calling the geometry manager too often
                self.geometry_manager_forget()
                self.visible = False
        else:
            if not self.visible: # avoid calling the geometry manager too often
                self.geometry_manager_add()
                self.visible = True
    super().set(lo, hi)
    
def _delay(self) -> bool:
    """This method prevents locking up due to bouncing between showing and hiding the scroll
    bars.  It should be called as part of the conditional to change the state of the scroll bars.
    It works by introducing a one second delay between state changes giving the user time to drag
    by the 'purgatory' in-between state, and at least preventing runaway oscilations from 
    locking up the GUI""" 
    now = time.time()
    if now - self._delayTime > 1.0:
        self._delayTime = now
        return True
    else:
        return False

@override
def grid(self, **kwargs):
    self.geometry_manager_add = partial(super().grid, **kwargs)
    self.geometry_manager_forget = super().grid_forget

@override
def pack(self, **kwargs):
    self.geometry_manager_add = partial(super().pack, **kwargs)
    self.geometry_manager_forget = super().pack_forget

@override
def place(self, **kwargs):
    self.geometry_manager_add = partial(super().place, **kwargs)
    self.geometry_manager_forget = super().place_forget

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM