0

The need for two different geometric plotters arises because i want to display a sophisticated GUI with text, entry fields, pictures, buttons, alongside an animated matplotlib graph on a FigureCanvasTkAgg with a NavigationToolbar2Tk. The NavigationToolbar2Tk fails to work when any geometric plotter other than pack() is used. I have tried over the course of the week several different methods of putting the NavigationToolbar2Tk into its own frame, but in the examples, only a single frame exists. I came to the conclusion that ideally, splitting my frame ~ PageOne ~ into two subframes or instantiating two frames which make up page one.

I have several frames as shown in my code below and my knowledge of python is rudimentary so i don't have the imagination or know how to circumvent this problem. I have gutted my code to show the problem concisely and Included my Imports.

import matplotlib
matplotlib.use("TkAgg")

from   matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from   matplotlib.figure import Figure
from   matplotlib.pylab  import *
from   matplotlib        import style
import matplotlib.animation as animation
import matplotlib.pyplot    as plt
import matplotlib.dates     as dates
import matplotlib.ticker    as mticker

import tkinter as tk
from tkinter import ttk
from tkinter import *

from PIL import Image, ImageTk
from mpl_toolkits.axes_grid1 import host_subplot
from gpiozero import CPUTemperature

import time
import datetime as dt
import pandas   as pd        
import numpy    as np


#************************************************************************#
# Format Graph_1 [ax1] onto fig1 at subplot [ row = 1, col = 1, index = 1 ]

# Set Figure Text 
font = {'size' : 9}
matplotlib.rc('font', **font)

# Setup Figure
fig1 = Figure()

# Define Axis 1 for Vin and Vout 
ax1 = fig1.add_subplot(1, 1, 1)
#subplots_adjust(left=0.05, bottom=0.10, right=0.55, top=0.8, wspace=0.2)
ax1.minorticks_on()
ax1.grid(b=True, which='major', color='k', linestyle='-')
ax1.grid(b=True, which='minor', color='k', linestyle=':')
ax1.set_title("PI3740 Paramaters", fontsize = 12)
ax1.set_xlabel("Relative Time (s)", fontsize = 10)
ax1.set_ylabel("Voltage (V)", fontsize =10)

# Define Axis 2 for Iout Which is tied to Axis 1's X-Axis
ax2 = ax1.twinx() 
ax2.set_ylabel("Output Current (A)")

# Parameters
x_len = 500                         # Resolution [Number of Points in Window]
x_max = 2                          # X-Axis Range [ (Step)ms Samp^-1 * (x_len)Samp = X_Range]  
y_range = [0, 50]                   # Range of possible Y values to display

# Create figure for plotting
steps = (x_max/x_len)
stepms = steps * 1000
xs = np.arange(0, x_max, steps)     # xs is a list from 0 to 10 in steps of 0.01 [A list refers to a 1D Array]
ys1 = [0] * x_len                   # ys is a list indexed from ys[0] to ys[999] all containing 0 # Vin
ys2 = [0] * x_len                   # ys is a list indexed from ys[0] to ys[999] all containing 0 # Vout
ys3 = [0] * x_len                   # ys is a list indexed from ys[0] to ys[999] all containing 0 # Iout
ax1.set_ylim(y_range)               # Y-Axis Voltage Range Set
ax2.set_ylim(0, 10)                 # Y-Axis Current Range Set
ax1.set_xlim(0, x_max)              # X-Axis Shared Relative Time Range Set

# Create a blank line. We will update the line in animate
line1, = ax1.plot(xs, ys1, 'b-', label = "Vin")
line2, = ax1.plot(xs, ys2, 'g-', label = "Vout")
line3, = ax2.plot(xs, ys3, 'r-', label = "Iout")

# Create a Legend 
ax1.legend([line1, line2],[line1.get_label(), line2.get_label()])
ax1.legend(bbox_to_anchor = (0.,0.99,1.,.102), loc = 3, ncol = 2, borderaxespad = 0., frameon = False)
ax2.legend([line3],[line3.get_label()])
ax2.legend(bbox_to_anchor = (1.00,0.99), loc = 'lower right', borderaxespad = 0., frameon = False)

#************************************************************************#
#**********************Animation Function********************************#

# This function is called periodically from FuncAnimation
def updateData(self):

    # Drop down menu event flags
    global ChartLoad

    # Graph variables
    global xs
    global ys1
    global ys2
    global ys3

    if ChartLoad == True:
        # Read temperature (Celsius) from TMP102
        temp_c = cpu.temperature
        temp_c1 = temp_c + 5.0
        temp_c2 = temp_c - 35.0
        # Add y to list
        ys1.append(temp_c)
        ys2.append(temp_c1)
        ys3.append(temp_c2)

        # Limit y list to set number of items
        ys1 = ys1[-x_len:]
        ys2 = ys2[-x_len:]
        ys3 = ys3[-x_len:] 

        # Update line with new Y values
        line1.set_ydata(ys1)
        line2.set_ydata(ys2)
        line3.set_ydata(ys3)

    return line1, line2, line3,

