Checkbutton won't be set to variable value - python

I created a simple Python Tkinter gui and I can't manage to have Checkbutton initalize with the correct values, both are always unchecked, no matter what I do, whereas the two prints in the code display :
('dryRunVar', 0)
('useGenreSubFolderVar', 1)
Tried whatever I could with BooleanVar also, and couldn't make it work
Strangely enough if I change instanciation of CheckButton() to ttk.CheckButton(), then both buttons are in 'gray' state
Even changing call to configuration values to constants 0 and 1 doesn't change anything, Buttons stay unchecked
Also try to tweak a little around Tk instanciation and mainloop, no success
#!/usr/lib/python2.7/
# -*- coding: utf-8 -*-
from Tkinter import *
import conf,ttk
class GUI():
def __init__(self,window, configuration) :
self.configuration = configuration
self.window = window
self.draw()
def draw(self) :
self.root = Frame(self.window,padx=15,pady=15,width=800,height=200)
self.root.grid(column=0,row=0)
self.drawParametersFrame()
def drawParametersFrame(self) :
#Parameters frame
self.parametersFrame = LabelFrame(self.root,text="Sorting Parameters",padx=15,pady=15)
self.parametersFrame.grid(column=0,row=2,sticky="EW")
dryRunVar = IntVar()
dryRunVar.set(self.configuration['dryRun'])
print("dryRunVar",dryRunVar.get())
dryRunCheckButton = Checkbutton(self.parametersFrame,text="Dry Run", variable=dryRunVar, onvalue=1, offvalue = 0)
dryRunCheckButton.grid(column=0,row=0,sticky="W")
useGenreSubFolderVar = IntVar()
useGenreSubFolderVar.set(self.configuration['genreSubFolders'])
print("useGenreSubFolderVar",useGenreSubFolderVar.get())
useGenreSubFolderCheckButton = Checkbutton(self.parametersFrame,text="Use genre subfolders", variable=useGenreSubFolderVar, onvalue=1, offvalue = 0)
useGenreSubFolderCheckButton.grid(column=2,row=0,sticky="W")
if __name__ == "__main__":
configuration = conf.loadConf(r"/home/thomas/code/perso/python/conf.conf")
window = Tk()
gui = GUI(window,configuration)
window.mainloop()

Make your IntVar an attribute of the class instead.
def drawParametersFrame(self) :
...
self.dryRunVar = IntVar()
self.dryRunVar.set(1)
dryRunCheckButton = Checkbutton(self.parametersFrame,text="Dry Run", variable=self.dryRunVar, onvalue=1, offvalue = 0)
...
self.useGenreSubFolderVar = IntVar()
self.useGenreSubFolderVar.set(1)
useGenreSubFolderCheckButton = Checkbutton(self.parametersFrame,text="Use genre subfolders", variable=self.useGenreSubFolderVar, onvalue=1, offvalue = 0)
...

Related

Is there a way to create a second window that connects to the parent window like a dropdown menu

