Matplot Plot in TopLevel anzeigen

Fragen zu Tkinter.
Antworten
ChrisB
User
Beiträge: 12
Registriert: Sonntag 13. Februar 2022, 13:27

Hallo,

ich stehe gerade vor einem Problem was ich nicht gelöst bekomme:

Ich habe drei Module:
  • gui_analyze.py
  • gui_plot.py
  • keywordanalytics.py
Ich würde gerne von der gui_analyze.py (TopLevel) ein fenster aufrufen, welches ein Plot von matplot anzeigt.
Von der gui_analyze.py übergebe ich Eingaben, die dann mithilfe von einer Funktion in keywordanalytics.py ein Dataframe mithilfe von Pytrends abfragt. Dieses Dataframe übergebe ich dann dem Modul gui_plot.py, welches mir dann in einer weiteren TopLevel Form einen Graphen ausspucken soll.

Ich habe das bereits alles mal versucht ohne Tkinter. Das heißt, dass ich über die keywordanalytics.py den matplot anzeigt habe über

Code: Alles auswählen

plt.show()
. Mein Ziel ist aber nun, dass dieser Plot in einem extra Fenster (gui_plot.py) angezeigt wird.

Sobald ich jedoch das ausführe sehe ich für einen kurzen Moment das Fenster erscheinen, aber ohne Plot. Das Fenster schließt sich aber sofort wieder.

Jetzt bin ich mir nicht ganz sicher woran es liegt. Eine Fehlermeldung erhalte ich nämlich auch nicht.

Hier die Ausschnitte aus der Syntax:

gui_analyze.py

Code: Alles auswählen

import tkinter as tk
import gui_helpers
import gui_plot
from researchtool import keywordsanalytics

        self.btn_image_get_historical = tk.PhotoImage(
            file=gui_helpers.assets("btn_get_historical.png"))
        btn_get_historical = tk.Button(
            self,
            image=self.btn_image_get_historical,
            borderwidth=0,
            highlightthickness=0,
            command=lambda: (
                gui_plot.GUIPlot(
                    self,
                    keywordsanalytics.interest_of_time(
                        gui_helpers.get_selection(listbox_further_keywords),    # Get list of selected listbox Items
                        clicked.get()                                           # Dropdown selected Item: Timeframe
                    ),
                    gui_helpers.get_selection(listbox_further_keywords)
                )
            ),
            relief="flat"
        )
keywordanalytics.py

Code: Alles auswählen

from pytrends.request import TrendReq

def interest_of_time(keyword_list, timeframe):
    trend = TrendReq()
    keywords = keyword_list
    trend.build_payload(kw_list=keywords, cat=None, timeframe=timeframe, geo="")
    data = trend.interest_over_time()

    return data

gui_plot.py

Code: Alles auswählen

import tkinter as tk
import gui_helpers
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg)

class GUIPlot(tk.Toplevel):
    def __init__(self, parent, data, keywords):
        super().__init__(parent)

        self_width = 500
        self_height = 500

        center_x, center_y = gui_helpers.center_form(self, self_width, self_height)
        self.geometry(f"{self_width}x{self_height}+{center_x}+{center_y}")
        self.title("Research Tool - Figure")
        self.configure(bg="#EEEEEE")

        self.plot(data, keywords)

        self.resizable(True, True)

    def plot(self, data, keywords):
        figure = Figure(figsize=(50, 50), dpi=100)
        plt = figure.add_subplot(1, 1, 1)

        for keyword in keywords:
            plt.plot(data.index, data[keyword], label=keyword)
        plt.legend()

        canvas = FigureCanvasTkAgg(figure, master=self)
        canvas.draw()
        canvas.get_tk_widget().grid(row=0, column=0)

Kann es vllt. sein, dass ich von einer TopLevel Form nicht eine weitere TopLevel Form erstellen kann?
Das Fenster schließt sich nämlich auch, wenn ich die Funktion

Code: Alles auswählen

 self.plot(data, keywords)
auskommentiere.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Bitte poste ein vollständiges Minimalbeispiel, das Deinen Fehler zeigt, der Code den Du zeigst, ist dafür unbrauchbar, weil niemand hier das einfach ausprobieren kann.
Mein Minimalbeispiel funktioniert einwandfrei:

