Полоса прокрутки в tkinter не работает с холстом

Я новичок в Python и tkinter, я хочу создать панель мониторинга с помощью tkinter. Я написал приведенный ниже код, чтобы добавить графики в tkinter, используя Matplotlib. Так как у меня 4 графика и я не могу уместить все, поэтому добавил горизонтальную полосу прокрутки, но почему-то она не работает. Пожалуйста, найдите приведенный ниже код.

import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import numpy as np

def calculate_gap(sum_of_demand, allocated, capacity):
    if sum_of_demand - allocated < 0:
        sum_of_not_demanded = abs(sum_of_demand - allocated)
    elif capacity - sum_of_demand > 0:
        sum_of_not_demanded = capacity - sum_of_demand
    else:
        sum_of_not_demanded = 0

    if capacity - allocated > 0:
        spare_capacity = capacity - allocated
    else:
        spare_capacity = 0

    if sum_of_demand - capacity > 0:
        skill_gap = sum_of_demand - capacity
    else:
        skill_gap = 0

    return sum_of_not_demanded, spare_capacity, skill_gap


def h_scroll(*args):
    print("In scroll")
    graph1_canvas.get_tk_widget().xview(*args)
    graph2_canvas.get_tk_widget().xview(*args)
    graph3_canvas.get_tk_widget().xview(*args)
    graph4_canvas.get_tk_widget().xview(*args)


def test_gap():
    sum_of_demand = 256
    allocated = 73
    capacity = 66
    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    width = 0.35
    below = np.array([sum_of_demand, allocated, capacity])
    above = np.array([sum_of_not_demanded, spare_capacity, skill_gap])

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph4_ax.bar(title, weight_count, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph4_canvas.draw()
    # canvas1.get_tk_widget().pack(side = "left",fill = "both",expand=True)
    graph4_canvas.get_tk_widget().pack(fill = "both", expand=True, side = "left")
    graph4_canvas.get_tk_widget().config(scrollregion=graph4_canvas.get_tk_widget().bbox("all"))
    graph4_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set,
                                         scrollregion=graph4_canvas.get_tk_widget().bbox("all"))
    # test_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set)
    # test_canvas.get_tk_widget().bind("<<Key>>", pop_up)


def graph_three():
    sum_of_demand = 256
    allocated = 73
    capacity = 66

    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    if sum_of_demand - capacity > 0:
        skill_gap = sum_of_demand - capacity
    else:
        skill_gap = 0

    width = 0.1

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph_ax.bar(title, weight_count, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph3_canvas.draw()
    # canvas1.get_tk_widget().pack(side = "left",fill = "both",expand=True)
    graph3_canvas.get_tk_widget().pack(fill = "both", expand=True, side = "left")
    graph3_canvas.get_tk_widget().config(scrollregion=graph3_canvas.get_tk_widget().bbox("all"))
    graph3_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set,
                                         scrollregion=graph3_canvas.get_tk_widget().bbox("all"))
    # mobile_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set)


def graph_two():
    sum_of_demand = 830
    allocated = 313
    capacity = 339
    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    width = 0.35

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph2_ax.bar(title, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph2_canvas.draw()
    # canvas1.get_tk_widget().pack(side = "left",fill = "both",expand=True)
    graph2_canvas.get_tk_widget().pack(fill = "both", expand=True, side = "left")
    graph2_canvas.get_tk_widget().config(scrollregion=graph2_canvas.get_tk_widget().bbox("all"))
    graph2_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set,
                                         scrollregion=graph2_canvas.get_tk_widget().bbox("all"))
    # be_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set)


def graph_one():
    sum_of_demand = 1631
    allocated = 778
    capacity = 795
    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    width = 0.35

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph1_ax.bar(title, weight_count, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph1_canvas.draw()
    graph1_canvas.get_tk_widget().config(scrollregion=graph1_canvas.get_tk_widget().bbox("all"))
    graph1_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set,
                                         scrollregion=graph1_canvas.get_tk_widget().bbox("all"))

    # horizontal_scrollbar.config(command=h_scroll())


root = tk.Tk()

# Main Frame
frame = ttk.Frame(root)
frame.pack()
# Data Frame
graph_frame = ttk.Labelframe(frame, text = "Skill Gap")
graph_frame.grid(side = tk.RIGHT, expand = True, fill = tk.BOTH)

horizontal_scrollbar = ttk.Scrollbar(graph_frame, orient=tk.HORIZONTAL, command=h_scroll)
horizontal_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)

# Canvas
graph1_fig, graph1_ax = plt.subplots(dpi=100)
graph1_canvas = FigureCanvasTkAgg(graph1_fig, graph_frame)
horizontal_scrollbar.config(command=graph1_canvas.get_tk_widget().xview)
graph1_canvas.get_tk_widget().config(yscrollcommand=horizontal_scrollbar.set)
# Canvas
graph2_fig, graph2_ax = plt.subplots(dpi=100)
graph2_canvas = FigureCanvasTkAgg(graph2_fig, graph_frame)

