Pyplot и Tkinter — нежелательное дополнительное окно

#python #matplotlib #tkinter

#python #matplotlib #tkinter

Вопрос:

Мой приведенный ниже код создает нежелательное дублирующее окно, когда я пытаюсь добавить заголовок окна с помощью plt.figure().canvas.manager.set_window_title("Custom Title") .

Я провел некоторое исследование и обнаружил, что я, вероятно, не должен смешивать pyplot и tkinter таким образом, поскольку они путаются. Однако я не мог разобраться в предложенных решениях, некоторые из которых использовали что-то под названием FigureCanvasTkAgg , о котором я не знаю. Я хочу, чтобы мой график был автономным, так же, как и при удалении plt.figure().canvas.manager.set_window_title("Custom Title") .

Как я могу реорганизовать свой код, пожалуйста, чтобы не нарушать какие-либо принципы, которые выполняет мой текущий код, и удалить нежелательное окно?

 import matplotlib.pyplot as plt
import tkinter as tk
import networkx as nx


NUM_ROWS = 5
BOLD_FONT = ("calbri", 12, "bold")
NORMAL_FONT = ("calbri", 12, "normal")


def create_widgets():
    for i in range(NUM_ROWS):
        key = chr(i   65)
        this_row = widgets[key] = {}
        this_row["label"] = tk.Label(root, text=key, font=BOLD_FONT)
        this_row["label"].grid(row=i, column=0, padx=5, pady=10)
        this_row["factor_field"] = tk.Entry(root, width=60, font=NORMAL_FONT)
        this_row["factor_field"].grid(row=i, column=1, padx=5, pady=10)
        this_row["target_node_field"] = tk.Entry(
            root, width=5, font=NORMAL_FONT)
        this_row["target_node_field"].grid(row=i, column=2, padx=5, pady=10)
        
    submit_button = tk.Button(root, text="Submit", command=submit,
                              font=BOLD_FONT).grid(row=NUM_ROWS   1, column=0, padx=5, pady=10)


def submit():
    plt.close()
    G = nx.DiGraph()
    edges = []
    for key, row in widgets.items():
        factor_field_contents = row["factor_field"].get()
        target_node_field_contents = row["target_node_field"].get().upper()
        if factor_field_contents != "" and target_node_field_contents != "":
            edges.append((key, target_node_field_contents))
            data[key] = {"factor": factor_field_contents,
                         "target_node": target_node_field_contents}
    G.add_edges_from(edges)
    # pos = nx.spring_layout(G, k=1.0, iterations=50)
    pos = nx.spring_layout(G)
    nx.draw_networkx_nodes(G, pos, node_size=500, node_color="green")
    nx.draw_networkx_labels(G, pos, font_color="white")
    nx.draw_networkx_edges(
        G, pos, connectionstyle='arc3, rad = 0.1', width=2, arrows=True)
    plt.figure().canvas.manager.set_window_title("Custom Title")
    plt.show()


if __name__ == "__main__":
    data = {}
    widgets = {}
    root = tk.Tk()
    root.title("My App")
    create_widgets()
    root.mainloop()

  

Ответ №1:

Вы сказали, что нашли решение в FigureCanvasTkAgg , но вы этого не понимаете. Вы должны получить это понимание, потому что это буквально единственный способ (согласно моим исследованиям). Мой пример должен помочь вам начать понимать это. Честно говоря, я вообще ничего не знаю о matplotlib . Я только что прочитал документы и выполнил требования. Кажется, это отлично работает при создании единого окна.

 #import matplotlib.pyplot as plt #remove this, you can't use it anymore
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import tkinter as tk
import networkx as nx


NUM_ROWS = 5
BOLD_FONT = ("calbri", 12, "bold")
NORMAL_FONT = ("calbri", 12, "normal")


def create_widgets():
    for i in range(NUM_ROWS):
        key = chr(i   65)
        this_row = widgets[key] = {}
        this_row["label"] = tk.Label(root, text=key, font=BOLD_FONT)
        this_row["label"].grid(row=i, column=0, padx=5, pady=10)
        this_row["factor_field"] = tk.Entry(root, width=60, font=NORMAL_FONT)
        this_row["factor_field"].grid(row=i, column=1, padx=5, pady=10)
        this_row["target_node_field"] = tk.Entry(
            root, width=5, font=NORMAL_FONT)
        this_row["target_node_field"].grid(row=i, column=2, padx=5, pady=10)
        
    submit_button = tk.Button(root, text="Submit", command=submit,
                              font=BOLD_FONT).grid(row=NUM_ROWS   1, column=0, padx=5, pady=10)

#this is your single window
#I'm sure some of this could be made just once and reused
#I'm also sure this could be made more dynamic
#At least you have the multi-window part solved
def plotter():
    global plotwin
    plotwin = tk.Toplevel(root)
    
    fig     = Figure(figsize=(5,5), dpi=100)
    fig.add_subplot(111)
    
    canvas  = FigureCanvasTkAgg(fig, plotwin)
    canvas._tkcanvas.pack(fill='both', expand=True)
    
    NavigationToolbar2Tk(canvas, plotwin, pack_toolbar=True).update()

def submit():
    try:
        #if plotter() hasn't been called yet this will throw a NameError
        #we simply catch and ignore it
        plotwin.destroy()
    except NameError as e:
        pass
        
    G = nx.DiGraph()
    edges = []
    for key, row in widgets.items():
        factor_field_contents = row["factor_field"].get()
        target_node_field_contents = row["target_node_field"].get().upper()
        if factor_field_contents != "" and target_node_field_contents != "":
            edges.append((key, target_node_field_contents))
            data[key] = {"factor": factor_field_contents,
                         "target_node": target_node_field_contents}
    G.add_edges_from(edges)
    # pos = nx.spring_layout(G, k=1.0, iterations=50)
    pos = nx.spring_layout(G)
    nx.draw_networkx_nodes(G, pos, node_size=500, node_color="green")
    nx.draw_networkx_labels(G, pos, font_color="white")
    nx.draw_networkx_edges(G, pos, connectionstyle='arc3, rad = 0.1', width=2, arrows=True)

    #instantiate window
    plotter()

if __name__ == "__main__":
    data = {}
    widgets = {}
    root = tk.Tk()
    root.title("My App")
    create_widgets()
    root.mainloop()
  

Комментарии:

1. Вы могли бы прочитать документы, подобные моему Q тоже: ( lol xp

2. @CoolCloud ~ Не совсем. Я не могу заставить selenium правильно работать с py 3.8, что означает, что я не могу протестировать selenium производный код. Для этого вопроса у меня уже было все, кроме networkx ~ который установлен без сучка и задоринки.

3. На самом деле часть selenium в порядке, я просто использую ее для аутентификации и токена доступа, я могу напрямую предоставить их вам;)

4. @CoolCloud ~ может быть, завтра. Я работаю над ctk . Хотите верьте, хотите нет, у меня есть свои собственные проекты. ctk содержит до 29 пользовательских виджетов. Некоторые из ваших вопросов на самом деле ответственны за пару из них. На самом деле, многие вопросы от многочисленных пользователей отвечают, вероятно, за половину из них. Я даже взял это matplotlib окно и классифицировал его. Мне нужно изучить немного больше, чтобы понять, какой интерфейс создать для него, чтобы сделать его повторно используемым.

5. Все в порядке, не торопитесь, просто дайте мне знать, когда освободитесь для этого: D и всего наилучшего!