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
Related
I'm trying to create an app with Tkinter which requires the user to hit the button of the first window and then a new window will appear where they'll write their name.
But i when i try to get the name, i always end up with an empty string.
Here's my code:
from tkinter import *
class first_class(object):
def __init__(self, window):
self.window = window
b1 = Button(window, text = "first_get", command = self.get_value_2)
b1.grid(row = 0, column = 1)
def get_value_2(self):
sc = Tk()
second_class(sc)
sc.mainloop()
class second_class(object):
def __init__(self, window):
def get_value_1():
print(self.name.get())
self.window = window
self.name = StringVar()
self.e1 = Entry(window, textvariable = self.name)
self.e1.grid(row = 0, column = 0)
b1 = Button(window, text = "second_get", command = get_value_1)
b1.grid(row = 0, column = 1)
window = Tk()
first_class(window)
window.mainloop()
What should i do to get the name properly?
Generally speaking, you should avoid calling Tk() more than once within a tkinter application. It's also hardly ever necessary to call mainloop() more than once.
Your code with the changes indicated below shows how to do this. Note that I also renamed and reformatted a few things so it follows the recommendations in PEP 8 - Style Guide for Python Code more closely — which I highly recommend you read and start following.
import tkinter as tk
class FirstClass(object):
def __init__(self, window):
self.window = window
b1 = tk.Button(window, text="first_get", command=self.get_value_2)
b1.grid(row=0, column=1)
def get_value_2(self):
# sc = tk.Tk() # REMOVED
SecondClass(self.window) # CHANGED
# sc.mainloop() # REMOVED
class SecondClass(object):
def __init__(self, window):
self.window = window
self.name = tk.StringVar()
self.e1 = tk.Entry(window, textvariable=self.name)
self.e1.grid(row=0, column=0)
def get_value_1():
print('self.name.get():', self.name.get())
b1 = tk.Button(window, text="second_get", command=get_value_1)
b1.grid(row=0, column=1)
window = tk.Tk()
FirstClass(window)
window.mainloop()
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)
For homework, I have to create an application that creates a text field everytime a user clicks a button, and then get values from the fields when "submit" button is pressed.
The trace method shows up repeatedly, but I do not know how to use it. I know it requires a callback function, but what should that callback function be?
from tkinter import *
from tkinter import ttk
import sqlite3
import getpass
import wipComingIn
class Application(object):
def __init__(self,master):
self.master=master
self.ScanWIPIn = Button(master, text="Scan WIP In", width=25,
font='Calibri 12
bold',background='snow',command=self.scanWIPIn).grid(row=0, column=0,
padx=10)
def scanWIPIn(self):
incomingInventory=wipComingIn.scanIn()
def main():
root = Tk()
app=Application(root)
root.title("Main Menu")
root.configure(background="light cyan")
root.resizable(0, 0)
root.geometry('230x230+300+80')
root.mainloop()
if __name__=='__main__':
main()
class scanIn(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.geometry('300x100+350+100')
self.title('Scan In')
self.resizable(0,0)
self.num_rows=1
self.LocationLb = Label(self,text='Scan Location:',font='Arial
12').grid(row=1,column=1)
self.LocationBCText = Entry(self).grid(row=1,column=2)
self.AddLotBtn= Button(self,text="Scan
Lot",command=self.addField).grid(row=2,column=1)
self.CompleteTransaction =
Button(self,text="Complete",command=self.AddEntry).grid(row=2,column=4)
global listOfLots
listOfLots=[]
listOfLocation=[]
global rowNum
rowNum=2
def addField(self):
height =Toplevel.winfo_height(self)
height=height+25
global rowNum
rowNum=rowNum+1
listOfLots.append(StringVar())
newLot = Entry(self, textvariable=listOfLots[rowNum - 2])
newLot.grid(row=rowNum,column=2, pady=1)
listOfLots.append(StringVar())
geometryText='300'+str(height)+'350+100'
print(geometryText)
self.geometry('300x'+str(height)+'+350+100')
newLot.focus_set()
You could try just making a class that does it, for example:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('200x200')
class EntryListWidget(ttk.Frame):
"""Widget that creates a column of entry boxes."""
def __init__(self, master):
super().__init__(master)
self.entries = []
def add_entry(self):
"""Creates a new entry box and keeps reference to respective variable."""
entry_var = tk.StringVar()
self.entries.append(entry_var)
ttk.Entry(self, textvariable=entry_var).pack()
def get_entries(self):
"""Gets each entrybox text and returns as list."""
return [entry.get() for entry in self.entries]
entry_widget = EntryListWidget(root)
entry_widget.pack()
# Buttons to control adding new entry and getting their values
ttk.Button(root, text='Add Entry', command=entry_widget.add_entry).pack()
ttk.Button(root, text='Get Entries', command=entry_widget.get_entries).pack()
root.mainloop()
Just using the variable classes and not trace; I actually wouldn't use trace in this situation because I believe trace uses the callback every time the variable changes and here you have a one time "submit" button that collects all the values. You could extend this class idea to get what you're looking to do I bet.
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()
I'm having trouble with using the Entry box widget in tkinter. I want to have a new window to open when the user selects Edit -> Backbone... . In this window there will be a number of Entry widgets (just one coded for below for simplicity) that show default Strings stored in various instances the class Elements. The user should be able to edit this string and save it by clicking OK and returning it to its default value by clicking default. The entry box should always show the current value of the variable each time the backbone editor is reopened (If the whole program is restarted it does not need to remember the user input).
Upon opening the 'Backbone Editor' window the Entry box should show the string text variable but I can't make it appear.
from tkinter import *
from tkinter import ttk
class View(ttk.Frame):
"""Main GUI class"""
def __init__(self, master = None):
self.WIDTH = 450
self.HEIGHT = 500
self.lib = MolecularLibrary()
# Set up the main window
ttk.Frame.__init__(self, master, borderwidth=5, width=self.WIDTH, height=self.WIDTH)
self.master.resizable(FALSE, FALSE)
self.grid(column=0, row=0, sticky=(N, S, E, W))
self.columnconfigure(0, weight=1)
self.create_menus()
def create_menus(self):
"""Produces the menu layout for the main window"""
self.master.option_add('*tearOff', FALSE)
self.menubar = Menu(self.master)
self.master['menu'] = self.menubar
# Menu Variables
menu_edit = Menu(self.menubar)
# Add the menus to the menubar and assign their variables
self.menubar.add_cascade(menu=menu_edit, label = "Edit")
### Edit ###
menu_edit.add_command(label="Backbone...", command=lambda : self.edit_backbone())
def edit_backbone(self):
"""Shows a window where the backbone constituents can be edited"""
backbone_window = Toplevel(borderwidth = 5)
backbone_window.title("Backbone Editor")
backbone_window.resizable(FALSE, FALSE)
print("sugar value", self.lib.sugar_var)
# LABELS FOR BACKBONE #
# Phosphate annotations and input
sugar_label = ttk.Label(backbone_window, text = "Sugar")
#inputs
sugar = ttk.Entry(backbone_window, textvariable = self.lib.sugar_var, justify = 'center', width=10)
### Buttons ###
default = ttk.Button(backbone_window, text = "Defaults", command=lambda : defaults())
okay = ttk.Button(backbone_window, text = "Okay", command=lambda : okay())
cancel = ttk.Button(backbone_window, text = "Cancel", command=lambda : backbone_window.destroy())
#content.grid(column=0, row=0)
sugar_label.grid(column=2, row=1)
sugar.grid(column=1, row=2, columnspan=3)
default.grid(column=0, row=12, columnspan=3, pady=2)
okay.grid(column=6, row=12, columnspan=3, pady=2)
cancel.grid(column=9, row=12, columnspan=4, pady=2)
backbone_window.focus()
def defaults():
"""Reset the backbone and bases to their defaults."""
self.lib.set_molecules()
def okay():
"""Set the backbone and base variables to the user set values."""
self.lib.sugar_var.new_formula(sugar.get())
backbone_window.destroy()
class MolecularLibrary:
"""
"""
def __init__(self, atom_file = r'C:\MyPyProgs\OSeq\resources\ATOMS.txt',
precision = 4):
self.molecules = {}
self.atom_file = atom_file
# self.molecule_file = molecule_file
# Variables
self.set_molecules()
def set_molecules(self):
"""
Set all of the molecules for the backbone and bases to their default values and forumlae.
"""
### Sugar ###
self.sugar_var = Element('C5H8O3', 'A')
def add_molecule(self, molecule):
"""(MolecularLibrary, list) -> None
Returns a dictionary of the molecule name as an Element
{molecule[0]: Element}
"""
print(molecule)
tmp = self.get_mass(molecule[1])
return {molecule[0]: Element(molecule[1], molecule[0], tmp[0], tmp[0])}
class Element:
"""
Creates an element with the following construct:
[symbol, name, monoisotopic, average]
"""
def __init__(self, symbol, name):
self.symbol = symbol
self.name = name
def __str__(self):
return str([self.symbol, self.name])
def get_name(self):
"""Returns the name of the Element"""
return self.name
def get_symbol(self):
"""Returns the symbol of the Element"""
return self.symbol
def new_formula(self, new_formula):
"""replace the formula with new_formaula and recalculate the
average and monoisotopic masses."""
self.symbol = new_formula
if __name__ == "__main__":
root = Tk()
root.title("Sequencer")
view = View(root)
root.mainloop()
The code above is a heavily stripped version of my program but has the same basic architecture. I'm afraid there is still quite a lot of code, I would normally try strip it down more, but I'm not sure if the issue I'm having is from the architecture or not.
In order to use the textvariable attribute, you must give it an instance of a tkinter variable: StringVar, IntVar, BooleanVar, or DoubleVar
A good starting point to learn more about these variables is here: http://effbot.org/tkinterbook/variable.htm