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.
Related
Hi i'm a beginner in python and I really got int trouble with some methods, I wanna give some number from Entry of tkinter class and show them with a chart,
but the thing is that I cant get int number:
so the chart wont work [here is the picture of my code , I get some bumber from entry but i cant make them integer number]
1: https://i.stack.imgur.com/2Vuvn.jpg
2: https://i.stack.imgur.com/Pa23V.jpg
Welcome. I'm posting a complete, I think, answer to this question but there are a couple of etiquette things you should know:
Please don't post screenshots of your code. Copy and paste into the editor.
Please post just enough code to show your problem, but which is complete enough that we can just copy it into our own editors / IDEs and run without a lot of modification.
The previous commenters are correct that this question has probably been answered a hundred times, so please try to search through previous answers before posting your question.
Having said that, I have not answered this question before, so here's my rendition. I know you're a beginner so I've tried to keep it as simple as possible, but you're also tackling TKinter so I've not made it overly simplistic.
import tkinter as tk
def main():
global entryVar, lableVar
#create a tkinter window:
rootWin = tk.Tk() #creates a root window
rootWin.title('Entry Test') #shows text on the title bar
rootWin.geometry('500x200') #sets the displayable size of the window
#we'll need these variables and they MUST be tk.StringVar()
entryVar = tk.StringVar() #variable to hold the entry value
lableVar = tk.StringVar() #variable to hold the lable value
#create an entry widget:
entry = tk.Entry(
rootWin,
width = 5,
textvariable = entryVar
)
entry.pack(expand=1)
entry.bind('<Return>', getEntryValue) #bind enter key to widget
entry.bind('<KP_Enter>', getEntryValue, add='+') #bind the other enter key to widget
#create a lable widget
lable = tk.Label(
rootWin,
textvariable = lableVar
)
lable.pack(expand=1)
lableVar.set("This is where the lable is.")
entry.focus_set() #set focus on the entry widget for convenience
rootWin.mainloop()
def getEntryValue(event):
global entryVar, lableVar
x = entryVar.get() #get the value from Entry
x = int(x) #change it to an int
lableVar.set(x) #set the lable variable
entryVar.set("") #clear the entry variable
if __name__ == "__main__":
main()
So, what's going on here is that we make a window in the usual way. I've created both an Entry() widget to get some input, and a Label() widget to show whatever has been input. I've broken the Entry() and Label() declarations up over multiple lines just to make them easier to read.
You can attach variables to many TKinter widgets to that you can .get() and .set() their values more easily, but they almost always need to be TKinter variable types such as StringVar() or IntVar(). I've created two such variables, one for the Entry() widget and another for the Label() widget.
I've also added "bindings" to the Entry() widget to both show how that works and to make data entry a bit more convenient. I don't know if you have a separate number pad on your computer keyboard so I've bound both the main <enter> key as well as the number pad's <enter> key. When you hit either one of those keys, the Entry() widget will call the getEntryValue() function which does the work of getting the value and displaying it on the window.
For convenience, entry.focus_set() immediately puts the focus on the Entry() widget, then the TKinter window enters the .mainloop() to do its stuff.
The getEntryValue() function is called by the events which we set on the Entry() widget. I broke it down into more lines than necessary to illustrate what needs to happen. First we retrieve the value of the Entry() widget through its variable, entryVar. You do that using entryVar's .get() method: x = entryVar.get(). That returns a string value which you will have to convert to an integer using the normal int() function available in Python. For this purposes of this demonstration I've chosen to display that value to a Label() widget which I've placed in the window, so I use the Label() widget's variable lableVar: lableVar.set(x). You don't have to convert the integer back into a string before doing this.
I then clear out the entryVar variable so that there isn't anything left in the Entry() widget to get in the way of our next entry.
I've used entryVar and lableVar as globals just to simplify the example.
And that's how you do it.
I guess the problem is here:
a=str(e3.get())
Try something like this:
a=int(e3.get())
Since what you want is an integer
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()
I just wrote a class in Tkinter, that allows me to make some widgets draggable. This really works great as long as the master of the widget is the root window. But if I, for example, have a Frame with a Lable in it, I can drag the Frame but the Lable just disappears. This is cause the class places the label in relation to the main window. So for example, if the Frame has the size 100x100 and is at the position 500, 500 at the main window, and I drag the Label(0, 0) 1px to the right, it will be placed at 501, 500 instead of 1, 0 cause the class thinks the master is the root window.
So now I thought to just use the master's position to subtract it from the Label position (501-500, 500-500 > 1, 0) There's just one problem. This:
f = Frame(root, width=100, height=100, bg='grey')
f.place(x=500, y=500)
l = Label(f, text='Drag me!)
l.place(x=0, y=0)
master = l.master
print(master)
returns me not one master but two. Even if there's just one print statement, it gives me this:
>>.
>>.!frame
If I put a sleep statement in between the declaration and the print it just takes longer. But if I check the types it's not a list, this are two objects.
Can anybody explain this? I just need a way to get the master of the Label to get it's position!
Tkinter widgets exist in a tree structure, and there must be an instance of Tk at the root. If you don't create one, it will be created for you the first time you create a widget.
In your output, "." represents the root window that was presumably created for you, and ".!frame" represents the frame.
Though, given the fact that you explicitly pass root to Frame(), it looks like your code is explicitly creating the root window somewhere.
I found the problem. It's not a problem with the code. I just didn't noticed I also müde the Frame dragable. And cause the Frames parent is root I got it.
I have created around 5 Entry boxes and binded them as well. Take this as model:
def makeEntry(self, master, data):
self.entry = Entry(master, width=8, textvariable=data)
self.entry.bind("<Leave>", lambda event, value=data: self.validate(event, value))
Now, I did also a validate method that check if the input was a string (and if so, the highlight background of the entry would change to red). The problem which is still taking me a lot of time is that I would need that the method should be able to check every entries, and if at least one of them has got a red background, then a final button should be disabled (button.configure(state=DISABLED)).
With just one entry it would be much easier, I would simply check if the background was red (status = str(self.myOneEntry.cget("highlightbackground"))), but what about with more entries?
If you want to check all of your entries, keep them in a list. Then, write a function that iterates over the list and sets the button state to disabled if any widget has a red background. You can then call this whenever something changes, such as within your validation function for each widget.
Example:
class Example(...):
def __init__(...):
self.entries = []
for data in ("one","two","three"):
entry = makeEntry(...)
self.entries.append(entry)
def _update_button(self):
for entry in self.entries:
if entry.cget("background") == "red":
self.button.configure(state="disabled")
return
self.button.configure(state="normal")
Note: you'll need to make sure that makeEntry(...) returns a reference to the created widget.
Also, you don't have to use makeEntry. You can create your widgets however you want. The point is to save references to the widgets in a data structure that you can iterate over. It doesn't matter how the widgets are created.
I'm trying to write the simplest tkinter window that will show an entry box and a button and the square of whatever number is entered in the entry box.
The code I've written sort of works, except that each new answer is over-written on the previous one so if you have a long number followed by a small number you can see the remains of the old answer underneath the new one.
How could I fix this? Is there a way to cancel the previous label?
from tkinter import *
x=Tk()
x.geometry('900x400')
e=Entry(x)
e.place(relx=0.4, rely=0.2)
def z():
aa=e.get()
aa=int(aa)
aa=aa**2
l=Label(x, text=aa).place(relx=0.4, rely=0.4)
e.delete(0, END)
b=Button(x, text='PRESS', command=z).place(relx=0.6, rely=0.2)
x.mainloop()
Do not create mpnew labels. Create the label once, then use the configure method to change the text.
global l
l = Label(...)
l.pack(...)
...
l.configure(text="some string")
As a suggestion: when learning tkinter, ignore place. It is much less useful than pack and grid.
note: you must create the label and then call pack/place/grid on separate lines because pack/place/grid always returns None.