I'm trying to program a application that carries over user inputs from one page to the other where the pages are separated by classes. A problem I'm having is that the array output on page 2 isn't updating. Basically the output is just [] which is just the starting initialized variable. I've tried some solutions I've found on stack overflow such as calling it a global variable again at the init bit of the PageTwo class as well as trying to use PageOne.overall_data and using self.overall_data but the problem persists although there weren't any errors. This is a part of the code before I tried anything. I tried to cut out the irrelevant bits but I'm not sure if I cut out too much, feedback is much appreciated. Thanks for reading!
import tkinter as tk
import tkinter_nav as tknav
from tkinter import ttk
import numpy as np
class App(tknav.Wrapper):
def __init__(self):
tknav.Wrapper.__init__(
self,
pages=[PageOne, PageTwo],
start_state={'previous_page': None}
)
self.geometry('450x450')
self.show_page('page_one')
class PageOne(tknav.Page):
def __init__(self, parent):
tknav.Page.__init__(self, parent, 'page_one')
player_details = []
global overall_data
overall_data = []
my_notebook = ttk.Notebook(self)
my_notebook.pack(pady = 15)
my_notebook.pack(fill="both", expand=True)
my_frame1 = tk.Frame(my_notebook, width = "500", height = "500")
def submit(): #called every time inputs are made to be appended to player_details then overall_data
player_details = []
global overall_data
player_details.append(name.get())
player_details.append(health.get())
player_details.append(ac.get())
player_details.append(initiative.get())
overall_data.append(player_details)
overall_data = sorted(overall_data, key = lambda x:x[3])
print(str(overall_data))
class PageTwo(tknav.Page): #second page of the application
def __init__(self, parent):
tknav.Page.__init__(self, parent, 'page_two')
tk.Label(
self,
text='Page Two'
).pack()
tk.Button(
self,
text='Navigate to Page One',
command=lambda: self.__navigate(),
).pack()
line21 = tk.Label(self, text = str(overall_data), font = ('Times New Roman', 12))
line21.place(x = 30, y = 30, width = 100, height = 25)
def __navigate(self):
print('navigating to page one')
self.navigate('page_one')
if __name__ == '__main__':
App().mainloop()
You can put your data at the class level in PageOne like this:
class PageOne(tknav.Page):
overall_data = []
and use it everywhere like this:
PageOne.overall_data.append(player_details)
Since you have used tkinter_nav, you can use its provided app_state to share data between pages:
class App(tknav.Wrapper):
def __init__(self):
tknav.Wrapper.__init__(
self,
pages=[PageOne, PageTwo],
start_state={'previous_page': None, 'overall_data': []} # added 'overall_data'
)
self.geometry('450x450')
self.show_page('page_one')
Then you can access this shared data in each page by:
self.app_state['overall_data']
Related
I'm trying to avoid to multiply functions in code by using
def Return_Label(self,number)
with a parameter.
Any Idea how to use string in order to define variable name usable to .set value to StringVar()?
Example code below:
import tkinter as tk
from tkinter import *
class WINDOW():
def __init__(self):
self.Settings_Window()
def Settings_Window(self):
self.settings_window = tk.Tk()
self.settings_window.minsize(200,200)
self.entry = Entry(self.settings_window)
self.entry.pack()
self.entry2 = Entry(self.settings_window)
self.entry2.pack()
self.label1input = StringVar()
self.label = Label(self.settings_window,textvariable=self.label1input, bg='yellow')
self.label.pack(expand='yes',fill='x')
self.label2input = StringVar()
self.label2 = Label(self.settings_window, textvariable=self.label2input, bg='yellow')
self.label2.pack(expand='yes', fill='x')
self.button = Button(self.settings_window,text='SETUP1',command=self.Next)
self.button.pack()
self.button2 = Button(self.settings_window,text='SETUP2',command=self.Next2)
self.button2.pack()
self.settings_window.mainloop()
def Next(self):
self.number=1
self.Return_Label(self.number)
def Next2(self):
self.number=2
self.Return_Label(self.number)
def Return_Label(self,number):
self.entry_field_value = self.entry.get()
print(self.entry_field_value)
#self.label1input.set(self.entry_field_value)
setattr(self,'label'+str(number)+'input.set',self.entry_field_value)
window=WINDOW()
I prefer a list approach to managing multiple entry fields and updating values.
By using list you can use the index value to manage the labels as well :D.
See the below example of how you can use list to deal with all the values and updates.
import tkinter as tk
from tkinter import *
class Window(tk.Tk):
def __init__(self):
super().__init__()
self.minsize(200, 200)
self.entry_list = []
self.label_list = []
entry_count = 2
for i in range(entry_count):
self.entry_list.append(Entry(self))
self.entry_list[i].pack()
for i in range(entry_count):
self.label_list.append(Label(self,bg='yellow'))
self.label_list[i].pack(expand='yes', fill='x')
Button(self, text='SETUP', command=self.Return_Label).pack()
def Return_Label(self):
for ndex, lbl in enumerate(self.label_list):
lbl.config(text=self.entry_list[ndex].get())
if __name__ == '__main__':
Window().mainloop()
Create lists of objects rather than individual attributes for each object. For example,
import tkinter as tk
from tkinter import *
class Window:
def __init__(self):
self.settings_window()
def Settings_Window(self):
self.settings_window = tk.Tk()
self.settings_window.minsize(200,200)
self.entries = [
Entry(self.settings_window),
Entry(self.settings_window)
]
for e in self.entries:
e.pack()
self.labelinputs = [
StringVar(),
StringVar()
]
self.labels = [
Label(self.settings_window, textvariable=label, bg='yellow')
for label in self.labelinputs
]
for l in self.labels:
l.pack(expand='yes', fill='x')
self.buttons = [
Button(self.settings_window,text='SETUP1',command=lambda: self.return_label(0))
Button(self.settings_window,text='SETUP2',command=lambda: self.return_label(1))
]
for b in self.buttons:
b.pack()
self.settings_window.mainloop()
def return_label(self,number):
entry_field_value = self.entry.get()
self.labelsinput[number].set(entry_field_value)
window=WINDOW()
Dynamicly computing variable names should be avoided at all costs. They are difficult to do correctly, and it makes your code hard to understand, hard to maintain, and hard to debug.
Instead, store the widgets in a dictionary or list. For example:
def __init___(self):
...
self.vars = {}
...
self.vars[1] = StringVar()
self.vars[2] = StringVar()
...
def Return_Label(self,number):
self.entry_field_value = self.entry.get()
var = self.vars[number]
var.set(self.entry_field_value)
Though, you really don't need to use StringVar at all -- they usually just add extra overhead without providing any extra value. You can save the labels instead of the variables, and call configure on the labels
self.labels[1] = Label(...)
...
self.labels[number].configure(text=self.entry_field_value)
Hello I think I have a python tkinter design issue. I have looked on this to structure my code better. I do not want to change the configuration of all my widgets individually and would like to use parent_widget.winfo_children() command that I found from this question.
I am wondering is there a better way to not individually configure the widgets and yet update their font and Style.
Here is my code and the current behavior:
class TabOne(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.tab1_note = ttk.Notebook(self,width=parent.winfo_screenwidth(), height=parent.winfo_screenheight())
tab1_open_observations = ttk.Frame(self.tab1_note)
tab1_closed_observations = ttk.Frame(self.tab1_note)
self.tab1_note.add(tab1_open_observations, text= "Open Projects")
self.tab1_note.add(tab1_closed_observations, text= "Closed/Deferred Projects")
self.tab1_note.pack()
self.tab_two_load(tab1_open_observations)
self.tab_three_load(tab1_closed_observations)
widget_list = []
widget_list.extend(tab1_open_observations.winfo_children())
widget_list.extend(tab1_closed_observations.winfo_children())
for wid in widget_list:
try:
wid.configure(font = 'helvetica 12')
except:
pass
def tab_one_load(self,tab1_refresh_db):
def tab_two_load(self,tab1_open_observations)
class TabTwo(Frame):
class TabThree(Frame):
class MainWindow(Frame):
def __init__(self, window, **kwargs):
Frame.__init__(self, window, **kwargs)
self.load_ui()
def load_ui(self):
self.note = ttk.Notebook(self,width=self.window.winfo_screenwidth()-(2*self.pad), height=self.window.winfo_screenheight()-(2*self.pad))
self.tab1 = TabOne(self.note)
self.tab2 = TabTwo(self.note)
self.tab3 = TabThree(self.note)
self.note.pack()
def main():
window = Tk()
window.title('Productivity Tool')
app = MainWindow(window)
app.pack(side="top", fill="both", expand=True)
window.mainloop()
if __name__ == '__main__':
main()
Current behavior for dependent drop down lists (code below):
The second list does not overlap if I add project_module_dropdown.configure(font='helvetica 12') below every dependent OptionsMenu, Here is more code for the function tab_two_load()
def tab_two_load(self,tab1_open_observations):
def update_modules(a,b,c):
proj_mo_names = [module[0] for module in project_modules]
proj_mod_select.set(proj_mo_names[0])
project_module_dropdown = OptionMenu(tab1_open_observations,proj_mod_select,*proj_mo_names)
project_module_dropdown.configure(font='helvetica 12')
project_module_dropdown.grid(row=2, column=1,padx=10, pady=10)
proj_select = StringVar(tab1_open_observations,value='Default Text')
proj_select.trace('w',update_modules)
proj_mod_select = StringVar(tab1_open_observations,value='Default Text')
proj_mod_select.trace('w',update_teams)
proj_mod_select.trace('w',update_artifacts)
proj_names = [project[1] for project in projects]
proj_select.set(proj_names[0])
project_dropdown = OptionMenu(tab1_open_observations,proj_select,*proj_names)
project_dropdown.grid(row=1,column=1,padx=10,pady=10)
I think the problem is with how I have structured my code but i believe i have compartmentalized the code well but I am open to suggestions. this is not exactly a code review problem. My problem is overlapping drowdowns, but I feel that I have some duplication in my code that I would like to avoid. Any help is great. Thanks.
I would like this as my desired behavior without adding project_module_dropdown.configure(font='helvetica 12'):
#___________________________________________________________
def open_files(self):
file_name_list = []
FM_file_name_list = []
RG_file_name_list = []
path = easygui.fileopenbox(multiple=True)
#print(path)
for i in range(len(path)):
file_name_list.append(path[i])
filename, file_extension = os.path.splitext(path[i])
#print (file_extension)
if file_extension == '.FDV':
FM_file_name_list.append(os.path.basename(path[i]))
if file_extension == '.R':
RG_file_name_list.append(os.path.basename(path[i]))
class GraphPage(tk.Frame):
def __init__(self, parent, controller):
self.controller=controller
tk.Frame.__init__(self, parent)
self.FM_List_Box =tk.Listbox(self)
self.FM_List_Box.config(highlightbackground='steelblue', highlightthickness = 1)
self.FM_List_Box.pack(side=tk.LEFT, fill=tk.Y, padx = 5)
I'm trying to get the values within the list 'FM_file_name_list' into the list box 'self.FM_List_Box'. The method 'open_files' is contained within the menu bar of the application in a different class, so the list box has been initially created before it is run.
It would be greatly appreciated if some could help me understand how you can update the values of a list box from a method in a different class.
This is relatively simple.
You simply need to call the class's variable from the other class.
This can be done something like the below:
from tkinter import *
class OtherClass:
def __init__(self):
OtherClass.list = ["1", "2", "3"]
class App:
def __init__(self, root):
self.root = root
self.listbox = Listbox(self.root)
self.listbox.pack()
for i in OtherClass.list:
self.listbox.insert(END, i)
OtherClass()
root = Tk()
App(root)
root.mainloop()
So here we have a class OtherClass which contains a list OtherClass.list. We can then access this list from our App class by calling OtherClass.list.
If you run the program you will see that we are successfully able to pull the list from one class to the other.
from tkinter import *
class MainBattle(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
global canvas
self.parent.title('Python')
self.pack(fill = BOTH, expand = 1)
canvas = Canvas(self)
self.Label_My = Label(self, text = 'MyObject')
self.Label_My.place(x = 470, y = 35)
canvas.pack(fill = BOTH, expand = 1)
canvas.update()
def aa(self):
self.Label_My['text'] = 'HisObject'
def Change():
Label_My['text'] = 'HisObject'
root = Tk()
ex = MainBattle(root)
root.geometry('700x500')
it should use global method?
I would defind labels inside the class and change it's text outside class if possible.
You don't need global variables. You have a reference to the instance, which allows you to access all instance variables:
ex.Label_My["text"] = "HisObject"
If your question is "can I use global to set variable values from outside the class" then yes.
Whenever you want to change the value of a global variable you need to write global.
def changetext():
global label_text
label_text = "new text"
I've been building an app to track stock prices. The user should see a window with an entry widget and a button that creates a new frame with a label and a button. The label is the stock price and symbol, the button is a delete button, and should hide that frame if clicked.
I've re-written this program 4 times now, and it's been a great learning experience, but what I've learned is that I can't have the "mini-frames" being called from methods part of the main GUI class - this funks up the delete buttons, and updates the value behind frame.pack_forget() so it only deletes the last item ever.
I've moved my mini-frame widgets down into the class for the actual stock values. I've packed them (what I assume to be correct) but they don't show up. They also don't error out, which isn't very helpful. Here's my code, although I've omitted a lot of the functional parts to show what is happening with my frames. Keep in mind I need to keep it so that I can call my updater (self.update_stock_value) with a .after method against myapp.myContainer.
Is there a better way to do this?? Thanks in advance, my head hurts.
import re
import time
import urllib
from Tkinter import *
import threading
from thread import *
runningThreads = 0
# each object will be added to the gui parent frame
class MyApp(object):
def __init__(self, parent):
self.myParent = parent
self.myContainer = Canvas(parent)
self.myContainer.pack()
self.create_widgets()
# METHOD initiates basic GUI widgets
def create_widgets(self):
root.title("Stocker")
self.widgetFrame = Frame(self.myContainer)
self.widgetFrame.pack()
self.input = Entry(self.widgetFrame)
self.input.focus_set()
self.input.pack()
self.submitButton = Button(self.widgetFrame, command = self.onButtonClick)
self.submitButton.configure(text = "Add new stock")
self.submitButton.pack(fill = "x")
# METHOD called by each stock object
# returns the "symbol" in the entry widget
# clears the entry widget
def get_input_value(self):
var = self.input.get()
self.input.delete(0, END)
return var
# METHOD called when button is clicked
# starts new thread with instance of "Stock" class
def onButtonClick(self):
global runningThreads # shhhhhh im sorry just let it happen
runningThreads += 1 # count the threads open
threading.Thread(target = self.init_stock,).start() # force a tuple
if runningThreads == 1:
print runningThreads, "thread alive"
else:
print runningThreads, "threads alive"
def init_stock(self):
new = Stock()
class Stock(object):
def __init__(self):
# variable for the stock symbol
symb = self.stock_symbol()
# lets make a GUI
self.frame = Frame(myapp.myContainer)
self.frame.pack
# give the frame a label to update
self.testLabel = Label(self.frame)
self.testLabel.configure(text = self.update_stock_label(symb))
self.testLabel.pack(side = LEFT)
# create delete button to kill entire thread
self.killButton = Button(self.frame, command = self.kill_thread)
self.killButton.configure(text = "Delete")
self.killButton.pack(side = RIGHT)
# create stock label
# call updater
def kill_thread(self):
global runningThreads
runningThreads -= 1
self.stockFrame.pack_forget() # hide the frame
self.thread.exit() # kill the thread
def update_stock_label(self, symb):
self.testLabel.configure(text = str(symb) + str(get_quote(symb)))
myapp.myContainer.after(10000, self.update_stock_label(symb))
def stock_symbol(self):
symb = myapp.get_input_value()
print symb
# The most important part!
def get_quote(symbol):
try:
# go to google
base_url = "http://finance.google.com/finance?q="
# read the source code
content = urllib.urlopen(base_url + str(symbol)).read()
# set regex target
target = re.search('id="ref_\d*_l".*?>(.*?)<', content)
# if found, return.
if target:
print "found target"
quote = target.group(1)
print quote
else:
quote = "Not Found: "
return quote
# handling if no network connection
except IOError:
print "no network detected"
root = Tk()
root.geometry("280x200")
myapp = MyApp(root)
root.mainloop()
Your code won't run because of numerous errors, but this line is definitely not doing what you think it is doing:
self.frame.pack
For you to call the pack function you must include (), eg:
self.frame.pack()
You ask if your code is the best way to do this. I think you're on the right track, but I would change a few things. Here's how I would structure the code. This just creates the "miniframes", it doesn't do anything else:
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.submit = tk.Button(self, text="Submit", command=self.on_submit)
self.entry.pack(side="top", fill="x")
self.submit.pack(side="top")
def on_submit(self):
symbol = self.entry.get()
stock = Stock(self, symbol)
stock.pack(side="top", fill="x")
class Stock(tk.Frame):
def __init__(self, parent, symbol):
tk.Frame.__init__(self, parent)
self.symbol = tk.Label(self, text=symbol + ":")
self.value = tk.Label(self, text="123.45")
self.symbol.pack(side="left", fill="both")
self.value.pack(side="left", fill="both")
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()