0

I am trying to combine my graphs from matplot into the tkinter window and be able to navigate through the different graphs. I have tried experimenting for now and have one graph on it. However, I get 2 windows from tkinter instead of 1.

enter image description here

I've inserted the code I have done so far:

import tkinter as tk
from tkinter import ttk
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
import itertools
import copy
import networkx as nx
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, 
NavigationToolbar2Tk
from tkinter import *
import tkinter.messagebox as tm


LARGE_FONT=("Verdana", 12) #font type and font size

df1= pd.read_csv(r"U:\\GE90\nodes_fixed.csv")

df2 = pd.read_csv(r"U:\\GE90\edge_list_3_fixed.csv")

g=nx.Graph()
# Add edges and edge attributes
for i, elrow in df2.iterrows():
# g.add_edge(elrow[0], elrow[1], attr_dict=elrow[2:].to_dict())  # 
deprecated after NX 1.11
g.add_edge(elrow[0], elrow[1], **elrow[2:].to_dict())

app=Tk()

class Trial(tk.Tk):

#self -implied but does not need to be passed at all depending on the 
structure
#* = args   --> arguments, unlimited number of variables --> can pass 
through as many variables as you want
#** = kwargs --> keyboard arguments, passing through dictionaries 
def __init__(self, *args, **kwargs):

    tk.Tk.__init__(self, *args, **kwargs)
    tk.Tk.wm_title(self, "Trial Only")
    container = tk.Frame(self)

    container.pack(side="top", fill="both", expand = True)

    container.grid_rowconfigure(0, weight=1)
    container.grid_columnconfigure(0, weight=1)

    self.frames = {}

    for F in (StartPage, PageOne, PageTwo, plot):

        frame = F(container, self)

        self.frames[F] = frame

        frame.grid(row=0, column=0, sticky="nsew")

    self.show_frame(StartPage)

def show_frame(self, cont):

    frame = self.frames[cont]
    frame.tkraise()



class StartPage(tk.Frame):

def __init__(self, parent, controller):
    tk.Frame.__init__(self,parent)
    label = tk.Label(self, text="Start Page", font=LARGE_FONT)
    label.pack(pady=10,padx=10)

    button = ttk.Button(self, text="Visit Page 1",
                       command=lambda: controller.show_frame(PageOne))
    button.pack()

#Adding a page

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

    tk.Frame.__init__(self, parent)
    label = tk.Label(self, text="Page One", font=LARGE_FONT)
    label.pack(pady=10,padx=10)

    button1 = ttk.Button(self, text="Back to home",
                           command=lambda: 
controller.show_frame(StartPage))
    button1.pack()

    button2 = ttk.Button(self, text="Visit page two",
                        command=lambda: controller.show_frame(PageTwo))
    button2.pack()

class PageTwo(tk.Frame):

def __init__(self, parent, controller):
    tk.Frame.__init__(self, parent)
    label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
    label.pack(pady=10,padx=10)

    button3 = ttk.Button(self, text="Back to Home",
                        command=lambda: controller.show_frame(StartPage))
    button3.pack()

    button4 = ttk.Button(self, text="Page One",
                        command=lambda: controller.show_frame(PageOne))
    button4.pack()

    button5 = ttk.Button(self, text="Visit Page 3",
                        command=lambda: controller.show_frame(PageThree))
    button5.pack()

class plot (tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label= tl.Label(self, text= "Figure 1", font = LARGE_FONT)
        label.pack(pady=10,padx=10)


# Edge list example
        print(elrow[0]) # node1
        print(elrow[1]) # node2
        print(elrow[2:].to_dict()) # edge attribute dict


# Add node attributes
for i, nlrow in df1.iterrows():
# g.node[nlrow['id']] = nlrow[1:].to_dict()  # deprecated after NX 1.11
nx.set_node_attributes(g, {nlrow['ID']:  nlrow[1:].to_dict()}) 

# Node list example
print(nlrow)

# Preview first 5 edges

list(g.edges(data=True))[0:5] 

# Preview first 10 nodes

list(g.nodes(data=True))[0:10] 

print('# of edges: {}'.format(g.number_of_edges()))
print('# of nodes: {}'.format(g.number_of_nodes()))

# Define node positions data structure (dict) for plotting
for node in g.nodes(data=True):
print(node)
print("")
node_positions = {node[0]: (node[1]['X'], -node[1]['Y']) for node in 
g.nodes(data=True)}

# Preview of node_positions
dict(list(node_positions.items())[0:5])

# Define data structure (list) of edge colors for plotting

# edge_colors = [e[2]['color'] for e in g.edges(data=True)]  
edge_colors = [e[2]['color'] for e in list(g.edges(data=True))]

# Preview first 10
edge_colors[0:10]
fig = plt.figure(figsize=(8, 6))
nx.draw(g, pos=node_positions, edge_color=edge_colors, node_size=10, 
node_color='black')
plt.title('Graph Representation of repair trail', size=15)




canvas = FigureCanvasTkAgg(fig, app)
canvas.get_tk_widget().pack()
canvas.draw()


app = Trial()
app.mainloop()

I want to display one window from tkinter to display all the different pages, however, the output gives me 2 different windows from tkinter 1st with all the starting pages and the buttons 2nd just with the graph from class plot

Kaori21
  • 43
  • 11
  • 1
    You create a first window with `app=Tk()`, then a second one when you init `Trial` because it inherits from `Tk`. I think you can just remove the line `app=Tk()` since you override it later with `app = Trial()`. BTW there are indentation issues in your code and you have posted more code than necessary for a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). – j_4321 Aug 14 '19 at 12:49
  • @j_4321 My apologies for the messy coding and too much code being posted. I am new coding and being in stack overflow. I would like to ask further about replacing app=Tk() with app=Trial(). I have just tried it on my code and it gave me an error NameError: name 'app' is not defined – Kaori21 Aug 14 '19 at 12:59
  • Then please fix the indentation in the question. Since you have an error, this means that you are using `app` before `app = Trial()`. Either you want `Trial` to be the root window, and in this case, create it before `canvas = FigureCanvasTkAgg(fig, app)` or you don't and in this case make `Trial` inherit from `Frame` and pack it in app. – j_4321 Aug 14 '19 at 13:05
  • @j_4321 I have done this and it did combine both, however, I cannot navigate away from the plot and it shows despite clicking through to different pages – Kaori21 Aug 14 '19 at 13:26
  • You need to create the plot in one of the pages: create `Trial` first, then create your graph with `canvas = FigureCanvasTkAgg(fig, )` replacing master by your PageOne for instance (the one stored in `app.frames`). It seems like you are trying to put together pieces of code you don't understand, so I would advise you to take the time to analyze each piece of code separately and have a look at simple examples of OOP with tkinter first. – j_4321 Aug 14 '19 at 13:32
  • @j_4321 I will try that and I will do that further. I have watched a tutorial on youtube about integrating matlab plots in tkinter but I got confused as they entered values themselves and didn't fully understand how to do it for this specific code. Thank you again for your help. – Kaori21 Aug 14 '19 at 13:37

