Home > Back-end >  Use entry widget from another frame to insert into another frame (Treeview widget)
Use entry widget from another frame to insert into another frame (Treeview widget)

Time:01-13

I'm currently working on a stock portfolio tracker, and I'm super confused about the way variables are passed from one frame to another. I basically have two frames so far, one is a treeview and the second is Userinputframe, a bunch of buttons and entry widgets.

My code should use those entry widgets and insert them into my treeview. However now that those are into two different frames, I cannot manage to get() the values from Userinputframe to work with my treeview, and I get the error: 'Frame' Object has no attribute 'Entry_ticker'. What am I missing here?

import tkinter
from tkinter import ttk
import datetime as dt
import yfinance as yf
import os
import csv

root_window = tkinter.Tk()
root_window.title('Python portfolio')
root_window.geometry('700x700')

# Frame functions
def call_first_frame():
    second_frame.pack_forget()
    first_frame.pack()

def call_second_frame():
    first_frame.pack_forget()
    second_frame.pack()

def call_third_frame():
    second_frame.pack_forget()

def quit_program():
    root_window.destroy()

# We create an csv flat file to store our values in
def create_database():
    global header
    header = ['Ticker', 'Volume', 'Purchased at', 'Current value']
    with open('stocks.csv', 'w', encoding='UTF8', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(header)

# we create a function to get the current share price of a ticker
def Shareprice(ticker):
    ticker_yahoo = yf.Ticker(ticker)
    data = ticker_yahoo.history()
    return data.tail(1)['Close'].iloc[0]

# we use this to record a date of purchase
def stamp():
    x = dt.datetime.now()
    return x.strftime("%c")

# we fetch the price of the ticker entered
def CurrentPrice():
    a=Shareprice(first_frame.Entry_ticker.get())
    b=int(first_frame.Entry_volume.get())
    return round(a*b,2)

# add an investment to the portfolio
def add_to_portfolio():
    my_tree = ttk.Treeview(Treeviewframe, show='headings')

    # Defining columns
    my_tree['columns'] = ('Ticker', 'Volume', 'Purchased at', 'Current value')

    # Column_formatting
    my_tree.column('#0', anchor=tkinter.W, width=70, stretch=False)
    my_tree.column('Ticker', anchor=tkinter.CENTER, width=50)
    my_tree.column('Volume', anchor=tkinter.CENTER, width=50)
    my_tree.column('Purchased at', anchor=tkinter.CENTER, width=175)
    my_tree.column('Current value', anchor=tkinter.CENTER, width=150)

    # Heading_formatting
    my_tree.heading('#0', text='ID', anchor=tkinter.W)
    my_tree.heading('Ticker', text='Ticker', anchor=tkinter.W)
    my_tree.heading('Volume', text='Volume', anchor=tkinter.W)
    my_tree.heading('Purchased at', text='Purchased at', anchor=tkinter.W)
    my_tree.heading('Current value', text='Current value', anchor=tkinter.W)

    global count
    my_tree.insert(parent='', index='end', iid=count, text='',
                   values=(Userinputframe.Entry_ticker.get(), Userinputframe.Entry_volume.get(), stamp(), CurrentPrice()))
    count  = 1

    # immediately after adding an investment in our treeview, we add our new investment to a our database
    csvdata=[first_frame.Entry_ticker.get(), first_frame.Entry_volume.get(), stamp(), CurrentPrice()]
    with open('stocks.csv', 'w', encoding='UTF8',newline='') as f:
        writer = csv.writer(f)
        writer.writerow(header)

    with open('stocks.csv', 'a') as f:
        writer = csv.writer(f)
        writer.writerow(csvdata)

    # We clear the entry widgets automatically when the button is pressed
    first_frame.Entry_ticker.delete(0, tkinter.END)
    first_frame.Entry_volume.delete(0, tkinter.END)

# remove one stock
def rmv_from_portfolio():
    x = my_tree.selection()[0]
    my_tree.delete(x)

# clear the portfolio (remove all stocks)
def rmv_all():
    for record in my_tree.get_children():
        my_tree.delete(record)

def start_tab_widgets():

    # We initialize the Treeview to hold our portfolio
    my_tree = ttk.Treeview(Treeviewframe, show='headings')

    # Defining columns
    my_tree['columns'] = ('Ticker', 'Volume', 'Purchased at', 'Current value')

    # Column_formatting
    my_tree.column('#0', anchor=tkinter.W, width=70, stretch=False)
    my_tree.column('Ticker', anchor=tkinter.CENTER, width=50)
    my_tree.column('Volume', anchor=tkinter.CENTER, width=50)
    my_tree.column('Purchased at', anchor=tkinter.CENTER, width=175)
    my_tree.column('Current value', anchor=tkinter.CENTER, width=150)

    # Heading_formatting
    my_tree.heading('#0', text='ID', anchor=tkinter.W)
    my_tree.heading('Ticker', text='Ticker', anchor=tkinter.W)
    my_tree.heading('Volume', text='Volume', anchor=tkinter.W)
    my_tree.heading('Purchased at', text='Purchased at', anchor=tkinter.W)
    my_tree.heading('Current value', text='Current value', anchor=tkinter.W)

    # We find out if the user already has investments in his portfolio
    path_name = 'stocks.csv'

    if os.path.exists(path_name):
        # then we read the data from the csv file, and make sure it appears in our treeview
        old_data = []
    else:
        # then we create the database (CSV file)
        create_database()
        old_data = []

    # We insert data in our portfolio
    global count
    count = 0
    for record in old_data:
        my_tree.insert(parent='', index='end', iid=count, text='', values=(record[0], record[1], record[2], record[3]))
        count  = 1

    # widgets_user_input:Labels and entry
    Lbl_ticker = tkinter.Label(Userinputframe, text='Ticker')
    Lbl_ticker.grid(row=0, column=0, padx=10, pady=20, sticky='n')

    Lbl_volume = tkinter.Label(Userinputframe, text='Volume')
    Lbl_volume.grid(row=1, column=0, padx=10, pady=20, sticky='n')

    Entry_ticker = tkinter.Entry(Userinputframe, text='Add ticker here...')
    Entry_ticker.grid(row=0, column=1, pady=20, sticky='n', columnspan=2)

    Entry_volume = tkinter.Entry(Userinputframe, text='Add nb of shares here...')
    Entry_volume.grid(row=1, column=1, pady=20, sticky='n', columnspan=2)

    # Buttons to add and remove records
    Btn_add_record = tkinter.Button(Userinputframe, text='Add to portfolio', command=add_to_portfolio)
    Btn_add_record.grid(row=2, column=1, pady=20, sticky='ns')

    Btn_rmv_record = tkinter.Button(Userinputframe, text='Remove one selected stock', command=rmv_from_portfolio)
    Btn_rmv_record.grid(row=3,column=1, pady=20, sticky='n')

    Btn_rmv_all = tkinter.Button(Userinputframe, text='Clear my portfolio', command=rmv_all)
    Btn_rmv_all.grid(row=4, column=1, pady=20, sticky='n')

    # we pack our Tree view onto our frame
    my_tree.pack(in_=Treeviewframe, pady=20, padx=0, fill='y')

    # we place those two frames one after the other
    Treeviewframe.pack(in_=first_frame, pady=20)
    Treeviewframe['borderwidth'] = 0
    Userinputframe.pack(in_=first_frame, pady=20)
    Userinputframe['borderwidth'] = 0


# Buttons to leave the program
# We declare our frames
first_frame = tkinter.ttk.Frame(root_window)
first_frame.pack()

# we define two sub-frames to work into
Treeviewframe = tkinter.ttk.Frame(first_frame)

Userinputframe = tkinter.ttk.Frame(first_frame)

second_frame = tkinter.Frame(root_window)
second_frame.pack()

third_frame = tkinter.Frame(root_window)
third_frame.pack()

# We hide all frames in reverse order, but leave the first frame visible
start_tab_widgets()

'''
portfolio_widgets()

reco_widgets()
'''

# Hide all frames in reverse order, but we leave the first frame visible
third_frame.pack_forget()
second_frame.pack_forget()

# we add this program to the main loop
root_window.mainloop()

CodePudding user response:

Looking at the code, the Error: Frame' Object has no attribute 'Entry_ticker' is expected.

Entry_ticker is a local variable and not part of the Userinputframe.

There are a few options you have here:

Option 1: Define your entry as global variable

entry_ticker = None
....
...
...
def start_tab_widgets():
    ...
    ...
    global entry_ticker
    entry_ticker = tkinter.Entry(Userinputframe, text='Add ticker here...')
    entry_ticker.grid(row=0, column=1, pady=20, sticky='n', columnspan=2)

def add_to_portfolio():
    ...
    ...
    #access entry_ticker 
    global entry_ticker
    value = entry_ticker.get()

Option 2: Create a new class for your frame and define a class level variable

class UserInputFrame:

    def __init__(self, parent):
        self.frame = tkinter.ttk.Frame(parent)
        self.entry_ticker = tkinter.Entry(self.frame, text='Add ticker here...')
        self.entry_ticker.grid(row=0, column=1, pady=20, sticky='n', columnspan=2)


def start_tab_widgets():
    ....
    ....
    ....
    # Create class instance
    user_input_frame = UserInputFrame(root_window)

    # Access the entry_ticker value
    user_input_frame.entry_ticker.get()

Option 3: Use tkinter.StringVar()

Now, The most suitable option to access the value is to use tkinter Var() in your case StringVar() can be use. So, when you create your entry

# create the stringvar
entry_ticker_var = tkinter.StringVar()
# Use the var while createing entry (textvariable=entry_ticker_var)
entry_ticker = tkinter.Entry(self.frame, text='Add ticker here...', textvariable=entry_ticker_var)
....
....

Now, while using it just call entry_ticker.get() or if you want to update the text in your entry you can entry_ticker.set("New Value")

You can also bind trace event to this variable entry_ticker.trace_add('write', on_change). Here on_change function will be called whenever value of your that variable is changed (i.e whenever user writes input to the text box)

Option 4: Option 2 and 3 together (Recommended)

class UserInputFrame:

    def __init__(self, parent):
        # create the variable
        self.entry_ticker_var = tkinter.StringVar()
        # create frame
        frame = tkinter.ttk.Frame(parent)
        # create text box
        entry_ticker = tkinter.Entry(self.frame, text='Add ticker here...', textvariable=self.entry_ticker_var)
        entry_ticker.grid(row=0, column=1, pady=20, sticky='n', columnspan=2)


def start_tab_widgets():
    ....
    ....
    ....
    # Create class instance
    user_input_frame = UserInputFrame(root_window)
    # Access the entry_ticker value
    user_input_frame.entry_ticker_var.get()

References:

Python Tkinter

python Tkinter-module


On the side note, if you want to access child widget of a frame you may need to use winfo_children()

for child in Userinputframe.winfo_children():
    widget_type = child.winfo_class()
    if widget_type == 'Entry':
          # do stuff
  •  Tags:  
  • Related