The thing is, I've expanded on this code more, and I am having another problem: The binding function is being ignored or something when I run this code (Of course I have my window setup above this as always):
from tkinter import *
#Window setup, ignore this mostly
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')
PointList = []
def AddCheck(event):
Point = e1.get()
PointTest = Point
if PointTest.find(',') is True:
PTest_X = PointTest[0].split(',')
PTest_Y = PointTest[1].split(',')
try:
PTest_X = float(PTest_X)
PTest_Y = float(PTest_Y)
PointList.append(Point)
l1 = Label(app, text='PointList').grid(row=1, column=0)
e1.delete(0, END)
except:
print('Error: Invalid point format.')
if PointTest.find(',') is False:
print('Error: Invalid point format.')
e1 = Entry(app)
e1.grid(row=0, column=0, sticky=W)
Entry.bind(e1, '<Return>', AddCheck)
mainloop()
Basically, my goal was to make a "checker" or whatever you might call it for the string entered into the Entry box. I am dealing with coordinates, so the first thing I wanted to do was to see if the string contained a comma. If it doesn't contain one, I automatically signal an error to the Terminal, and I will to the window later. The comma will split up the two main parts of the string I will be operating on in the ultimate function of this program. So, I split the string from the comma into the two parts. Then I wanted to see if the split parts of the string (PTest_X and PTest_Y) could be converted into floats with a Try statement, which obviously means only integers or floating point numbers that are before or after the comma will work under a conversion. The Except part of the Try statement just tells to return an error to the Terminal as well, like before. So, if PTest_X and PTest_Y can be converted, that's when I finally want to append them to a list, make a label of the list, and do whatever else.
The point of this "checker" system is to give feedback to the user telling them that their coordinate point "syntax", if you will, is incorrect. I would rather implement this system at this point to tell the user they have entered in a point incorrectly rather than telling them them the this after everything has already been processed.
I am doing this for user friendliness, which TRULY always comes first when it comes to user interaction. I hope to alleviate any pain from the user's experience with the program.
The binding function is working fine. The problem is that you are using find() & checking if its True. The find() returns the index if it does find the string else returns -1.
Also,your split doesn't work either as it is just getting the value of number before , at index 0,1.
from tkinter import *
#Window setup, ignore this mostly
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')
PointList = []
def AddCheck(event):
print ("hello")
Point = e1.get()
PointTest = Point
print (PointTest)
if ',' in PointTest:
PTest_X, PTest_Y =PointTest.split(',')
try:
PTest_X = float(PTest_X)
PTest_Y = float(PTest_Y)
PointList.append(Point)
l1 = Label(app, text=PointList)
l1.grid(row=1, column=0)
e1.delete(0, END)
except:
print('Error: Invalid point format.')
if PointTest.find(',') is False:
print('Error: Invalid point format.')
e1 = Entry(app)
e1.grid(row=0, column=0, sticky=W)
e1.bind('<Return>', AddCheck)
app.mainloop()
If its different labels you require then you would have to use loops
Related
I got another little question...
I want to make multiple variables which I create with 'setattr'
That works quite fine. It creates these variables:
self.sectionButton_1 = Button(text=x)
self.sectionButton_2 = Button(text=x)
self.sectionButton_3 = Button(text=x)
Now I want them to get displayed on the window with tkinter so that this should happen:
self.sectionButton_1.grid(row=i, column=0)
self.sectionButton_2.grid(row=i, column=0)
and so on..
But how do I have to edit the loop that the sectionButtons gonna be created with .grid from tkinter in a loop without writing the above ten times.
# Display Section selection
def checkSection(self):
# Read all sections from config
self.sections = config.sections()
self.sectionsCount = str(len(self.sections))
self.i = 0
self.text = Label(text="Choose Section:" + self.sectionsCount)
self.text.grid(row=1, column=0)
for x in self.sections:
i = +1
setattr(self, 'sectionButton_' + str(i), Button(text=x))
I'm not that good at explaining but hopefully its enough to understand my problem ^^
If not, just comment, I will try to answer it
If you have a group of related variables of the same type and you're doing the same operations to each one then that's a natural place to switch to using a list instead of individual variables.
Your code would become more like:
self.sectionButtons = []
for i, x in enumerate(self.sections):
button = Button(text=x)
button.grid(row=i+1, column=0)
self.sectionButtons.append(button)
This also has the advantage of no longer needing to construct the variable names as strings and use setattr, which is often a sign there's a better way.
I want to have a calculator with 4 buttons and 2 entry. What is my problem? I don't know how to fix that it can't multiply sequence by non-int of type str nor can't multiply entry * entry.
Also I don't know where is my output and how use that.
from tkinter import ttk
from tkinter import *
import tkinter as tk
#the environment
calc=tk.Tk()
calc.title("Calculator")
#-------------------------------------
#lables and their data entry
tk.Label(calc,text="enter your first number:").grid(row=0)
tk.Label(calc,text="enter your second number:").grid(row=1)
e1=tk.Entry(calc)
e2=tk.Entry(calc)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
x1=e1.get()
x2=e2.get()
tk.Label(calc,text="your result is:").grid(row=3)
# a free variable for get output,is this really need?
lbl=list()
#---------------------------
#the functions but my entry are entry type
def prod():
lbl=print(x1*x2)
def div():
lbl=print(x1/x2)
def sum():
lbl=print(x1+x2)
def min():
lbl=print(x1-x2)
#-------------------------------
#buttons and function in them as a command
btn1=tk.Button(calc,text="*",command=prod()).grid(row=0,column=2)
btn2=tk.Button(calc,text="/",command=div()).grid(row=1,column=2)
btn3=tk.Button(calc,text="+",command=sum()).grid(row=2,column=2)
btn4=tk.Button(calc,text="-",command=min()).grid(row=3,column=2)
#--------------------------------------------------------------------
#The answer which i need it
print("your answer is:")
calc.mainloop()
In GUI programming, you need to get the value of widgets at the time you need them, not at the time you create them.
Also, widgets return string values. If you are doing math on them, you need to convert them to numbers first.
If you want to display the result in the window, you can update a label widget with the configure method
For example, your prod definition could look something like this:
def prod():
x1 = int(e1.get())
x2 = int(e2.get())
result = x1 * x2
result_label.configure(text=str(result))
You then need to do two other things. First, you need to create the label for the result, and store the widget in a variable. To do that you must separate the creation of the widget from the layout since calling grid would cause the variable to be set to None
result_label = tk.Label(calc,text="your result is:")
result_label.grid(row=3)
Finally, your buttons need to be given a reference to the function. What you're currently doing is calling the function and assigning the result to the command attribute.
Your button should look like the following. Notice that prod does not have () after it:
btn1 = tk.Button(calc, text="*", command=prod)
...
btn1.grid(row=0,column=2)
On a side note, I think that for all widgets with a common parent, grouping all widget creation code together and then all calls to grid or pack together makes the code easier to read, easier to maintain, and easier to visualize.
For example:
btn1 = tk.Button(...)
btn2 = tk.Button(...)
btn3 = tk.Button(...)
btn4 = tk.Button(...)
btn1.grid(row=0,column=2)
btn2.grid(row=1,column=2)
btn3.grid(row=2,column=2)
btn4.grid(row=3,column=2)
I want to create a fast typing effect through packing a label in tkinter. When I run this code, it prints left to right as I want it to, but the letters are spaced far apart and the spaces print {} brackets instead.
How can I remove the brackets and just show a space? Is there also a cleaner and easier way to do the scrolling effect other than the list method I used?
root = Tk()
delay = 50
label_var = StringVar()
label = Label(root, textvariable=label_var, height=10)
num = 0
def scroll():
global num
roll_text = list(message) # Edit: deleted this line
num = num + 1
label_var.set(roll_text[1:num]) # Edit: changed roll_text to message
root.after(delay, scroll)
message = ' This message should be scrolling left to right. '
scroll()
label.pack()
root.mainloop()
The brackets are appearing because you're converting the string to a list. When tkinter is given a list where it expects a string it uses Tcl's rules for converting the list back to a string. Those rules include using curly braces to preserve the original data.
The solution is simple: don't pass a list to label_var.set.
As for the alignment, because you don't provide any alignment options, tkinter will try to center the widget. A simple solution for this specific case is to pass side='left' to the pack command.
I am using a standard tkinter entry to input a directory path. When the user presses enter - if the physical length of the string exceeds that of the entry, I want the program to modify the displayed text to ...[end of directory]. I have the logistics of this figured out but as of yet I have no accurate way to test whether the entry box is full, or how full
Things I have tried:
Using 'PIL.ImageFont.truetype("font.otp",fontsize).size' - cannot calculate the point at which to cut the directory
Simply using the length of the string against the length of the entry- inaccurate as the font I am using (which I don't want to change if possible) varies in length with each character
Another compromise behaviour which I tried was to make the entry "look" at the start of the path. I tried inserting at, selecting at and moving the cursor to position 0 but none of these worked
You can use xview method of Entry. xview return the visible part of the text displayed in the entry. You can use it to interactively create a text that fit the entry.
Here is a quick and dirty proof of concept
from tkinter import *
root = Tk()
v = StringVar(root)
e = Entry(root,textvariable=v)
e.pack(fill=BOTH)
v.set('abcdefghijklmnopqrstuvwxyz0123456789')
new_s = None
def check_length():
global new_s
original_s = v.get()
def shorten():
global new_s
e.xview(0)
if e.xview()[1] != 1.0:
new_s = new_s[:-4] + '...'
v.set(new_s)
print("new_s: " + new_s)
e.xview(0)
e.after(0,shorten)
print(e.xview()[1])
if e.xview() != (0.0,1.0):
new_s = original_s + '...'
shorten()
b = Button(root,text="hop",command=check_length)
b.pack()
e.mainloop()
I'm trying to create a GUI using tkinter. This is my code:
from tkinter import *
from random import randint
B3Questions = ["How is a cactus adapted to a desert environment?", "What factors could cause a species to become extinct?"]
B3Answers = ["It has leaves reduced to spines to cut water loss, a thick outer layer to cut down water loss and a deep-wide spreading root system to obtain as much water as possible", "Increased competition, new predators and new diseases"]
B3Possibles = [x for x in range (len(B3Questions))]
def loadGUI():
root = Tk() #Blank Window
questNum = generateAndCheck()
questionToPrint = StringVar()
answer = StringVar()
def showQuestion():
questionToPrint.set(B3Questions[questNum])
def showAnswer():
answer.set(B3Answers[questNum])
def reloadGUI():
global questNum
questNum = generateAndCheck()
return questNum
question = Label(root, textvariable = questionToPrint)
question.pack()
answerLabel = Label(root, textvariable = answer, wraplength = 400)
answerLabel.pack()
bottomFrame = Frame(root)
bottomFrame.pack()
revealAnswer = Button(bottomFrame, text="Reveal Answer", command=showAnswer)
revealAnswer.pack(side=LEFT)
nextQuestion = Button(bottomFrame, text="Next Question", command=reloadGUI)
nextQuestion.pack(side=LEFT)
showQuestion()
root.mainloop()
def generateAndCheck():
questNum = randint(0, 1)
print(questNum)
if questNum not in B3Possibles:
generateAndCheck()
else:
B3Possibles.remove(questNum)
return questNum
Basically, when pressing "Next Question", the question label does not update. Pressing "Next Question" again will throw the code into a nice loop of errors.
I honestly cannot see where I'm going wrong, but that's likely due to my lack of experience
RobertR answered your first question. The reason why you receive an error when pressing the Next Question button again is because your list, B3Possibilities has two numbers, 0 and 1. So when you run the function twice, you'll remove both one and zero from this list. Then you have an empty list. When you call reloadGUI a third time you'll never hit your else statement because the randint generated will NEVER be in B3Possibilites. Your if clause is called and you dive into an un-ending recursive call.
A solution to this might be to have a check in your generageAndCheck function:
if(len(B3Possibiles) == 0):
#run some code. Maybe restart the program?
First off, the short answer is that you're not actually updating the content of the StringVar questionToPrint. I would fix this by changing the reloadGUI() function to this:
def reloadGUI():
global questNum
questNum = generateAndCheck()
showQuestion()
answer.set("") # Clear the answer for the new question
Also, as Dzhao pointed out, the reason you're getting an error after you run out of questions is because you need to put some sort of protection within your generateAndCheck() function to prevent against infinite recursion.
Additionally, I would recommend you change the way you determine what question to ask because the way you have it right now is needlessly complicated. Look in to the random module a little more, particularly the random.choice() function. You'll notice it raises an IndexError when the list is empty, so you can catch that error and that will help solve the problem that Dzhao had pointed out.