I'm trying to make it so that new information shows in in a new window, but I want the new window to be connected to the parent window, even when the parent window is clicked the new window should still show up similar to how a dropdown menu works. I'm also planning on having some of the new windows have treeviews later on.
from tkinter import *
win = Tk()
win.geometry("500x500+0+0")
def button_function ():
win2 = Toplevel()
label = Label(win2,text='dropdown', width=7)
label.pack()
win2.geometry(f"+{win.winfo_x()}+{win.winfo_y()+30}")
button = Button(win, command=lambda: button_function (), width=12)
button.pack()
win.mainloop()
Ok so with a little bit of googling I came across this post: tkinter-detecting-a-window-drag-event
In that post they show how you can keep track of when the window has moved.
By taking that code and making some small changes we can use the dragging() and stop_drag() functions to move the top level window back to where it was set to relative to the main window.
That said this will only work in this case. You will need to write something more dynamic to track any new windows you want so they are placed properly and on top of that you will probably want to build this in a class so you do not have to manage global variables.
With a combination of this tracking function and using lift() to bring the window up we get closer to what you are asking to do.
That said you will probably want remove the tool bar at the top of the root window to be more clean. I would also focus on using a dictionary or list to keep track of open and closed windows and their locations to make the dynamic part of this easier.
import tkinter as tk
win = tk.Tk()
win.geometry("500x500+0+0")
win2 = None
drag_id = ''
def dragging(event):
global drag_id
if event.widget is win:
if drag_id == '':
print('start drag')
else:
win.after_cancel(drag_id)
print('dragging')
drag_id = win.after(100, stop_drag)
if win2 is not None:
win2.lift()
win2.geometry(f"+{win.winfo_x()}+{win.winfo_y() + 30}")
def stop_drag():
global drag_id, win2, win
print('stop drag')
drag_id = ''
if win2 is not None:
win2.lift()
win2.geometry(f"+{win.winfo_x()}+{win.winfo_y() + 30}")
win.bind('<Configure>', dragging)
def button_function():
global win2
win2 = tk.Toplevel()
label = tk.Label(win2, text='drop down', width=7)
label.pack()
win2.geometry(f"+{win.winfo_x()}+{win.winfo_y()+30}")
tk.Button(win, command=button_function, width=12).pack()
win.mainloop()
EDIT:
Ok so I took some time to write this up in a class so you could see how it could be done. I have also added some level of dynamic building of the buttons and pop up windows.
We use a combination of lists and lambdas to perform a little bit of tracking and in the end we pull off exactly what you were asking for.
let me know if you have any questions.
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('500x500')
self.pop_up_list = []
self.drag_id = ''
self.button_notes = ['Some notes for new window', 'some other notes for new window', 'bacon that is all!']
self.bind('<Configure>', self.dragging)
for ndex, value in enumerate(self.button_notes):
print(ndex)
btn = tk.Button(self, text=f'Button {ndex+1}')
btn.config(command=lambda b=btn, i=ndex: self.toggle_button_pop_ups(i, b))
btn.grid(row=ndex, column=0, padx=5, pady=5)
self.pop_up_list.append([value, 0, None, btn])
def dragging(self, event):
if event.widget is self:
if self.drag_id == '':
pass
else:
self.after_cancel(self.drag_id)
self.drag_id = self.after(100, self.stop_drag)
for p in self.pop_up_list:
if p[1] == 1:
p[2].lift()
p[2].geometry(f"+{p[3].winfo_rootx() + 65}+{p[3].winfo_rooty()}")
def stop_drag(self):
self.drag_id = ''
for p in self.pop_up_list:
if p[1] == 1:
p[2].lift()
p[2].geometry(f"+{p[3].winfo_rootx() + 65}+{p[3].winfo_rooty()}")
def toggle_button_pop_ups(self, ndex, btn):
p = self.pop_up_list
if p[ndex][1] == 0:
p[ndex][1] = 1
p[ndex][2] = tk.Toplevel(self)
p[ndex][2].overrideredirect(1)
tk.Label(p[ndex][2], text=self.pop_up_list[ndex][0]).pack()
p[ndex][2].geometry(f"+{btn.winfo_rootx() + 65}+{btn.winfo_rooty()}")
else:
p[ndex][1] = 0
p[ndex][2].destroy()
p[ndex][2] = None
if __name__ == '__main__':
Main().mainloop()

Tkinter Python / tk - Affecting other widgets of tkinter and same class object

