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()
Related
I've posted about this before and I've been able to whittle the program down to a single function for testing purposes. I'm not getting any errors, but I am getting a bug that is driving me up the wall. I'm able to replace the shortcut, but it only replaces the first letter of the shortcut.
Here's the code:
from tkinter import *
root = Tk()
text = Text(root)
text.pack(expand=1, fill=BOTH)
syntax = "shortcut = sc" # This will be turned into a function to return the shortcut
# and the word, I'm only doing this for debugging purposes.
def replace_shortcut(event=None):
tokens = syntax.split()
word = tokens[:1]
shortcut = tokens[2:3]
index = '1.0'
while 1:
index = text.search(shortcut, index, stopindex="end")
if not index: break
last_idx = '%s + %dc' % (index, len(shortcut))
text.delete(index, last_idx)
text.insert(index, word)
last_idx = '%s + %dc' % (index, len(word))
text.bind('<space>', replace_shortcut)
text.mainloop()
The shortcut given, in our case 'sc' will turn into 'shortcutc' after the space is typed. Any help is appreciated!
You have two problems.
You have the variable shortcut defined to be ['sc'] instead of 'sc'. So len(shortcut) will always be 1 (the length of the array) rather than 2 (the length of the string). You'll always end up deleting just one character. Probably you want len(shortcut[0])
[You also have the same problem with len(word). You'll always get 1, the length of the array.]
Also, the last line of your while loop should set index rather than last_idx, since that's the variable that's going to be used in the next search.
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.
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
I am writing a Tkinter program for the first time and have a question on radio buttons. What I am trying to do is this:
open a set of images (one at a time).
When an image is opened, annotate a value using the radio button.
Collect this value in a list
So, in this example I have 2 compounds and the list would have 2 annotations.
The problem I have is, if by mistake the user clicks radiobutton 2 instead of one, and then corrects him/herself, the list will have 4 items (3 for the first image, 1 for the second). How do I handle this, so that the list will have only 2 values?
import Tkinter as tk
from PIL import ImageTk, Image
from tkFileDialog import askopenfilename
cmp_list = ["VU435DR","VU684DR"]
li = []
li_final = []
def sel():
selection = str(var.get())
if selection == "1":
li.append("Antagonist")
elif selection == "2":
li.append("Agonist")
for i in range(len(cmp_list)):
root = tk.Tk()
var = tk.IntVar()
ig = str(cmp_list[i] + '.png')
img = ImageTk.PhotoImage(Image.open(ig))
panel = tk.Label(root,image=img)
panel.pack(side = "top",fill="none",expand="no")
#w = tk.Text(height=2,width=50)
#w.pack(side='right")
q = tk.Radiobutton(root,text="Antagonist",command=sel,value=1,variable=var)
q.pack()
r = tk.Radiobutton(root,text="Agonist",command=sel,value=2,variable=var)
r.pack()
root.mainloop()
print li
Your code is creating more than one instance of tk.Tk(). This is not how Tkinter was designed to work, and it will yield unpredictable behavior. A proper Tkinter program always has exactly one instance of tk.Tk().
If you need more than one window, for the second and subsequent windows you should create an instance of tk.Toplevel.
To answer your specific question about how to handle someone first hitting one radiobutton and then the other -- the problem is that you are unconditionally appending to your list each time they click on a radiobutton. The solution is to use some sort of flag or indicator to know whether one of the radiobuttons has been clicked, or change your code so that it doesn't matter.
Let's look at that second option - make it so it doesn't matter. When you open up a new image you can automatically append a value to your list. In this case, set it to None to say that nothing has been picked yet. Then, in sel, you would always replace the last element rather than append a new element, since you know that the last element always refers to the current compound.
I'm trying to build a search engine that will check a list and then remove all list items that do not meet the search parameters. I know there is several problems with my program such as it will not add things back to the list when you backspace and in my updating for loop I simply tack on a '*' thinking that it will search for strings only beginning with the current parameters, but I will cross those bridges later.
class StudentFinderWindow(Tkinter.Toplevel):
def __init__(self):
Tkinter.Toplevel.__init__(self) # Create Window
searchResultList = ['student1', 'student2', 'student3'] # Test list.
##### window attributes
self.title('Edit Students') #sets window title.
##### Puts stuff into the window.
# text
editStudentInfoLabel = Tkinter.Label(self,text='Select the student from the list below or search for one in the search box provided')
editStudentInfoLabel.grid(row=0, column=0)
# Entry box
self.searchRepositoryEntry = Tkinter.Entry(self)
self.searchRepositoryEntry.grid(row=1, column=0)
# List box
self.searchResults = Tkinter.Listbox(self)
self.searchResults.grid(row=2, column=0)
This fills the Tkinter Listbox with the original list.
# Search results initial updater.
self.getStudentList()
for student in self.studentList:
self.searchResults.insert(Tkinter.END, student)
##### Event handler
Right here I bind to run the list updater after a key is entered into the search box
self.searchRepositoryEntry.bind('<Key>', self.updateSearch)
This is supposed to run every time a key is pressed. It gets the string that is in the Entry then starts a variable count so I know which index the name is at. After that it run a for loop on the current list supposedly checking to see if it fits the requirement of the parameters and any other letter after it. If it does not match it should delete. The problem is the first time I hit a letter the parameters string is just a blank space and then the next letter the string is the first letter and so on. It is always one step behind. And that is the problem
def updateSearch(self, event):
parameters = self.searchRepositoryEntry.get()
int = 0
currentList = self.searchResults.get(0, Tkinter.END)
for i in currentList:
if not i == parameters + '*':
self.searchResults.delete(int)
int += 1
def getStudentList(self):
global fileDirectory # Gets the directory that all the files are in.
fileList = listdir(fileDirectory)
self.studentList = []
for file in fileList:
self.studentList.append(file[:-4])
I believe I have run into this same problem you describe before, when attempting to make an actively searching ctrl-F feature in one of my programs.
What I found to work is not bind on Key but instead KeyRelease. I'm not entirely sure why this works (probably just a quirk with Tkinter). However, it works.
Snippet's:
The binding
# self.FW.En is an entry widget.
self.FW.En.bind('<KeyRelease>', self.find)
Which would run
def find (self, event):
self.Te.tag_remove('found', '1.0', 'end')
pat = self.FW.En.get()
if len(pat) > 1:
index = '1.0'
while True:
index = self.Te.search(pat, index, nocase=1, stopindex='end')
if not index:
break
lastidex = '%s+%dc' % (index, len(pat))
self.Te.tag_add('found', index, lastidex)
index = lastidex
self.Te.tag_config('found', background='#80ff00')