# Canvas
graph3_fig, graph_ax = plt.subplots(dpi=100)
graph3_canvas = FigureCanvasTkAgg(graph3_fig, graph_frame)

#  Canvas
graph4_fig, graph4_ax = plt.subplots(dpi=100)
graph4_canvas = FigureCanvasTkAgg(graph4_fig, graph_frame)

graph_one()
graph_two()
graph_three()
test_gap()
horizontal_scrollbar.config(command=h_scroll)

tk.mainloop()

Я ожидаю, что горизонтальная полоса прокрутки будет работать. И возможность прокручивать все графики.

🤔 А знаете ли вы, что...
Python является интерпретируемым языком программирования.


1
90
1

Ответ:

Решено

Tkitner можно прокручивать только Canvas, Listbox и Text, но не Frame

Если вы хотите прокручивать все элементы, вам нужно объединить их в один tkinter.Frame и поставить этот фрейм tkinter.Canvas и использовать tkinter.Scrollbar для прокрутки этого кадра на холсте.

Я взял один из своих кодов из ответа на ваш предыдущий вопрос.

Python — невозможно отображать более 4-5 гистограмм с помощью matplotlib — 1 ответ

и я добавил прокручиваемые кавны

import tkinter as tk
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt

# --- functions ---

def resize(event): 
    dashboard_canvas.configure(scrollregion=dashboard_canvas.bbox('all'))

# --- main ---

x = [1, 2, 3, 4]
y = [1, 2, 3, 4]
AS = [10 / 2 ** 0]

# ---

root = tk.Tk()
root.geometry("1000x1000")
root.title("eggs")

# ---

# canvas
dashboard_canvas = tk.Canvas(root)#, bg='#00c000')  # background color only to test its size
dashboard_inner_frame = tk.Frame(dashboard_canvas)
inner_frame_id = dashboard_canvas.create_window((0,0), window=dashboard_inner_frame, anchor='nw')
dashboard_canvas.pack(fill='both', expand=True)

# scrollbar
dasboard_scrollbar_x = tk.Scrollbar(root, orient='horizontal')
dasboard_scrollbar_x.pack(fill='x')

# join widgets 
dashboard_canvas.configure(xscrollcommand=dasboard_scrollbar_x.set)
dasboard_scrollbar_x['command'] = dashboard_canvas.xview

# resize scrollregion on canvas when plots will have size (and it will be after starting program and drawing plots)
dashboard_inner_frame.bind('<Configure>', resize)

#---

# put on dashboard_inner_frame
frame_top = tk.Frame(dashboard_inner_frame, width=2000)
frame_top.pack(fill='both', expand=True)

plots = []

for index in range(10):
    fig = Figure(dpi=100)
    ax = fig.add_subplot(111)
    ax.plot(x, y)
    fig.suptitle(f"Plot {index+1}")

    canvas = FigureCanvasTkAgg(fig, master=frame_top)
    canvas.draw()
    canvas.get_tk_widget().pack(side = "left", fill='both', expand=True)
    #canvas.get_tk_widget()['width'] = 1
    plots.append({'fig': fig, 'canvas': canvas})

root.mainloop()

Версия с кодом из текущего вопроса

import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import numpy as np


# --- functions ---

def resize(event): 
    dashboard_canvas.configure(scrollregion=dashboard_canvas.bbox('all'))


def calculate_gap(sum_of_demand, allocated, capacity):
    if sum_of_demand - allocated < 0:
        sum_of_not_demanded = abs(sum_of_demand - allocated)
    elif capacity - sum_of_demand > 0:
        sum_of_not_demanded = capacity - sum_of_demand
    else:
        sum_of_not_demanded = 0

    if capacity - allocated > 0:
        spare_capacity = capacity - allocated
    else:
        spare_capacity = 0

    if sum_of_demand - capacity > 0:
        skill_gap = sum_of_demand - capacity
    else:
        skill_gap = 0

    return sum_of_not_demanded, spare_capacity, skill_gap


def h_scroll(*args):
    print("In scroll")
    graph1_canvas.get_tk_widget().xview(*args)
    graph2_canvas.get_tk_widget().xview(*args)
    graph3_canvas.get_tk_widget().xview(*args)
    graph4_canvas.get_tk_widget().xview(*args)


