Creating a dynamic variable and use it further in python (tkinter) [duplicate] - 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

Creat and access dictionary within class

I have created a class within tkinter that builds a frame and buttons (example below).
I am also trying to create a series of radiobuttons each with a command, and rather than having to type three times the same, I am trying to use a dictionary that stores the function name and the function (again see below):
import tkinter as tk
from tkinter import ttk
from tkinter import *
class EnoxDoseCalculator:
def __init__(self, root):
root.title('Enoxaparin Dose Calculator')
indication = {'VTE': self.vte, 'Atrial Fibrilation': self.af, 'Mechanical Heart valve': self.mech}
mainframe = ttk.Frame(root)
mainframe.grid(column = 0, row = 0, sticky='nsew')
test_button = ttk.Button(mainframe, text= 'Push Mew!!', command = self.format)
test_button.grid(row = 0, column = 0)
def vte(self):
pass
def af(self):
pass
def mech(self):
pass
def format(self):
self.var3 = tk.StringVar()
for key, value in self.indication.items():
ttk.Radiobutton(mainframe, text = self.indication.key, variable = self.var3, value = self.indication.key, command = self.indication.value).grid(row = n+1, column =0, sticky = 'nswe')
root = Tk()
EnoxDoseCalculator(root)
print(dir(EnoxDoseCalculator))
root.mainloop()
However, I keep getting this message:
I get this is to do with the dictionary functions being listed before they are created, so my question is, where do I place said dictionary, because I am sure this is possible. or is it that I am using the wrong names for the function?
Try this:
class EnoxDoseCalculator:
def __init__(self, root):
self.root = root # add this (mostly for consistency/best practice)
self.root.title('Enoxaparin Dose Calculator')
self.indication = {'VTE': self.vte, 'Atrial Fibrilation': self.af, 'Mechanical Heart valve': self.mech}
self.mainframe = ttk.Frame(self.root) # update this to include 'self'
self.mainframe.grid(column=0, row=0, sticky='nsew')
# update this to include 'self'
self.test_button = ttk.Button(self.mainframe, text='Push Mew!!', command=self.format)
self.test_button.grid(row=0, column=0)
Using self to namespace your class variables to EnoxDoseCalculator

How to reference "not yet created" class instance in Python