MY CODE :
Here's the output in OOP way:
You can see the 2nd object's entry field changed automatically without any reason. I dont want it to be changed.
When I run this code both objects of Entry2 Class don't work properly as I wanted.
Where am I doing mistake . I hope this is clear for you to understand what i want.
from tkinter import *
root = Tk()
#"""
#Orange Theme
bgcolor = "#ef6c45"
bgcolor2 = "#fcf0e2"
textcolor = "#8e5541"
tabcolor = "#F6E8D5"
#"""
root.config(bg=bgcolor)
root.geometry("250x200")
Dname = StringVar()
dname = StringVar()
#Label For root
Label(root,bg=bgcolor2).place(x=3,y=3,height=194,width=244)
#Class for creating a unique entry
class Entry2(Label):
def __init__(self,perant,var,text,x,y,h,w,*args,**kwargs):
Label.__init__(self,*args,**kwargs,bg=tabcolor) # calling Label Class
self.text = text
self.var = var
self.x = x # x-axis of obj
self.y = y # y-axis
self.h = h # height
self.w = w # width
self.place(x=self.x,
y=self.y,
height=self.h,
width=self.w) # placing self object
self.entry = Entry(perant,
bd=0,
font=("Helvetica",11),
bg=tabcolor,
fg=textcolor,
textvariable=self.var,
insertbackground=textcolor,
selectbackground=bgcolor,
selectforeground='white')
self.INFO = self.place_info() # Placing information of self to use it for further objects
self.entry.place(x=int(self.INFO['x'])+2,y=int(self.INFO['y'])+13,
height=int(self.INFO['height'])-15,
width=int(self.INFO['width'])-4,
)
self.label = Label(perant,bd=0,
# text=self.text,
bg=tabcolor,
fg=textcolor,
font=("Helvetica",11,'bold'),
anchor='w'
) # Title
self.label.place(x=int(self.INFO['x'])+2,
y=int(self.INFO['y'])+2,
width=int(self.INFO['width'])-4,
height=20,
)
self.entry.delete(0,END)
self.entry.insert(0,f'<{self.text}>')
self.entry.bind("<FocusIn>",self.efi)
self.entry.bind("<FocusOut>",self.efo)
def efi(self,event): # Function for changing objects on focus in
if self.var.get()==f"<{self.text}>":
self.entry.delete(0,END)
self.label.config(text="")
self.label.config(text=self.text)
self.config(bg=bgcolor)
def efo(self,event): # function for changing objects on focus Out
if self.var.get()=="":
self.entry.delete(0,END)
self.entry.insert(0,f"<{self.text}>")
self.label.config(text="")
self.config(bg=tabcolor)
name = Entry2(root,var=Dname,x=10,y=20,h=45,w=220,text='Name') # Object 1
number = Entry2(root,var=dname,x=10,y=100,h=45,w=220,text='Number') # Object 2
Button(root,text="Submit",bg=bgcolor,
fg='white',
activebackground=tabcolor,
activeforeground=textcolor,
bd=0).place(x=10,y=160,height=30,width=100) # Submit Button
Button(root,text="Cancel",bg=tabcolor,
fg=textcolor,
activebackground=tabcolor,
activeforeground=textcolor,
bd=0).place(x=130,y=160,height=30,width=100) # Cancel Button
root.mainloop()

Update tkinter canvas when button is clicked

I want to update the name of the child when you press one of the buttons below. With this lambda function, I always get the last name no matter which button is pressed. Is it possible to fix it using lambda functions? As far as I have seen lambda functions work well with define values but I have not seen any examples with variables. How can I fix it? Thanks,
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------------------------------
__title__= 'Control'
__date__ = '10/07/2017'
__version__ = '0.0.1'
from tkinter import *
from tkinter import ttk
class Panel():
def __init__(self, database, name):
self.root = Tk()
self.root. title('Control V' + __version__)
self.root.configure(bg='beige')
self.window = ttk.Frame(self.root, borderwidth=2, relief="raised",
padding=(10,10))
self.window.grid(column=0, row=0)
self.window.option_add("*Font", "Helvetica 12")
# Row 0
self.header = ttk.Label(self.window, text=name, padding=(1,1),
anchor='center')
self.header.grid(row=0, column=0, columnspan=3)
# Row 1
for col, record in enumerate(database, 0):
name = database[col]['Letter']
label = ttk.Button(self.window, text=name,
command=lambda *args: self.get_name(col, database))
label.grid(row=1, column=(col), sticky='ew')
# All set
self.root.mainloop()
def get_name(self, row, database):
name = database[row]['Name']
self.header.configure(text=name)
def main():
database = [
{'Letter': 'A', 'Name': 'Zara'},
{'Letter': 'B', 'Name': 'Ezra'},
{'Letter': 'C', 'Name': 'Amy'},
]
name = database[0]['Name']
my_panel = Panel(database, name)
return 0
if __name__ == '__main__':
main()
If you change get_name to a curried function:
def get_name(row, database):
name = database[row]['Name']
def _get_name():
self.header.configure(text=name)
return _get_name
and replace the command declaration with this returned function:
label = ttk.Button(self.window, text=name,
command=self.get_name(col, database))
the result is as expected.
(What this does is define the return value of the button click when you create the button, rather than trying to pass in a parameter later. Your original code had all the buttons depend on 'col' (the variable). This remains the last thing it was set to (2->Amy), which explains why you were always getting that result.)