Code: Alles auswählen

import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class PlotWindow(tk.Toplevel):
    def __init__(self, data):
        tk.Toplevel.__init__(self)
        figure = Figure()
        axis = figure.add_subplot(1, 1, 1)
        axis.plot(data)
        canvas = FigureCanvasTkAgg(figure, master=self)
        canvas.draw()
        canvas.get_tk_widget().grid(row=0, column=0)

class MainWindow(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        tk.Button(self, text="Plot", command=self.plot).pack()

    def plot(self):
        PlotWindow([1,2,4])

def main():
    main_window = MainWindow()
    main_window.mainloop()

if __name__ == "__main__":
    main()
ChrisB
User
Beiträge: 12
Registriert: Sonntag 13. Februar 2022, 13:27

Vielen Dank für deine Antwort!
Ich habe das mal gemacht und ich muss sagen, ich bin verwirrter als vorher, da es mit dem Beispiel was ich erstellt habe auch funktioniert. Ich habe lediglich die Teile aus den vier verschiedenen Modulen zu einem zusammengefügt:

Jede Klasse stellt in meinem Fall eigentlich ein Modul dar, da es noch einige andere Elemente erhält.
Die Funktion interest_of_time ist in meinem Modul keywordanalytics.py. Da ich die grafischen Oberflächen gerne auch über Module trennen und die Hintergrund Informationsbeschaffung ebenso (wie in dem Fall die keywordanalytics.py)

Code: Alles auswählen

import tkinter as tk
from pytrends.request import TrendReq
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg)


class GUIPlot(tk.Toplevel):
    def __init__(self, parent, data, keywords):
        super().__init__(parent)

        self_width = 500
        self_height = 500
        self.geometry(f"{self_width}x{self_height}")
        self.title("Research Tool - Figure")
        self.configure(bg="#EEEEEE")

        self.plot(data, keywords)

        self.resizable(False, False)

    def plot(self, data, keywords):
        figure = Figure()
        plt = figure.add_subplot(1, 1, 1)

        for keyword in keywords:
            plt.plot(data.index, data[keyword], label=keyword)
        plt.legend()
        canvas = FigureCanvasTkAgg(figure, master=self)
        canvas.draw()
        canvas.get_tk_widget().grid(row=0, column=0)


class GUIAnalyze(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)

        self_width = 1200
        self_height = 800

        self.geometry(f"{self_width}x{self_height}")
        self.title("Research Tool - Keyword Analysis")
        self.configure(bg="#EEEEEE")

        btn_get_historical = tk.Button(
            self,
            borderwidth=0,
            highlightthickness=0,
            bg="#00ADB5",
            command=lambda: (
                GUIPlot(
                    self,
                    self.interest_of_time(
                        ["Python"],
                        "today 5-y"
                    ),
                    ["Python"]
                )
            ),
            relief="flat"
        )
        btn_get_historical.place(
            x=650.0,
            y=566.0,
            width=365.0,
            height=59.0
        )

    def interest_of_time(self, keyword_list, timeframe):
        trend = TrendReq()
        keywords = keyword_list
        trend.build_payload(kw_list=keywords, cat=None, timeframe=timeframe, geo="")
        data = trend.interest_over_time()

        return data


class GUIMainMenu(tk.Tk):
    def __init__(self):
        super().__init__()

        self_width = 800
        self_height = 500
        self.geometry(f"{self_width}x{self_height}")
        self.title("Research Tool")
        self.configure(bg="#EEEEEE")

        btn_analyze = tk.Button(
            self,
            borderwidth=0,
            bg="#00ADB5",
            highlightthickness=0,
            command=lambda: self.open_gui_analyze(),
            relief="flat"
        )

        btn_analyze.place(
            x=500.0,
            y=288.0,
            width=200.0,
            height=58.0
        )

    def open_gui_analyze(self):
        form_analyze = GUIAnalyze(self)
        form_analyze.grab_set()


if __name__ == "__main__":
    mainmenu = GUIMainMenu()
    mainmenu.mainloop()


