Recreate left-to-right scrolling text in tkinter - python

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.

Related

Why am I getting brackets in between text when trying to print some text in the Tkinter canvas?

I am trying to print some text on the Tkinter canvas along with an image which is doing fine. But unfortunately, some curly braces are also being printed on the screen without using them anywhere in the print statement. I am fetching some part of the text from a dataframe and storing it in a variable before printing it on the screen.
My code is as follows:
best_batsmen = dataset.loc[dataset.loc[dataset['Innings']>=15,'Average'].idxmax(),'Names']
message = ("The best Batsman of the Tournament could possibly be: ",best_batsmen)
canvas_width = 500
canvas_height = 500
root = Toplevel()
root.geometry("700x600")
root.title("New Window")
canvas = Canvas(root, width=canvas_width, height=canvas_height)
canvas.create_text(1, 10, anchor=W, text=message)
img = ImageTk.PhotoImage(Image.open("virat.jpeg"))
canvas.create_image(0, 20, anchor=NW, image=img)
canvas.image = img
canvas.pack()
root.mainloop()
When I run the above code it's printing {The best Batsmen of the Tournament could possibly be:} {Virat Kohli} instead of The best Batsmen of the Tournament could possible be: Virat Kohli. Those curly braces are looking quite odd. Can anyone please help me to solve this error?
It's either in a set or a dictionary in your dataset. Just convert it to a string before displaying:
string = ''.join(str(l) for l in list(name))
This will do the trick for any amount of elements in a set.
This code sets message to a tuple:
message = ("The best Batsman of the Tournament could possibly be: ",best_batsmen)
This uses the tuple as the value of the text attribute without converting it to a string:
canvas.create_text(1, 10, anchor=W, text=message)
This value gets passed down to the underlying tcl interepreter as a list (from Tcl's perspective). When tcl converts a list to a string, which it must do before adding it to the canvas, it adds curly braces in order to retain it's list-like properties.
The solution is simple: don't pass lists or tuples to tkinter functions. Explicitly convert them to a string first:
message = " ".join(message)
canvas.create_text(1, 10, anchor=W, text=message)

Get individual listbox item properties in tkinter using itemcget?

I wrote the below code to bind event and do operations on individual listbox items.
import tkinter as tk
root = tk.Tk()
custom_list = tk.Listbox(root)
custom_list.grid(row=0, column=0, sticky="news")
def onselect_listitem(event):
w = event.widget
index = int(w.curselection()[0])
value = w.get(index)
print(index, value, " color : ",custom_list.itemcget(index,'background'))
custom_list.itemconfig(index, fg='gray', selectforeground="gray")
custom_list.bind('<Double-Button-1>', onselect_listitem)
for k in range(20):
custom_list.insert(k, " --------- " + str(k))
root.mainloop()
I am having trouble using itemcget to get the background properties while itemconfig works properly. Everything else is working. Can someone tell me if there is something wrong? I am trying to obtain the current item background color via index of the item in the listbox. The part with custom_list.itemcget doesn't print anything.
Thanks
From the New Mexico tech Tkinter reference:
.itemcget(index, option)
Retrieves one of the option values for a specific line in the listbox. For option values, see itemconfig below. If the given option has not been set for the given line, the returned value will be an empty string.
So since you haven't set the background option, itemcget returns an empty string. You can see this working by changing the print to custom_list.itemcget(index,'fg'). The first time you doubleclick you get an empty sting because you haven't set it, the second time it prints gray.

Check if Python (3.4) Tkinter Entry is Full

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()

Python function not working - Tkinter

My function isn't giving me the right output, and it doesn't want to work. I keep getting this error:
TypeError: list indices must be integers, not str
This is my code:
def showShop(level = level, cash = cash):
top = Tkinter.Tk()
shop = ["$100 & level 2 - Shotgun", "$250 & level 3 - 5 Grenades", "$500 & level 5 - Rocket Launcher"]
buttons = []
for i in shop:
temp = shop[i]
temp = Tkinter.Button(top, height=10, width=100, text = temp, command = shopping(i))
temp.pack()
buttons.append(temp)
top.mainloop()
I want it to display what is in the shop list based on what button it is...
Remove temp = shop[i] from the code
for i in shop:
temp = Tkinter.Button(top, height=10, width=100, text = temp, command = shopping(i))
temp.pack()
buttons.append(temp)
The for loop iterates over the elements in the list and not the indices!. The python docs make it more clear
The for statement in Python differs a bit from what you may be used to in C or Pascal. Rather than always iterating over an arithmetic progression of numbers (like in Pascal), or giving the user the ability to define both the iteration step and halting condition (as C), Python’s for statement iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence.
Also note that the command argument in the Button constructor takes a function as an argument. So you maybe better off by writing command = shopping there instead of the call command = shopping(i).
Change for i in shop to for i in xrange(shop).
You have to use something like partial to pass arguments to the function called by the button press. Note that you have declared the variable "temp" as 2 different things. The only reason it works is because the second declaration is after you use the first. Also note that the "buttons" list can not be used outside of the function showShop() because it is created in/local to that function. The following is working code based on what you posted. Also, please do not use "i", "l" or "O" as single digit variable names as they can look like numbers.
import Tkinter
from functools import partial
def shopping(btn_num):
print "button number %d pressed" % (btn_num)
buttons[btn_num]["bg"]="lightblue"
def showShop(buttons):
top = Tkinter.Tk()
shop = ["$100 & level 2 - Shotgun", "$250 & level 3 - 5 Grenades",
"$500 & level 5 - Rocket Launcher"]
##buttons = []
for ctr in range(len(shop)):
temp = Tkinter.Button(top, height=10, width=100, text = shop[ctr],
command = partial(shopping, ctr))
temp.pack()
buttons.append(temp)
top.mainloop()
## lists are mutable
buttons=[] ## not local to the function
showShop(buttons)

Need Help Binding, .get()-ing, appending in Tkinter GUI with Entry box

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

Categories

Resources