#************************************************************************#
#*******************Tkinter Window Initalization*************************#

class MyApp(tk.Tk):                                               
    def __init__(self, *args, **kwargs):                       

        tk.Tk.__init__(self, *args, **kwargs)                          
        tk.Tk.wm_title(self,"EMEA Vicor Charging Application ")
        img=tk.PhotoImage(file='/home/pi/Pictures/Vicor_Icon1.png')
        self.tk.call('wm','iconphoto',self._w,img)

        container = tk.Frame(self)                                                

        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)               
        container.grid_columnconfigure(0, weight=1)

    #*********************************************************
    #******Function Required to Display Seperate Pages********
        self.frames = {}
        for F in (StartPage, HomePage, PageOne):                  
            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()

    #*********************************************************
    #*********************************************************

#Start Page - Agreement
class StartPage(tk.Frame):                                            

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

#Page One - Primary Terminal 
class PageOne(tk.Frame):                                            

    def __init__(self, parent, controller):                        
        tk.Frame.__init__(self, parent)
        tk.Frame.__init__(self, parent, bg = "white")

        # Two Frames Need to be Established on Page One (Which is a “Higher Order Frame”)

        my_window = Tk()
        frame_name = Frame(my_window)
        frame_addr = Frame(my_window)

        label_f = Label(frame_name, text = "Check")
        label_f.grid(row = 0, column = 0)

        label_g = Label(frame_addr, text = "Correct")
        label_g.grid(row = 0, column = 0)

        frame_name.grid(row = 0, column = 0)
        frame_addr.grid(row = 0, column = 1)

        #my_window.mainloop()



app = MyApp()
app.geometry("1010x700")
ani = animation.FuncAnimation(fig1, updateData, blit = True, interval = stepms)
app.mainloop()

Page One will contain my Canvas for the Graph and Tool Bar. however to simplify the problem into its fundamental, getting two frames to make up "Higher Order" frame PageOne, with each subframe containing a label. When I run the code, another window opens with the two labels displayed. This is more progress than from other solutions that i can't implement/don't understand, producing error messages i can't respond to. I am self taught at python, and have been following tutorials from the community. I just need some help implementing the solution. If I've gutted my code to much i can provide a more comprehensive code snippet. But the essence of the problem is, while displaying one of several frames on a Tkinter Window which move to the foreground when requested, how do you split one of these "High Order" frames into two frames as to allow two different geometric plotters to be used to structure each one.

Resources Used So Far: https://www.youtube.com/watch?v=Mxk4cMBaH3g&list=PL6lxxT7IdTxGoHfouzEK-dFcwr_QClME_&index=37&t=0s [Most Recent Attempt - Trying to Simplify the problem]

how to make two split up screen ( canvas) inside the python tkinter window

http://www.openbookproject.net/courses/python4fun/tkphone1.html

matplotlib Navigation Bar error 'FigureCanvasTkAgg' object has no attribute 'manager' in tkinter

Displaying Matplotlib Navigation Toolbar in Tkinter via grid [Hit the Same Problem - One Master Frame Split Into Two Sub Frames] [This Link is probably has the answer in it and if that is the case, apologies for repeating a question already asked on stack overflow)

EDIT. To Expand Further for Clarity take the code below:

import tkinter as tk                # python 3
from tkinter import font  as tkfont # python 3
#import Tkinter as tk     # python 2
#import tkFont as tkfont  # python 2

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")

        # the container is where we'll stack a bunch of frames
        # on top of each other, then the one we want visible
        # will be raised above the others
        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):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame

            # put all of the pages in the same location;
            # the one on the top of the stacking order
            # will be the one that is visible.
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage")

    def show_frame(self, page_name):
        '''Show a frame for the given page name'''
        frame = self.frames[page_name]
        frame.tkraise()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is the start page", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(self, text="Go to Page One",
                            command=lambda: controller.show_frame("PageOne"))
        button2 = tk.Button(self, text="Go to Page Two",
                            command=lambda: controller.show_frame("PageTwo"))
        button1.pack()
        button2.pack()


class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 1", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()


class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 2", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()


if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

how would i get PageTwo to compose of two different frames so i can use two different geometric plotters (pack() and grid()).

james
  • 21
  • 2
  • It's not clear what the problem is. Inside each frame you can do whatever you want, including adding two or more frames. – Bryan Oakley Feb 22 '20 at 15:10
  • sorry, basically when i run the code above, when it gets to PageOne, instead of it displaying frame_name and frame_addr (proof of concept example), it opens a 2nd window for them. What i'm trying to do is incorporate that window onto my first tkinter window which is currently displaying PageOne. Does that help? – james Feb 22 '20 at 16:46

0 Answers0