Wenn ich es so ausführe funktioniert es. Gibt es vllt. Tipps, wie ich die Fehlersuche einschränken kann?
Ich glaube als nächsten Schritt werde ich versuchen erstmal die Klasse GUIPlot ohne irgendwelche Inhalte mal öffnen zu lassen in meinem Projekt mit den verschiedenen Modulen.
ChrisB
User
Beiträge: 12
Registriert: Sonntag 13. Februar 2022, 13:27

Gut, ich konnte es eingrenzen.

Es hat was mit meiner Funktion auf sich zum Zentrieren der Form. Wenn ich das weg lasse und den feste Größen gebe, wie in meinem kleinen Beispiel oben, funktioniert alles einwandfrei:

Es geht also um den Part aus meiner gui_Plot.py (bzw. das steht in jeder Klasse bei mir, die eine Form aufruft).

Code: Alles auswählen

        center_x, center_y = gui_helpers.center_form(self, self_width, self_height)
        self.geometry(f"{self_width}x{self_height}+{center_x}+{center_y}")
gui_helpers.py

Code: Alles auswählen

def center_form(form: tk, form_width: int, form_height: int):

    form.update_idletasks()
    screen_width = form.winfo_screenwidth()
    screen_height = form.winfo_screenheight()
    x_coordinate = (screen_width / 2) - (form_width / 2)
    y_coordinate = (screen_height / 2) - (form_height / 2)

    return int(x_coordinate), int(y_coordinate)

Ich habe meine Ermittlung der x und y Koordinate ausgelagert in einem modul gui_helpers.py, um meine Formen immer zu zentrieren.
Das klappt auch mit den TopLevel Formen, die ich von der gui_mainmenu.py ausführe.
Meine gui_Plot.py erstelle ich aber von der TopLevel Form gui_Analyze.py. Sie wird auch nicht vor der Form angezeigt, sondern dahinter und ich kann sie auch nicht separat schließen. Wenn ich die Form von gui_Analyze.py schließe schließt sie sich auch. Aber ich kann sie nicht separat schließen. Als würde sie gesperrt sein.

Eine Idee, wie ich die vor der Form bekomme und dass ich sie auch schließen kann, ohne die gui_Analyze schließen zu müssen? Es muss ja was mit TopLevel zu tun haben und meiner anfänglichen Vermutung, dass es was mit dem Aufrufen der TopLevel Form von einer TopLevel Form zu tun hat.
ChrisB
User
Beiträge: 12
Registriert: Sonntag 13. Februar 2022, 13:27

Keiner eine Idee, wie ich die Toplevel gui_plot vor der TopLevel gui_Analyze bekomme und auch, wie ich die dann schließen kann?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich würde ja sagen, lass den Quatsch weg, das Fenster in die Mitte zu setzen.

Wenn Du den Helfenden helfen willst, dann poste ein Minimalbeispiel, das Deinen Fehler zeigt; durch hunderte Zeilen Code und Fragmenten sich durchzuwühlen, haben wohl wenige Leute Lust.
ChrisB
User
Beiträge: 12
Registriert: Sonntag 13. Februar 2022, 13:27

Ich habe den Fehler gefunden: Ich habe schlichtweg

Code: Alles auswählen

grab.set()
vergessen.

Am Beispiel oben, muss also in der Klasse GUIAnalyze die Funktion erstellt werden (wie ich es auch in der Klasse GUIMainForm mache):

Code: Alles auswählen

    def open_gui_plot(self, list_sel, dropdown_sel):
        form_plot = gui_plot.GUIPlot(
            self,
            keywordsanalytics.interest_of_time(
                list_sel,
                dropdown_sel
            ),
            list_sel
        )
        form_plot.grab_set()
Das Beispiel von oben war exakt so kopiert von meinem Projekt. Das Beispiel von oben funktionierte. Aber hier habe ich auch kein

Code: Alles auswählen

grab_set()
gehabt.
Ich kann mir das zwar gerade nicht erklären, warum das in einem Modul funktioniert, aber in mehreren aufgeteilten nicht, aber ich beschwere mich jetzt nicht, dass es funktioniert.
Antworten