New to python and generally object-oriented programming. I have written a script to do some stuff and here is a very simplified version of it with its main structure.
from tkinter import *
import matplotlib.pyplot as plt
import numpy as np
class MainWindow:
def __init__(self):
self.mw = Tk()
button = Button(self.mw, text='Open', command=self.openfile)
button.pack()
self.anotherwd = []
self.mw.mainloop()
def openfile(self):
x = float(input('Number:'))
self.anotherwd.append(AnotherWindow(x))
class AnotherWindow:
def __init__(self,number):
self.aw = Toplevel()
self.var = IntVar()
self.numb = number
label = Label(self.aw, text="Multiply by:")
entry = Entry(self.aw, textvariable=self.var)
button = Button(self.aw, text='Plot', command=self.plot)
label.pack()
entry.pack()
button.pack()
def plot(self):
numb_change = self.numb * self.var.get()
plotwd.plotvar = numb_change
plotwd.plot_line()
YetAnotherWindow()
class PlotWindow:
def __init__(self):
self.plotvar = 0
self.fig, self.ax = plt.subplots()
def plot_line(self):
example = np.arange(0., 5., 0.2)
self.ax.plot(example, self.plotvar*example)
self.fig.show()
class YetAnotherWindow:
def __init__(self):
self.yaw = Toplevel()
self.var_yaw = IntVar()
label = Label(self.yaw, text="More numbers:")
entry = Entry(self.yaw, textvariable=self.var_yaw)
button = Button(self.yaw, text='Plot more', command=self.plot_another_line)
label.pack()
entry.pack()
button.pack()
def plot_another_line(self):
example = np.arange(0., 5., 0.2)
plotwd.ax.plot(example, self.var_yaw.get() * plotwd.plotvar * example)
plotwd.fig.show()
plotwd = PlotWindow()
gui = MainWindow()
In the 'MainWindow' it let's you import some data (here as a number as console-input) then in a 'AnotherWindow' you modify the data (here multiply it by a entered number) and it plots this modified data in a 'PlotWindow'. Afterwards you can do some stuff with it in 'YetAnotherWindow' (again some useless multiplication) and plot the result in the same 'PlotWindow'.
The problem is, that there is only one 'PlotWindow'-Object while I want to create an instance every time I press the button in 'AnotherWindow' with separate modified data and another 'YetAnotherWindow'-Instance.
Now I could do it like in 'MainWindow' where I append new instances to a list but then I don't know how to reference such an instance in a separate class like 'YetAnotherWindow', as the one attribute in that instance of 'PlotWindow' would not be created yet while initialising. I wan which is why I declared it "fixed" as 'plotwd' in the first place as a "workaround".
I think "inheritance" might be a relevant keyword but I can't get it to work that way. I might doing some things too complicated anyway, so I better ask before I do some massive rewriting in the actual code.
Thanks.
Edit 1: Also a (related?) problem is that if I close the plot window 'AnotherWindow' closes too and I REALLY don't want that, which is the primary reason I want to change things up.
Edit 2: Not working example how I imagine it would work.
from tkinter import *
import matplotlib.pyplot as plt
import numpy as np
class MainWindow:
def __init__(self):
self.mw = Tk()
button = Button(self.mw, text='Open', command=self.openfile)
button.pack()
self.anotherwd = []
self.mw.mainloop()
def openfile(self):
x = float(input('Number:'))
self.anotherwd.append(AnotherWindow(x))
class AnotherWindow:
def __init__(self, number):
self.aw = Toplevel()
self.var = IntVar()
self.numb = number
self.plotwindows = []
self.yawindows = []
label = Label(self.aw, text="Multiply by:")
entry = Entry(self.aw, textvariable=self.var)
button = Button(self.aw, text='Plot', command=self.plot)
label.pack()
entry.pack()
button.pack()
def plot(self):
numb_change = self.numb * self.var.get()
self.plotwindows.append(PlotWindow)
self.plotwindows[-1].plotvar = numb_change
self.plotwindows[-1].plot_line
self.yawindows.append(YetAnotherWindow)
class PlotWindow:
def __init__(self):
self.plotvar = 0
self.fig, self.ax = plt.subplots()
def plot_line(self):
example = np.arange(0., 5., 0.2)
self.ax.plot(example, self.plotvar*example)
self.fig.show()
class YetAnotherWindow():
def __init__(self):
self.yaw = Toplevel()
self.var_yaw = IntVar()
label = Label(self.yaw, text="More numbers:")
entry = Entry(self.yaw, textvariable=self.var_yaw)
button = Button(self.yaw, text='Plot more', command=self.plot_another_line)
label.pack()
entry.pack()
button.pack()
def plot_another_line(self):
example = np.arange(0., 5., 0.2)
gui.anotherwd[-1].plotwindows[-1].ax.plot(example, self.var_yaw.get() * gui.anotherwd[-1].plotwindows[-1].plotvar * example)
gui.anotherwd[-1].plotwindows[-1].fig.show()
gui = MainWindow()
Also even if that would work, I would acess in "YetAnotherGui" the "last PlotWindow" and not the one it "belongs to".

getting information from Entry