Allow user to change default text in tkinter entry widget.

I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it

Setting a tkinter variables using a generic set_value() method

I'd like to have a generic method that will change the value of a tkinter variable. I want to be able to do this from a menu option that brings up a new dialog box which is from a generic method that can be used for different variables. the code I have so far is below:
import sys
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
import sequencer as seq
class View(ttk.Frame):
"""Main Gui class"""
def __init__(self, master = None):
ttk.Frame.__init__(self, master, borderwidth=5, width=450, height=500)
self.master = master
self.grid(column=0, row=0, sticky=(N, S, E, W))
self.columnconfigure(0, weight=1)
###############################
### User editable variables ###
self.precision = IntVar(value=4, name='precision')
self.sensitivity = IntVar(value = 50, name='sensitivity')
### User editable variables ###
###############################
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_file = Menu(self.menubar)
menu_edit = Menu(self.menubar)
# Add the menus to the menubar and assign their variables
self.menubar.add_cascade(menu=menu_file, label="File")
self.menubar.add_cascade(menu=menu_edit, label = "Edit")
### ADD COMMANDS TO THE MENUES ###
### File ###
menu_file.add_command(label="Quit", command=self.master.destroy)
### Edit ###
menu_edit.add_command(label="Backbone", command=lambda : self.edit_backbone())
menu_edit.add_command(label="Precision", command=lambda : self.precision.set(self.set_value_int("Precision")))
menu_edit.add_command(label="Sensitivity", command=lambda : self.sensitivity.set(self.set_value_int("Sensitivity")))
menu_edit.add_command(label="print precision", command=lambda : print(self.precision.get()))
menu_edit.add_command(label="print sensitivity", command=lambda : print(self.sensitivity.get()))
def set_value_int(self, name):
"""Standards dialog that return a user define value of a specific type"""
t = Toplevel(self)
t.title("Set " + name)
label = ttk.Label(t, text="Set "+name)
label.grid(row=0)
entry = ttk.Entry(t)
entry.grid(row=1)
cancel = ttk.Button(t, text="Cancel", command=lambda : t.destroy())
cancel.grid(column=0, row=2)
okey = ttk.Button(t, text="Okey", command=lambda : okey(entry.get()))
okey.grid(column=1, row=2)
def okey(value):
"""return value according to type"""
try:
t.destroy()
return int(value)
except:
self.error_box("value must be and integer")
def error_box(self, error_message="Unknown error"):
"""(Frame, String) -> None
Opens an window with an Okey button and a custom error message"""
t=Toplevel(self)
t.title("Error")
label = ttk.Label(t, text=error_message)
label.grid(row=0)
okey = ttk.Button(t, text="Okey", command=lambda : t.destroy())
okey.grid(row=1)
if __name__ == "__main__":
root = Tk()
root.title("Sequencer")
view = View(root)
root.mainloop()
print("End")
The Edit-> print xxxxxx commands are purely for testing purposes to check if the values have changed. If these are executed before trying to change the values of precision or sensitivity then they work as expected.
If you try to change either of the tkinter variables in the way I have tried to do they become None types and I can't see why. I can only assume that you are not allowed to change them in the way that I have but I can't think of another way to do it without having a separated method for each variable which I'd like to avoid.
Baicly I'd like the user to be able to customise the variables precision and sensitivity and use the same method in the code to change the values.
Extra but not necessarily vital:- If there is a way to define which type the variable should be in the methods arguments as well that would be even better as I will have other variables for the user to change later and they will be of different types.
Any help would be much appreciated. I have tried to be as clear as I can but let me know if anything is not.
Thanks
self.set_value_int always returns None, so it's always going to set your variable to none.
Instead of trying to write a complex lambda that is hard to debug, put all of your logic inside the function. Have the function set the value. All you need to do is tell it what variable to set:
menu_edit.add_command(label="Precision",
command=lambda name="Precision", var=self.precision: self.set_value_int(name, var))
...
def set_value_int(self, name, var):
...
def okey():
s = entry.get()
try:
var.set(int(s))
...

Categories

Resources