2 Answers2

1

You get two windows because you create a first window with app = Tk(), then a second one when you init Trial because it inherits from Tk. In fact you don't need app = Tk().

You want your plot to be in one of the pages, so you need to move all the code creating the matplotlib figure inside one of your Page class, e.g. PageTwo:

class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Page Two")
        label.pack(pady=10, padx=10)

        # code for the plot
        fig = plt.figure(figsize=(8, 6))  # create matplotlib figure
        # add axes and plot (replace this by your matplotlib code)
        ax = fig.subplots() 
        ax.plot(range(10))
        ax.set_title('Graph Representation of repair trail', size=15)
        # create the tkinter widget to display the figure
        canvas = FigureCanvasTkAgg(fig, self)  # here self is the widget in which you want to display the figure
        canvas.get_tk_widget().pack()
        toolbar = NavigationToolbar2Tk(canvas, self)  # add toolbar
        canvas.draw()  # show content

        # navigation button 
        button3 = ttk.Button(self, text="Back to Home",
                            command=lambda: controller.show_frame(StartPage))
        button3.pack()

        button4 = ttk.Button(self, text="Page One",
                            command=lambda: controller.show_frame(PageOne))
        button4.pack()
j_4321
  • 15,431
  • 3
  • 34
  • 61
  • Could I possible ask what "canvas.get_tk_widget().pack()" does? what is .get_tk_widget? – Kaori21 Aug 14 '19 at 14:15
  • @ElizabethDC `canvas.get_tk_widget()` returns the tkinter widget containing the matplotlib figure. `pack` is the method used to display this widget in the page, you could also use `grid` or `place` instead. – j_4321 Aug 14 '19 at 14:29
  • I have replaced the code to this: `fig = Figure() canvas= plt.figure(figsize=(8, 6)) nx.draw(g, pos=node_positions, edge_color=edge_colors, node_size=10, node_color='black') plt.title('Graph Representation of repair trail', size=15) toolbar = NavigationToolbar2Tk(canvas, self) canvas.draw()'` Please do correct me if I am wrong, but fig=Figure() provides a blank canvas? I have tried this but : `AttributeError: 'NoneType' object has no attribute 'bbox'` I am not sure where bbox is coming from? – Kaori21 Aug 14 '19 at 15:02
  • According to the code you posted, you should have `fig = plt.figure(figsize=(8, 6))` and `canvas = FigureCanvasTkAgg(fig, self)`. Not `canvas= plt.figure(figsize=(8, 6))` because `canvas` is the `FigureCanvasTkAgg` containing the figure, not the matplotlib figure. – j_4321 Aug 14 '19 at 15:07
  • That makes sense to me, however,I don't understand why I now get an error that `NameError: name 'fig' is not defined` ? we have defined fig to be `fig = plt.figure(figsize=(8, 6))`? so why does this return fig is not defined? I have just moved all the other code for the calculations on the top of the code, would this have affected it? – Kaori21 Aug 14 '19 at 15:24
  • You probably use `fig` somewhere else in your code, either outside the `PageTwo` class (in this case move this piece of code inside the class) or before `fig = ...` in the class (then move it after). – j_4321 Aug 14 '19 at 15:46
  • I have tried that but now I am getting an error saying `name 'self' is not defined` I watched a video and all it mentioned was self is implied but does not need to be passed at all depending on the structure. What does it mean when it says that `self` is not defined? and what actually is `self`? – Kaori21 Aug 15 '19 at 06:49
  • If you are asking this question then you need to read a tuto about object oriented programming. See also this [question](https://stackoverflow.com/questions/2709821/what-is-the-purpose-of-the-word-self-in-python). – j_4321 Aug 15 '19 at 07:03
  • Thank you for the link, I have now fixed it and managed to get it into a page as I have wanted – Kaori21 Aug 15 '19 at 07:06
0

You are explicitly creating two windows.

The first is this line: app=Tk()

The second is the line app = Trial(), since Trial inherits from tk.Tk.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • If I move app=Trial() above the canvas lines, it does combine them together but I cannot change pages away from the figure – Kaori21 Aug 14 '19 at 13:24