Python 3.x - using text string as variable name - python

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)

Related

How to appropriately use vartext in tkinter?

I am trying to clean up my code. I currently have a long list of labels that are 'printed' if a certain condition is met. For example:
if 0000:
output_0 = Label(frame_20, text="Condition 0 costs $15",
bg='white', padx=10, pady=10).grid(column=0,row=0)
if 0001:
output_1 = Label(frame_20, text="Condition 1 costs $20",
bg='white', padx=10, pady=10).grid(column=0,row=0)
Ideally what I want is to separate the cost variable and the text entirely from the Label so that my code looks something like this:
if 0000:
cost_0 = 15
outputText = ("Condition 0 costs %d", cost_0)
if 0001:
cost_1 = 20
outputText = ("Condition 1 costs %d", cost_1)
output_x = Label(frame_20, vartext=outputText,
bg='white', padx=10, pady=10).grid(comumn=0,row=0)
I assume you are asking for some feature that you don't know yet - and also guessing you have way more conditions than your example.
For string formmating, use f-string. That is easiest, yet robust way to do so. For more details, check python online docs.
In a nutshell, it's used like this:
a = 10
f" <any_text> {a} <even_more_text> "
About dynamic widget adding, forget about naming every label widget you created. Just iterate thru condition pairs and generate widgets, then store those in list.
import tkinter
from typing import Iterable, Tuple, List
# List your conditions - expressions, bool, lambda, whatever in pair with associated value
conditions = (10 == 6, 15), (True, 20), (5, 25)
class App(tkinter.Frame):
def __init__(self, root_):
super().__init__(root_)
self.root = root_
self.pack()
# anything to store the reference - list, class, anything
self.labels: List[tkinter.Label] = []
def generate_labels(self, conditions_: Iterable[Tuple[bool, int]]):
for idx, (condition, price_tag) in enumerate(conditions_):
if condition:
self.labels.append(tkinter.Label(self, text=f"Condition {idx} costs ${price_tag}"))
for label in self.labels:
label.pack()
root = tkinter.Tk()
app_reference = App(root)
app_reference.generate_labels(conditions)
root.mainloop()
And there's no such parameter vartext in tkinter.Label. I think you wanted to type textvariable.
If what you wanted is to have a state and checking if it matches the condition, then this will do:
import tkinter
class App(tkinter.Frame):
def __init__(self, root_):
super().__init__(root_)
self.root = root_
self.pack()
self.solution_dict = {"0000": 15, "0001": 20, "0002": 25}
self.label: tkinter.Label = None
def get_solution(self, condition: str):
if condition in self.solution_dict.keys():
self.label = tkinter.Label(self, text=f"Condition {condition} costs ${self.solution_dict[condition]}")
self.label.pack()
root = tkinter.Tk()
app_reference = App(root)
app_reference.get_solution("0001")
root.mainloop()
If there's more than one condition matching, try if dictionary key is inside the condition string.
import tkinter
from typing import List
class App(tkinter.Frame):
def __init__(self, root_):
super().__init__(root_)
self.root = root_
self.pack()
self.solution_dict = {"0000": 15, "0001": 20, "0002": 25}
self.labels: List[tkinter.Label] = []
def get_solution(self, condition: str):
for key in self.solution_dict.keys():
if key in condition:
widget = tkinter.Label(self, text=f"Condition {key} costs ${self.solution_dict[key]}")
widget.pack()
self.labels.append(widget)
root = tkinter.Tk()
app_reference = App(root)
app_reference.get_solution("0001 & 0002")
root.mainloop()

How do I carry over a variable from one class to another?

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']

Get variable from another file - python