def test_gap():
    sum_of_demand = 256
    allocated = 73
    capacity = 66
    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    width = 0.35
    below = np.array([sum_of_demand, allocated, capacity])
    above = np.array([sum_of_not_demanded, spare_capacity, skill_gap])

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph4_ax.bar(title, weight_count, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph4_canvas.draw()
    # canvas1.get_tk_widget().pack(side = "left",fill = "both",expand=True)
    graph4_canvas.get_tk_widget().pack(fill = "both", expand=True, side = "left")
    graph4_canvas.get_tk_widget().config(scrollregion=graph4_canvas.get_tk_widget().bbox("all"))
    # test_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set)
    # test_canvas.get_tk_widget().bind("<<Key>>", pop_up)


def graph_three():
    sum_of_demand = 256
    allocated = 73
    capacity = 66

    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    if sum_of_demand - capacity > 0:
        skill_gap = sum_of_demand - capacity
    else:
        skill_gap = 0

    width = 0.1

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph_ax.bar(title, weight_count, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph3_canvas.draw()
    # canvas1.get_tk_widget().pack(side = "left",fill = "both",expand=True)
    graph3_canvas.get_tk_widget().pack(fill = "both", expand=True, side = "left")
    graph3_canvas.get_tk_widget().config(scrollregion=graph3_canvas.get_tk_widget().bbox("all"))
    # mobile_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set)


def graph_two():
    sum_of_demand = 830
    allocated = 313
    capacity = 339
    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    width = 0.35

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph2_ax.bar(title, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph2_canvas.draw()
    # canvas1.get_tk_widget().pack(side = "left",fill = "both",expand=True)
    graph2_canvas.get_tk_widget().pack(fill = "both", expand=True, side = "left")
    graph2_canvas.get_tk_widget().config(scrollregion=graph2_canvas.get_tk_widget().bbox("all"))
    # be_canvas.get_tk_widget().config(xscrollcommand=horizontal_scrollbar.set)


def graph_one():
    sum_of_demand = 1631
    allocated = 778
    capacity = 795
    sum_of_not_demanded, spare_capacity, skill_gap = calculate_gap(sum_of_demand, allocated, capacity)

    width = 0.35

    weight_counts = {
        "Below": np.array([sum_of_demand, allocated, capacity]),
        "Above": np.array([sum_of_not_demanded, spare_capacity, skill_gap])
    }
    title = ['Demand', 'Allocated', 'Capacity']

    bottom = np.zeros(3)
    for boolean, weight_count in weight_counts.items():
        p = graph1_ax.bar(title, weight_count, width, label=boolean, bottom=bottom)
        bottom += weight_count

    graph1_canvas.draw()
    graph1_canvas.get_tk_widget().config(scrollregion=graph1_canvas.get_tk_widget().bbox("all"))

    # horizontal_scrollbar.config(command=h_scroll())


root = tk.Tk()

# Main Frame
frame = ttk.Frame(root)
frame.pack(expand=True, fill='both')

# Data Frame
graph_frame = ttk.Labelframe(frame, text = "Skill Gap")
graph_frame.pack(expand=True, fill='both')

# ---

# canvas
dashboard_canvas = tk.Canvas(graph_frame)#, bg='#00c000')  # background color only to test its size
dashboard_inner_frame = tk.Frame(dashboard_canvas)
inner_frame_id = dashboard_canvas.create_window((0,0), window=dashboard_inner_frame, anchor='nw')
dashboard_canvas.pack(fill='both', expand=True)

# scrollbar
dasboard_scrollbar_x = tk.Scrollbar(graph_frame, orient='horizontal')
dasboard_scrollbar_x.pack(fill='x')

# join widgets 
dashboard_canvas.configure(xscrollcommand=dasboard_scrollbar_x.set)
dasboard_scrollbar_x['command'] = dashboard_canvas.xview

# resize scrollregion on canvas when plots will have size (and it will be after starting program and drawing plots)
dashboard_inner_frame.bind('<Configure>', resize)

#-----

# Canvas
graph1_fig, graph1_ax = plt.subplots(dpi=100)
graph1_canvas = FigureCanvasTkAgg(graph1_fig, dashboard_inner_frame)

# Canvas
graph2_fig, graph2_ax = plt.subplots(dpi=100)
graph2_canvas = FigureCanvasTkAgg(graph2_fig, dashboard_inner_frame)

# Canvas
graph3_fig, graph_ax = plt.subplots(dpi=100)
graph3_canvas = FigureCanvasTkAgg(graph3_fig, dashboard_inner_frame)

#  Canvas
graph4_fig, graph4_ax = plt.subplots(dpi=100)
graph4_canvas = FigureCanvasTkAgg(graph4_fig, dashboard_inner_frame)

graph_one()
graph_two()
graph_three()
test_gap()

tk.mainloop()

Я убираю вашу полосу прокрутки и помещаю все файлы PictureCanvasTkAgg в один и тот же deskboard_inner_frame, а deskboard_canvas и deskboard_scrollbar в ваш label_frame.

Чтобы сделать его более контролируемым, вы можете сохранить как desckboard_cavnas, так иdeskboard_scrollbarin one frame (e.g.deskboardordeskboard_outer_frame) and then you have to put only one element in label_frame`


Код длинный, и его сложно объяснить, поэтому, возможно, используйте такой инструмент, как Meld, чтобы увидеть различия между вашей и моей версией.