my name is Rod. I recently started programming with OOP and it's not yet quite clear to me. I want to make my Button get information from my four entries but i don't know how to say to the program to get it from the four of them at the same time. I know i have to use the get() method but i don't understand how to insert it in the class so it will recognize my four Entries. Thanks!
from tkinter import *
from tkinter import ttk
class Application(Frame):
def __init__(self):
Frame.__init__(self)
self.grid()
def createButton(self,b_text,b_command,r,c):
self.newButton = Button(self, text=b_text,command=b_command)
self.newButton.grid(padx=20, pady=10, row=r,column=c)
def createEntry(self,px,r,c):
text = StringVar()
self.newEntry = Entry(self,width=8,textvariable=text)
self.newEntry.grid(padx=px, pady=10,row=r,column=c)
def printEntryData():
#code here
app = Application()
entry1 = app.createEntry(20,0,0)
entry2 = app.createEntry(20,0,1)
entry3 = app.createEntry(20,0,2)
entry4 = app.createEntry(20,0,3)
app.createButton("add",printEntryData,1,6)
app.mainloop()
Every time you make an entry you overwrite the previous value of text. All those previous Entry boxes now are orphans: there's no way to access them to get the information out. (they would have been inaccessible anyway since they are local variables).
Instead, you could add the new StringVars to a container like a list, so that you have access to all of them.
from tkinter import *
from tkinter import ttk
class Application(Frame):
def __init__(self):
Frame.__init__(self)
self.entry_list = []
self.grid()
def createButton(self,b_text,b_command,r,c):
self.newButton = Button(self, text=b_text,command=b_command)
self.newButton.grid(padx=20, pady=10, row=r,column=c)
def createEntry(self,px,r,c):
text = StringVar()
self.newEntry = Entry(self,width=8,textvariable=text)
self.newEntry.grid(padx=px, pady=10,row=r,column=c)
self.entry_list.append(text)
def printEntryData():
for entry in app.entry_list:
print(entry.get())
app = Application()
app.createEntry(20,0,0)
app.createEntry(20,0,1)
app.createEntry(20,0,2)
app.createEntry(20,0,3)
app.createButton("add",printEntryData,1,6)
app.mainloop()

Passing a variable from combobox to called function

I am trying to pass a variable from a tkinter combobox to a function called when clicking a 'Run' button. I am relatively new to python and every option I have tried creates an error - mostly that the variable is not defined. I believe this is because I am not defining it in the correct place. Any help is greatly appreciated.
from tkinter import *
from tkinter import ttk
from URL_Generator import crawl_site
listFile = open('regions1.txt','r')
root = Tk()
root.configure()
varItems = StringVar(root, value='')
class MainWindow(Frame):
def __init__(self,master = None):
Frame.__init__(self,master)
self.master = master
self.grid()
self.create_widgets()
def create_widgets(self):
"""Create Window Layout"""
self.label = Label(self, text="List Items").pack()
self.itemCombo = ttk.Combobox(self, width = 16, textvariable = varItems)
self.itemCombo.bind("<Return>", self.itemCombo_onEnter)
self.itemCombo.bind('<<ComboboxSelected>>',self.itemCombo_onEnter)
self.itemCombo['values'] = [l.strip() for l in listFile.readlines()]
self.itemCombo.pack()
self.blank = Label(self,text='').pack()
"""I want to pass the value selected in the combobox to the crawl_region() function when pushing Run"""
self.RunButton = Button(self, text="Run",command = crawl_site.crawl_region(region))
self.RunButton.pack()
def itemCombo_onEnter(self,event):
varItems.set(varItems.get().lower().strip())
mytext = varItems.get().strip()
vals = self.itemCombo.cget('values')
self.itemCombo.select_range(0,END)
print(mytext)
region = mytext
"""I want to pass mytext to the function called when pushing Run"""
if not vals:
self.itemCombo.configure(values = (mytext,))
elif mytext not in vals:
with open('regions1.txt', 'w') as f:
self.itemCombo.configure(values=vals + (mytext,))
f.write("\n".join(vals + (mytext,)))
f.close()
return 'break'
app = MainWindow(root)
root.mainloop()
Sample function called (crawl_site.crawl_region()):
class crawl_site():
def crawl_region(region):
print('passed region '+ str(region))
passed region [] is immediately returned, but nothing happens when I make a selection or press the Run button.
Try the below code.
I've created a class property self.mytext which is set when the combo button is entered itemCombo_onEnter. When the button is pressed the onRunButton function is called. if self.mytext has been set, it will call the crawl_region function with self.mytext as an argument.
from tkinter import *
from tkinter import ttk
from URL_Generator import crawl_site
listFile = open('regions1.txt','r')
root = Tk()
root.configure()
varItems = StringVar(root, value='')
class MainWindow(Frame):
def __init__(self,master = None):
Frame.__init__(self,master)
self.master = master
self.grid()
self.create_widgets()
def create_widgets(self):
"""Create Window Layout"""
self.label = Label(self, text="List Items").pack()
self.itemCombo = ttk.Combobox(self, width = 16, textvariable = varItems)
self.itemCombo.bind("<Return>", self.itemCombo_onEnter)
self.itemCombo.bind('<<ComboboxSelected>>',self.itemCombo_onEnter)
self.itemCombo['values'] = [l.strip() for l in listFile.readlines()]
self.itemCombo.pack()
self.blank = Label(self,text='').pack()
"""I want to pass the value selected in the combobox to the crawl_region() function when pushing Run"""
self.RunButton = Button(self, text="Run",command = self.onRunButton)
self.RunButton.pack()
def onRunButton(self):
if self.mytext:
crawl_site.crawl_region(self.mytext)
def itemCombo_onEnter(self,event):
varItems.set(varItems.get().lower().strip())
mytext = varItems.get().strip()
vals = self.itemCombo.cget('values')
self.itemCombo.select_range(0,END)
print(mytext)
self.mytext = mytext
"""I want to pass mytext to the function called when pushing Run"""
if not vals:
self.itemCombo.configure(values = (mytext,))
elif mytext not in vals:
with open('regions1.txt', 'w') as f:
self.itemCombo.configure(values=vals + (mytext,))
f.write("\n".join(vals + (mytext,)))
f.close()
return 'break'
app = MainWindow(root)
root.mainloop()
Your code wasn't working because of this line
self.RunButton = Button(self, text="Run",command = crawl_site.crawl_region(region))
This immediately calls the method crawl_region with the region as an argument and tries to set the callback of the button to the result of that method.
Another way to 'fix' your problem without creating another function would be to use lambda but I think my method is more readable.