I am creating a Tkinter program that allows the user to enter text into a nice looking box rather than the python shell.
As I would like to use this in multiple programs I made into a function that can be used in other files.
I can get it to run in another file, but not import the variable here is my code.
File 1:
import tkinter as tk
def input_text(label_text, button_text):
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text=button_text, command=self.on_button)
self.label = tk.Label(self, text=label_text)
self.label.pack(side = 'top', pady = 5)
self.button.pack(side = 'bottom', pady = 5)
self.entry.pack()
def on_button(self):
answer = self.entry.get()
self.destroy()
w = SampleApp()
w.resizable(width=True, height=True)
w.geometry('{}x{}'.format(180, 90))
w.mainloop()
File 2:
import text_input as ti
from text_input import answer
ti.input_text('Enter some text', 'OK')
I get the error ImportError: cannot import name 'answer'
answer is a local variable withinbutton. If you want toimport` it, you need to make it a package attribute:
import tkinter as tk
global answer
def input_text(label_text, button_text):
class SampleApp(tk.Tk):
...
def on_button(self):
global answer
answer = self.entry.get()
However, this is a very strange way to access the data. Clean module design would likely have the object (SampleApp) at hand, and extract the answer with a method call for that app. More simply, why not just return that value from on_button?
def on_button(self):
answer = self.entry.get()
self.destroy()
return answer
... so your usage would be
response = my_app.on_button()

Python: retrieve objects's value from a class object

Here is my problem, I'm creating a Tkinter GUI
and in a class object I call for a text tipped in a textfield and make an array of it
But I can't find how to retrieve and use this object later on in the script.
Here my script
import pandas as pd
from pandas import *
import numpy as np
import Tkinter
from Tkinter import *
from pylab import *
from ttk import *
import ScrolledText
df = pd.DataFrame(np.random.randint(100, 500, size = (10, 20)), index = list('abcdefghij'))
mylist = df.index.values.tolist()
rootWin = Tkinter.Tk()
class GraphGUI():
def __init__(self, rootWin):
self.textfield = ScrolledText.ScrolledText(rootWin, width=30, height=10)
#Add some text:
self.textfield.insert(INSERT, "Replace")
self.textfield.grid(column=0, row=4)
self.button = Button(rootWin, text="Process!", command=self.clicked)
self.button.grid(column=1, row=4)
def clicked(self):
eText = self.textfield.get('1.0', END)
converted = eText.encode('ascii','ignore')
myarray = converted.splitlines()
print(myarray)
def clicked2():
print(clicked.myarray)
start = Tkinter.Button(rootWin, text="start!", command=clicked2).grid(column=7, row=8)
app = GraphGUI( rootWin )
rootWin.mainloop()
The problem is when I click on the process Button no problem myarray get printed.
But when I use the start button I can't retrieve it.
Is there a way to retrieve myarray out of the Class object?
Also Is it possible to print myarray with the Start button without having to use the Process button first?
##### EDIT
Here the final working script thanks to the help
class GraphGUI():
def __init__(self, rootWin):
self.textfield = ScrolledText.ScrolledText(rootWin, width=30, height=10)
#Add some text:
self.textfield.insert(INSERT, "Replace by your Accession list")
self.textfield.grid(column=6, row=4)
self.button = Button(rootWin, text="Process!", command=self.clicked)
self.button.grid(column=7, row=4)
#property
def myarray(self):
e_Text = self.textfield.get('1.0', END)
converted = e_Text.encode('ascii','ignore')
return converted.splitlines()
def clicked(self):
print(self.myarray)
print(variablea.get()) #not defined here#
print(variableb.get())
def clicked2():
print(app.myarray)
start = Tkinter.Button(rootWin, text="start!", command=clicked2).grid(column=7, row=10)
Looks like you're trying to access a local variable of an object method. That's not possible. If you need it, try defining it as its own callback instead.
class GraphGUI():
def __init__(self, rootWin):
self.textfield = ScrolledText.ScrolledText(rootWin,
width=30,
height=10)
# Add some text:
self.textfield.insert(INSERT, "Replace")
self.textfield.grid(column=0, row=4)
self.button = Button(rootWin, text="Process!", command=self.clicked)
self.button.grid(column=1, row=4)
def clicked(self):
eText = self.textfield.get('1.0', END)
converted = eText.encode('ascii', 'ignore')
myarray = converted.splitlines()
return myarray
rootWin = Tkinter.Tk()
app = GraphGUI(rootWin)
start = Tkinter.Button(rootWin, text="start!", command=app.clicked)
start.grid(column=7, row=8) # your code assigned `start = None` btw
rootWin.mainloop()
Alternatively, myarray could just be defined as a property of the class.
class GraphGUI():
...
#property
def myarray(self):
e_txt = self.textfield.get('1.0', END)
converted = e_txt.encode('ascii', 'ignore')
return converted.splitlines()
def clicked(self):
print(self.myarray)
# do whatever else it has to do
rootWin = Tkinter.Tk()
start = Tkinter.Button(rootWin, text="Start!",
command=lambda: app.myarray)
...
You are trying to access a local variable in the method of an object, which you simply cannot do.
What you can do is that that other method return the object, or you can convert that local variable into an instance variable.
The simplest solution is to have the function return the data that you want:
class GraphGUI():
...
def clicked(self):
...
return myarray
def clicked2():
myarray = app.clicked()
print("myarray:", myarray)

Tkinter Radiobutton Text

I have the following code:
class Test:
def __init__(self, master): # master is a Tk or Toplevel instance
self.master, self.typeFrame = master, tk.Frame(master)
self.typeVar = tk.StringVar(self.typeFrame)
for column, wordType in enumerate(['Noun', 'Verb', 'Adjective', 'Adverb'], 1):
typeRadioButton = tk.Radiobutton(self.typeFrame, text = wordType, textvariable = self.typeVar, value = wordType, command = self.createLambda(wordType))
typeRadioButton.grid(row = 1, column = column)
self.typeFrame.grid()
def createLambda(self, obj):
return lambda: self.changeInfo(obj)
def changeInfo(self, obj):
pass # this will do something later
However, when I run the code like this, the Radiobuttons have no text associated with them.
root = tk.Tk()
test_instance = Test(root)
test_instance.master.mainloop()
How can I fix this? Thanks in advance!
Change textvariable=... to variable=....
BTW, your example does not contain self.typeFrame.pack() or self.typeFrame.grid(..).

Categories

Resources