Полоса прокрутки в 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()

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

если вы хотите переместить все элементы, вам следует поместить их все в один tkinter.Frame, поместить этот фрейм в tkinter.Canvas и прокрутить этот фрейм внутри Canvas - но вы пытаетесь прокручивать контент в FigureCanvasTkAgg. Но у него может быть сложная система, позволяющая держать сюжет в центре. Может иметь собственную функцию для прокрутки данных на графике - Python - Прокрутка графиков - GeeksforGeeks

furas 23.06.2024 12:21

можно ли обойтись без Canvas?

Chandrakanth Reddy 23.06.2024 13:37

ты не можешь. В tkitner Scrollbar работает только с Canvas, Listbox, Text, но не с Farme. Итак, вам нужно поместить рамку на холст и прокрутить холст. У меня уже есть рабочий пример с (коротким) кодом из предыдущего вопроса.

furas 23.06.2024 14:04
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
90
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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, чтобы увидеть различия между вашей и моей версией.

Я попробовал это, добавив два кадра (Frame1 и Frame2), но поле холста стало очень маленьким. Pack(side=tk.TOP,expand=True)frame2.pack(side=tk.BOTTOM,expand=True) # холст Dashboard_canvas = tk.Canvas(frame1)#, bg='#00c000') # цвет фона только для проверьте его размер Dashboard_inner_frame = tk.Frame(dashboard_canvas) Internal_frame_id = Dashboard_canvas.create_window((0,0), window=dashboard_inner_frame,nchor='nw') Dashboard_canvas.pack(fill='both',expand=True) Не могли бы вы, пожалуйста? Помоги мне

Chandrakanth Reddy 23.06.2024 14:57

В моем случае мне нужно держать холст внутри фрейма (а не в корне)

Chandrakanth Reddy 23.06.2024 15:12

сначала поместите все в один кадр, а затем поместите этот кадр на холст. Лучше поставьте код под вопрос — он будет более читабельным. И у вас будет больше места для описания проблемы и отображения сообщений об ошибках.

furas 23.06.2024 15:29

Спасибо Фурас за поддержку. Я сохранил код в новом протекторе. Пожалуйста, посмотрите. Я тоже пытался сохранить фрейм верхнего уровня, но он тоже не работает должным образом. stackoverflow.com/questions/78658845/…

Chandrakanth Reddy 23.06.2024 15:41

тем временем я сделал пример с вашим кодом и поместил его в текущий ответ.

furas 23.06.2024 15:49

Спасибо Фурас, все получилось. Спасибо за поддержку.

Chandrakanth Reddy 23.06.2024 16:00

Другие вопросы по теме