Python, Tkinter library, Attribute Error in Object involving GUI

I'm making a very simple program for class that involves multiplying the number of a GUI slider by another number of another GUI slider. But, for some reason when I run the program now, I get an AttributeError saying that 'gui' object has no attribute 'slider1'. Any ideas? Here's the code:
import tkinter
import random
class gui:
def __init__(self):
self.main_window = tkinter.Tk()
#widgets
self.__canvas = tkinter.Canvas(self.main_window,bg='white',width=300,height=10)
self.label = tkinter.Label(self.main_window,text=('Product:',0))
self.slider1 = tkinter.Scale(self.main_window,from_=0, to=12)
self.slider2 = tkinter.Scale(self.main_window,from_=0, to=12)
#packs
self.__canvas.pack()
self.label.pack(side='top')
self.slider1.pack(side='left')
self.slider2.pack(side='right')
self.button = tkinter.Button(self.main_window,text='Click to multiply',command=self.multiply())
self.button.pack(side='bottom')
tkinter.mainloop()
def multiply(self):
x = int(self.slider1.get())
y = int(self.slider2.get())
num = x*y
self.label.config(text=('Product:',num))
gui()
There is a few syntax error in the program, I commented those. As well as you should put orientations on the scales. Here is the code.
import tkinter as tk
class gui:
def __init__(self):
self.root = tk.Tk()
# the widgets
self.button = tk.Button(self.root, text="Multiply!", command=self.multiply)
# you need no '()' for the function when inputing it in tkinter.
self.label = tk.Label(self.root, text="Product: 0") # the '0 must be a string
self.sliderX = tk.Scale(self.root, from_=0, to=12, orient=tk.HORIZONTAL)
self.sliderY = tk.Scale(self.root, from_=0, to=12, orient=tk.VERTICAL)
# add an orient to the scales.
# now pack the widgets.
self.button.pack()
self.label.pack()
self.sliderX.pack()
self.sliderY.pack()
def multiply(self):
x = int(self.sliderX.get())
y = int(self.sliderY.get())
num = str(x * y) # need to turn the int to a string.
self.label.config(text="Product: "+num)
app = gui()
app.root.mainloop()
The reason it isn't working for you is because there is no instance of the program. This is what I do at the very end. Python's garbage collecting collects the instance made with gui() and so Tkinter can't reference an instance of the class.

Categories