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()
Related
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()
I'm using Tkinter to create a GUI for a simple geometry calculator I'm creating.
Basically, what I have is an Entry box. What I want is for the program/GUI/system to detect when the user of the program hits the 'Enter' or 'return' key WHILE they are in the Entry box. When this is detected, I want the contents of the Entry box to be appended to a list I have defined earlier. I also want a simple label to be created on the GUI that displays the contents of the list (including the appended item(s)). Note that the list begins with nothing in it.
Here is my code so far:
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint).grid(row=0, column=0)
app.bind('<Return>', list_add)
mainloop()
I don't really know the proper way to check for 'Return' and then use it in an if statement.
I hope you understand what I'm trying to get help with, and I've looked all around for an explanation that I could understand with no success.
Instead of binding with the app just bind it with the Entry widget object,i.e,e1
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
print ("hello")
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint)
e1.grid(row=0, column=0)#use grid in next line,else it would return None
e1.bind('<Return>', list_add)# bind Entry
mainloop()
The solution is to set the binding on the widget itself. That way, the binding will only apply while focus is on that widget. And since you're binding on a specific key, you don't need to check for the value later. You know the user pressed return, because that's the only thing that will cause the binding to fire.
...
e1.bind('<Return>', list_add)
...
You have another problem in that your list_add function needs to call the get method of the variable rather than accessing the variable directly. However, since you aren't using any of the special features of a StringVar, you really don't need it -- it's just one more thing you have to manage.
Here's how to do it without the StringVar:
def list_add(event):
PointLit.append(e1.get())
...
e1 = Entry(app)
e1.grid(row=0, column=0)
e1.bind('<Return>', list_add)
Note that you need to create the widget and lay out the widget in two steps. Doing it the way you did it (e1=Entry(...).grid(...) will cause e1 to be None since that is what .grid(...) returns.
I am creating a revision program for myself however whenever the fun1 function is called it prints out underneath the previously executed function. e.g the label will print out underneath the last one instead of replacing it, any ideas? Any help would be appreciated!!
#Imports moduals used
from tkinter import *
import time
import random
#Sets GUI
gui = Tk()
gui.geometry("500x500")
gui.maxsize(width=500, height=500)
gui.minsize(width=500, height=500)
#Sets list of facts
def t():
print("hi")
facts = ['fact one','true', 'fact two','abc']
#Defines random fact generator
def fun1():
r = random.randrange(len(facts))
lbl = Label(gui,text=facts[r]).pack()
btnt = Button(text="True", command=t).pack()
btnf = Button(text="False", command=t).pack()
gui.after(5000, fun1)
gui.after(5000, fun1)
mainloop()
Overview
The best way to write this sort of program is to create the label or button only once, and then use the configure method to change the text of the the label.
Using a procedural style
Here's an example based off of your original code:
#Imports moduals used
from tkinter import *
import time
import random
#Sets GUI
gui = Tk()
gui.geometry("500x500")
gui.maxsize(width=500, height=500)
gui.minsize(width=500, height=500)
def t():
print("hi")
#Defines random fact generator
def fun1():
r = random.randrange(len(facts))
lbl.configure(text=facts[r])
gui.after(5000, fun1)
#Sets list of facts
facts = ['fact one','true', 'fact two','abc']
# create the widgets
lbl = Label(gui,text="")
btnt = Button(text="True", command=t)
btnf = Button(text="False", command=t)
# lay out the widgets
lbl.pack(side="top", fill="x", expand=True)
btnt.pack(side="left", fill="x")
btnf.pack(side="right", fill="y")
# show the first fact; it will cause other
# facts to show up every N seconds
fun1()
mainloop()
Using an object-oriented style
Since you're just getting started, let me suggest a better way to organize your code. Python is object-oriented in nature, so it makes sense to make your application an object. Even though you may not be familiar with classes an objects, if you start with this pattern and follow a couple of simple rules, you can get all the benefits of object orientation without much effort.
The only thing you need to remember is that in your main class, all functions need to have self as the first parameter, and when calling a function you use self.function and omit self as an argument (python does that for you). Other than that, you can pretty much code as normal inside a class.
Here's an example:
import tkinter as tk
import random
class Example(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.facts = ['fact one','true', 'fact two','abc', 'a long fact\non two lines']
self.label = tk.Label(self,text="", width = 40, height=4)
self.true_button = tk.Button(self, text="True", command=self.t)
self.false_button = tk.Button(self, text="False", command=self.t)
self.label.pack(side="top", fill="both", expand=True, pady=40)
self.true_button.pack(side="left", fill="x")
self.false_button.pack(side="right", fill="x")
self.fun1()
def t(self):
print("hi")
def fun1(self):
r = random.randrange(len(self.facts))
self.label.configure(text=self.facts[r])
self.after(5000, self.fun1)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Here are a few things to notice:
import tkinter as tk this requires a tiny bit more typing, but it makes your program more robust. The side effect of this (which is a good side effect IMO) is that all tkinter commands now need to be prefixed with tk (eg: tk.Button). In my opinion this is a much better way than just blindly importing everything from tkinter as a bunch of global variables and functions. I know must tutorials show from tkinter import *, but they are misguided.
The main part of your logic is a class. This makes it easy to avoid using global variables. As a rule of thumb when programming, you should avoid global variables.
Notice how self is an argument to t, fun1 and __init__ -- this is a requirement for python classes. It's just how you do it. Also notice we call them like self.fun1 rather than just fun1. This lets python know that you want to call the function associated with the current object. Once your program gets bigger, this makes it easier to know where fun1 is defined.
I removed the code that forces the size of the GUI. Tkinter is really good at calculating what the size should be, so let it do it's job. Also, if you take care when laying out your widgets, they will grow and shrink properly when the window is resized. This means you don't need to force a min or max size to a window. Forcing a size gives a bad user experience -- users should always be able to resize windows to fit their needs.
I separated the creation of the widgets from the layout of the widgets. For one, you have to separate them if you want to keep references to widgets. This is because this: lbl=label(...).pack() sets lbl to None. That's just how python works -- the last function is what gets saved to a variable, and both pack and grid always return none. The second reason is simply that it makes your code easier to write and maintain. All of the code that organizes your widgets is in one place, making it easier to see the big picture.
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
I am not sure if I am on the right track here-- but I started GUI programming with Python.
I have all of my buttons and entries worked out. The main problem I am having is with the method that rolls my die and places the result.
def roll(self):
self.die = Die(int(self.sides.get())) # gets from label
t = self.die.roll()
t += int(self.mod.get()) # gets from label
self.result = Label(self.root, text=t).grid(row=2, column=1, sticky=W)
Is my problem the fact that I am re-instantiating a Label over the old one? Shouldn't the old Label's text be destroyed and the frame should only show the new label in its place?
It seems to me that you're not using objects at their best values. You should modify you code in this way:
each time you need a new roll, you instantiate a new Die. Why not keeping the same instance?
each time you want to display the roll, you instantiate a new Label. Maybe you're not aware of this, but you can update the label text (and any Tkinter widget), using its configure() method. This would mean that you need to grid the instance only the first time.
By the way, .grid returns None. If you want to keep reference of the result label, you have to use two lines for Label instantiation:
self.result = Label(self.root, text=t) # first creating instance...
self.result.grid(row=2, colum=1, sticky=W) # ... and placing it in self.root
Try to update your code like this. You will certainly feel the need to move some of this code to the __init__() function of self, so write it in your question as well.