I'm trying to get the following code to create 2 buttons which when you press one button will show the fader and when the other button is pressed will hide the fader, but obviously this isn't working i think this is mainly because i can't get my head around how booleans work in python so if somebody could help me i would greatly appreciate it.
from tkinter import *
#first window
master= Tk()
master.geometry('1440x900+0+0')
master.title('DMX512 Controller')
#buttons
bw=250
bh=110
bool1show = False
Button(master,text="show the slider", command =bool1show= True).place(x=800,y=10)
Button(master,text="hide the slider", command = bool1show= not True).place(x=900,y=10)
#slider characteristics
slw=130
sll=600
sly=1
stc='blue'
if bool1show==True:
Scale(master, from_=255, to=0, length =sll,width =slw, troughcolor = stc).grid(row=sly,column=5)
if bool1show==not True:
Scale(from_=255, to=0, length =sll,width =slw, troughcolor = stc).grid(row=sly,column=5)
You have to give a reference to a function in the command parameter, and bool1show= True is not a reference to a function. However, since all you're doing is showing or hiding a widget, you don't need to use a boolean variable at all unless you're using radiobuttons (which you're not).
For the code you've posted, you just need two functions: one to show the slider and one to hide it. To do that, you create the Scale once, and then use the grid methods to show and hide it. To make that work, you have to "remember" the scale widget by saving a reference in a global variable.
def show_scale():
# note: grid will remember the values you used when you first called grid
the_scale.grid()
def hide_scale():
# this removes the scale from view, but remembers where it was
the_scale.grid_remove()
the_scale = Scale(master, from_=255, to=0, length =sll,width =slw, troughcolor = stc)
the_scale.grid(row=sly, column=5)
...
Button(master,text="show the slider", command show_scale).place(x=800,y=10)
Button(master,text="hide the slider", command hide_scale).place(x=900,y=10)
On an unrelated note, I strongly encourage you to not use place. Your GUIs will be easier to write and maintain, and they will behave better when being resized or run on systems with different resolutions or different fonts.
How about if you assign the command argument to a lambda function that calls another function:
Button(master,text="show the slider", command=lambda: bool_func(bool1show).place(x=800,y=10)
Button(master,text="hide the slider",command=lambda: bool_func(bool1show,b=False).place(x=900,y=10)
where:
def bool_func(variable, b=True): # b is True by default, hence you don't provide the argument in the first Button statement.
variable = b
return variable
for some info on lambda functions, look here
There is some ambiguity in your code so ii'm not sure what you are trying to achieve. If you want to verify a True condition you can do:
if bool1show == True:
do stuff...
#or
if bool1show:
do stuff...
if bool1show==not True: does not work. If you really want to go like this you can do:
if bool1show==(not True)
#or better:
if bool1show == False
#even better:
if not bool1show:
Hope this helps understand better python booleans
Related
I have a scale and an input field which both control the same variable to give the user choice of which one they'd like to use. I've coded it a bit like this:
def scale_has_moved(value):
entry_field.delete(0, END)
entry_field.insert(0, str(float(value)))
# Other functions I want the code to do
def entry_field_has_been_written(*args):
value = float( entry_field.get() )
scale.set(value)
This works, when I move the scale the entry_field gets written in and vice versa, and the other functions I want the code to do all happen. The obvious problem is the functions call each other in a loop, so moving the scale calls scale_has_moved() which calls the additional functions within and writes in the entry field, then because the entry field has been written in entry_field_has_been_written() gets called which in turn calls scale_has_moved() again, it doesn't go in an endless loop but it does everything at least twice everytime which affects performance.
Any clue how I'd fix this? Thank you
If you use the same variable for both widgets, they will automatically stay in sync. You don't need your two functions at all. The following code illustrates the technique.
import tkinter as tk
root = tk.Tk()
var = tk.IntVar(value=0)
scale = tk.Scale(root, variable=var, orient="horizontal")
entry = tk.Entry(root, textvariable=var)
scale.pack(side="top", fill="x")
entry.pack(side="top", fill="x")
root.mainloop()
My problem is that when creating a Radiobutton it is automatically checked and i can't uncheck it. I create it inside a frame of x and y dimensions.
I've tried the .deselect() function but it changes nothing
(Python 3.6)
code:
frm = ttk.Frame(root)
frm.place(x=0,y=0,width=1000,height=1000)
Ek = ttk.Radiobutton(frm,text="text")
Ek.place(x=100,y=400)
And photo of it:
photo
First, if we just wanted to modify your code to give us a single unchecked radio button all by itself, this would do the trick.
from tkinter import Tk, IntVar, Radiobutton, mainloop, ttk
root = Tk()
frm = ttk.Frame(root)
frm.place(x = 0, y = 0, width = 1000, height = 1000)
v = IntVar()
Ek = ttk.Radiobutton(frm, text = "text", variable = v, value = 1)
Ek.place(x = 100, y = 100)
mainloop()
Aside from the boilerplate for setup at the beginning and end, the only thing we had to change in your original code was to add the arguments variable = v, value = 1 to the Radiobutton call.
Those extra arguments don't really make sense in isolation, for the same reason that it doesn't generally make sense to have a single radio button. Once we add two of them, we can see what's going on a bit better.
In the documentation #Stack posted (this thing), the first code sample looks like this:
from Tkinter import *
master = Tk()
v = IntVar()
Radiobutton(master, text="One", variable=v, value=1).pack(anchor=W)
Radiobutton(master, text="Two", variable=v, value=2).pack(anchor=W)
mainloop()
If we run that, we get two unchecked radio buttons by default. If we then change the value=1 part to value=0, the first radio button shows up checked, and if we change value=2 to value=0, the second radio button shows up checked. So value=0 seems to give us buttons that are checked by default, but we don't know why yet. Let's experiment a bit more.
If we try to delete pieces in the new sample until we get back to something more closely resembling what you wrote originally, we can sort of see what happened. Deleting the value arguments entirely and running it like this:
Radiobutton(master, text="One", variable=v).pack(anchor=W)
Radiobutton(master, text="Two", variable=v).pack(anchor=W)
leaves us with neither button checked by default, though then further deleting the variable arguments to make the code look like your original call:
Radiobutton(master, text="One").pack(anchor=W)
Radiobutton(master, text="Two").pack(anchor=W)
gives us two buttons that are both checked by default, which gets us back to your original problem.
Basically, we're running into various odd corner cases here because we just started fiddling with code and forgot what a radio button actually represents.
What the concept of a radio button represents in the first place is the value of a variable. Not the entire variable, just one of the things it might be equal to. And the set of radio buttons itself, taken together, gives us a visual representation of a discrete variable: a thing that can be in 1 of N states.
So the API for Radiobuttons, naturally, is asking us for some information like "what python variable do you want us to use to hold these values?" (that's roughly the variable keyword) and "what values do you want us to glue to each of these buttons behind the scenes to distinguish the different states?" (that's the value keyword).
As expected, the code works best in the case above where the values were 1 and 2, because in that case the code is properly reflecting what a radio button actually is, conceptually. When we collide the values or set them to zero or leave them out entirely, things get a bit weird and less predictable because we're then dealing with the implementation details of the tkinter API, rather than with the simple concept of a radio button that the API is meant to implement.
Laptop's about to die, so I'm gonna go ahead and hit send. Hope that wasn't too wordy. Good luck. :)
Radiobuttons need to be associated with one of the special Tkinter variables (StringVar, etc), and are designed to work in groups of two or more. If you don't specify a variable, one will be created for you. The default value of a Radiobutton is the empty string, which is also the default variable will be set to.
Just assign a different value to the declared variable
from tkinter import Tk, IntVar, Radiobutton, mainloop, ttk
root = Tk()
frm = ttk.Frame(root)
frm.place(x=0,y=0,width=100,height=400)
language=StringVar(value='portuguese')
Ek = ttk.Radiobutton(frm,variable="language",text="spanish",value="spanish")
Ek.place(x=10,y=50)
Ek = ttk.Radiobutton(frm,variable="language",text="english",value="english")
Ek.place(x=10,y=85)
mainloop()
Basically I want to execute the command as seen below and also close the window with the 2 buttons (in either case). The command just sets the variable self.switch to True or False, so if there is a neater way of doing this rather than writing a new method for each of those, that would also be nice.
def switchButton(self):
top = Tk()
self.a = Button(top,text="Switch", command=self.switchTrue())
self.a.pack(side=LEFT)
self.b = Button(top,text="Don't switch", command=self.switchFalse())
self.b.pack(side=RIGHT)
top.mainloop()
Doing it in a new method is the neat way of doing things- you'd probably make the command=lambda:self.switchTo([value]), with [value] being True or False that you would like to swap to. You would have to pass top into the function as well, so you can destroy it.
Doing this destruction with Tk() is (normally) bad practice, as Tk() creates a Tcl/Tk interpreter- you really just want to instantiate it once and hide it typically.
folks! So, thanks to you guys I was able to figure out what it was I was doing wrong in my previous script of staggering animation for selected objects in a scene. I am now on part two of this little exercise: Creating a UI for it.
This involves creating a window with a button and user input of how much the animation will be staggered by. So, instead of me putting how much the stagger should increment by (which was two in my previous script), I'd now allow the user to decide.
The script I have so far created the window, button, and input correctly, though I am having some trouble with getting the UI to properly execute, meaning when I click on the button, no error pops up; in fact, nothing happens at all to change the scene. I get the feeling it's due to my not having my increment variable in the correct spot, or not utilizing it the right way, but I'm not sure where/how exactly to address it. Any help would be greatly appreciated.
The code I have (with suggested edits) is as follows:
import maya.cmds as cmds
spheres = cmds.ls(selection=True)
stagWin = cmds.window(title="Stagger Tool", wh=(300,100))
cmds.columnLayout()
button = cmds.button(label="My Life For Aiur!")
count = cmds.floatFieldGrp(fieldgroup, query=True, value=True)
fieldgroup = cmds.floatFieldGrp(numberOfFields=1)
cmds.button(button, edit=True, command=lambda _:stagger(fieldgroup))
cmds.showWindow(stagWin)
def stagger(fieldgroup):
for i in spheres:
cmds.selectKey(i)
cmds.keyframe(edit=True, relative=True, timeChange=count)
print "BLAH"
Moving the comments into an answer because I think I've got it all figured out finally:
First of all, the better practice is to pass the stagger object to the button command rather than the string. so that would be:
cmds.button(label="My Life For Aiur!", command=stagger)
Secondly, the count isn't getting updated, so it stays 0 as per your third line. To update that:
count = cmds.floatFieldGrp(fieldgroup, query=True, value=True)
But wait, where did fieldgroup come from? We need to pass it into the function. So go back to your button code and take out the command entirely, also saving the object to a variable:
button = cmds.button(label="My Life For Aiur!")
Now store the object for the fieldgroup when you make it:
fieldgroup = cmds.floatFieldGrp(numberOfFields=1)
Now that you have fieldgroup, you can pass that in the function for the button, like this:
cmds.button(button, edit=True, command=lambda _:stagger(fieldgroup))
I had to wrap the function in a lambda because we're passing fieldgroup, but if I just put stagger(fieldgroup) it would call that and pass the result of that into the command for the button
Also update stagger def with fieldgroup argument:
def stagger(fieldgroup):
One final note that won't actually affect this, but good to know:
when you shift the keyframes inside stagger you're using a DIFFERENT count variable than the one you declared as 0 up above. The outer one is global, and the inner is local scope. Generally it's best to avoid global in the first place, which fortunately for you means just taking out count = 0
Putting that all together:
import maya.cmds as cmds
spheres = cmds.ls(selection=True)
stagWin = cmds.window(title="Stagger Tool", wh=(300,100))
cmds.columnLayout()
button = cmds.button(label="My Life For Aiur!")
fieldgroup = cmds.floatFieldGrp(numberOfFields=1)
cmds.button(button, edit=True, command=lambda _:stagger(fieldgroup))
cmds.showWindow(stagWin)
def stagger(fieldgroup):
count = 0
increment = cmds.floatFieldGrp(fieldgroup, query=True, value=True)[0]
print count
for i in spheres:
cmds.selectKey(i)
cmds.keyframe(edit=True, relative=True, timeChange=count)
count += increment
print "BLAH"
I'm using Python 2.7, if that matters.
Here is a code I wrote for fun:
def p():
root = Tk()
def cmd(event):
print int(slider.get())
slider = Scale(root, orient = "horizontal", from_ = 0, to = 100, command = cmd, state = "disabled")
def enable():
slider.config(state = "active")
b = Button(root, text = "Enable slider", command = enable)
b.grid()
slider.grid(row = 1)
root.mainloop()
For this code, I'm wondering why the command for Scale requires an event, but that for Button does not. It seems that for some widgets in Tkinter their commands need to have "event" as an argument, and other don't. Why? How to distinguish them?
Thanks.
Scale doesn't take an event. It takes the current value. Try this:
def cmd(value):
print int(value)
If you read the Tk tutorial, it explains this:
There is a "command" configuration option, which lets you specify a script to call whenever the scale is changed. Tk will automatically append the current value of the scale as a parameter each time it invokes this script (we saw a similar thing with extra parameters being added to scrollbar callbacks and those on the widgets they scroll).
Or, if you read the actual manpage:
Specifies the prefix of a Tcl command to invoke whenever the scale's value is changed via a widget command. The actual command consists of this option followed by a space and a real number indicating the new value of the scale.
In other words, the way to distinguish them is to read the docs. Unfortunately, the Tkinter docs aren't all that complete—they assume you already know how Tcl/Tk works, or how to look it up yourself. That's why the docs start off with a list of links to sources of Tk documentation.
If you prefer to figure it out by trial and error, it's not that hard to see what gets passed:
def cmd(*args):
print('Scale command says {}'.format(args))
def enable(*args):
print('Button command says {}'.format(args))
But this won't always tell you everything you need to know; there are other callbacks whose arguments aren't obvious enough to figure out without a lot more work, or which are configurable (e.g., the validate callback).
When you set up a binding (with the bind) command, the callback always is given an event object.
When you are working with the command attribute of a widget, different widgets send different information to the command. In this case they never send an event, but they will send other types of data. This is simply due to the fact different commands do different things.
The scale widget is no different -- you claim the callback takes an event, but that is false. It is passed the current value of the scale widget rather than an event object.