I am working on a wordle game and have entries that will take in a single character before moving on to the next entry. As of now, the game is working, but it does not allow for backspace if a mistake was made. I am trying to incorporate that.
def pause(event, i):
p = StringVar()
window.bind(event, lambda e : p.set(inner[i].get()))
window.wait_variable(p)
i = 0
while i < 30:
inner[i].focus_set()
pause("<KeyRelease>", i)
if inner[i].get() == "":
i -= 1
inner[i].delete(0, END)
word = word[:-1]
continue
word += inner[i].get()
inner is a list of all the entries. I wasn't sure if there was a better way to get the exact keyboard input (I tried to download and import keyboard, but that was not working), so I was able to determine that if inner[i].get() == "" that means that the input was a backspace. I wanted to increment i by negative one if this was the case so that the entry in focus would be the previous entry. I would then want to delete what was in that entry and insert a new character by continuing through the loop. However, the entry is not being deleted at all.
Related
I need to get only the latest input from my text widget, and then append that character to a list.
I am using
Text.get(1.0,'end-1c')
, and it does not work because the loop constantly gets all the input, instead of only getting the latest input when there is a new latest input.
def main_screen():
start_time=time.time()
tk=Tk()
tk.title('Typing Test')
tk.geometry('800x500')
main_title=Label(tk,text='1 Minute Test',font=('Times New Roman',36))
main_title.pack(pady=5)
directions=Label(tk,text='Start Typing',font=('Times New Roman',14))
directions.pack()
base_text=Label(tk,text=randomizer(),bg='#E0E0EE',font=('Arial',14),wraplength=700,justify=LEFT)
base_text.pack(pady=10)
text_area=Text(tk,font=('Arial',14),width=63,height=7,wrap='word')
text_area.pack()
tk.update()
#WPM Calculation
target_text=randomizer()
typed_text=[]
wpm=0
errors=0
while True:
tk.update()
time_elapsed=max(time.time()-start_time,1)
wpm=round((len(typed_text)/60)/5)
if time_elapsed>=60:
break
#Problem Section
key=text_area.get(1.0,'end-1c')
typed_text.append(key)
for x in typed_text:
if x != target_text:
errors += 1
Alternatively, I tried using a variable in place of the 1.0 in .get, that would increase by one with each iteration of the loop. Next, I tried a try/except command, and put the #Problem Section into a function. I tried calling that function by binding the text area to
'<Key>'
'<KeyPress>'
'<KeyRelease>'
None of these attempts work. I used a print statement to see what those variables are with each iteration of the loop, and using the first method, it just keeps making a longer and longer string that repeats constantly, instead of updating with each new character. Trying the other ways I just got nothing, no output, but no error either. I am completely stuck, and don't know what else to try.
You can bind the text_area with a <KeyPress> event, but you need to pass the list typed_text as an argument so you can append the presses.
So you should do something like this:
text_area.bind("<KeyPress>", lambda _: getKey(_, typed_text))
while True:
tk.update()
time_elapsed = max(time.time() - start_time, 1)
wpm = round((len(typed_text) / 60) / 5)
if time_elapsed >= 60:
break
# Problem Section
for x in typed_text:
if x != target_text:
errors += 1
def getKey(event, list):
list.append(event.char)
print(list)
The text widget supports something called a "mark", which is like a bookmark. You can put a mark anywhere in the text and use it just like a normal index.
Assuming that data is only ever appended to the end of the widget, the simplest solution is to fetch a block of data and then move the mark to the end of the text that you fetched. The next time you fetch data, start at that mark.
Marks have something called "gravity" that defines which character the mark sticks to. For example, if the gravity is "left" and you set it to character "2.2", the mark will always stay adjacent to the character at index "2.2". If the gravity is "right", it will be stuck at the character following index "2.2" (eg: "2.3" or "3.0")
Here's a contrived example that will print only the latest additions to a text widget every five seconds, by tracking the last position that was used to fetch the data.
import tkinter as tk
def get_new_text():
data = text.get("last", "end-1c")
print(f"new data: >>>{data}<<<")
text.mark_set("last", "end-1c")
root.after(5000, get_new_text)
root = tk.Tk()
text = tk.Text(root, wrap="word")
text.pack(fill="both", expand=True)
text.mark_set("last", "1.0")
text.mark_gravity("last", "left")
root.after(5000, get_new_text)
root.mainloop()
I am new to python and new to programming so please forgive my ignorance.
I am using python-docx to automatically format a document as I need it. In our database application we have a good number of forms that are updated periodically in batches. They all follow pretty much the same format and we are given the newly updated document not formatted for our needs.
So I have a couple questions for what I am trying to do:
1) In each document, there is a number such as 5.1 at the beginning of the document. After the number I need to place a tab and then underline the remainder of the paragraph. I cannot figure out, and maybe it is not possible with the way I am looking at it, but I cannot put a tab in a certain spot or figure out how to underline the remaining of the paragraph because there is only a single run only and I cannot find any way to split a single run into two runs. What i have been able to do is to open the document and use pyautogui to move the number of spaces over to the right using pyautogui.press('right') in a loop after counting how many numbers there are in the 1st paragraph. But that is not preferred i think.
I thought that maybe i could insert the text into a string, then split the number from the rest of the words, and then use python-docx to remove the old text and then insert the new text with the different formatting(runs). Is that the best way to do this or are there better ways?
This is currently how I am performing this task but it does not allow me to bold. I would like to perform the whole task using python-docx so that i am not as dependent using the gui to make the changes
def JITitleNumberLength():
doc = docx.Document('1ji.docx')
p0 = doc.paragraphs[0]
p0Size = len(p0.text) #finds length of title in paragraph 0
JI_Title = p0.text
JI_Title_List = list(JI_Title)
#print(JI_Title_List[2])
JI_Index_Length = 0 #Returns the amount of numbers in the title of the Jury Instruction
counter = 0
while (counter < p0Size) and True:
#print(JI_Title_List[counter], ' ', JI_Index_Length)
if (JI_Title_List[counter] == '1' or
JI_Title_List[counter] == '2' or
JI_Title_List[counter] == '3' or
JI_Title_List[counter] == '4' or
JI_Title_List[counter] == '5' or
JI_Title_List[counter] == '6' or
JI_Title_List[counter] == '7' or
JI_Title_List[counter] == '8' or
JI_Title_List[counter] == '9' or
JI_Title_List[counter] == '0' or
JI_Title_List[counter] == '.'):
#print('If Statement True')
JI_Index_Length = JI_Index_Length + 1
else:
#print('False')
False
counter = counter + 1
return JI_Index_Length
def OpenDocumentForAutoGUI():
os.system("start " + '1ji.docx')
time.sleep(1) #causes delay to allow document to full open before next command runs
def main():
TitleNumberLength = int(JITitleNumberLength())
for i in range(TitleNumberLength):
pyautogui.press('right')
pyautogui.press(['delete', 'tab']) #removes space and inserts tab between number and instruction name
2) In the middle of a paragraph, there will be different options given in a format of [option 1] [option 2] [option 3]. I would like to create a content control that would give a drop down option of these three options. No where that i have read has there been something to content controls with docx. Is there any way to do this or just manually doing this with pyautogui the only option i have? Basically my thought is that i would search the paragraphs for the brackets [] and then input them into a content control somehow, and if need be, use pyautogui which i prefer to stay away from if possible.
I havent even begun the code for this part yet, my only thought would be to put each option into a list and then recall from the list after using pyautogui to manually move the mouse to click on the developer tab in word and then select the content control as there is no keyboard shortcut to bring in a content control. I would really prefer not to do this because then the screen resolution plays a big part and only specific screen resolutions would work.
Sorry - I am pretty sure that run-level formatting is the most granular that you can get. It should be trivial to add the code to create a second Run in the paragraph and apply an underline style to it.
No idea on drop-down list boxes
Two stylistic tips:
You can use 'in' and the constant string.digits with a concatenation operator to simplify your very long if statement
if JI_Title_List[counter] in (string.digits+'.') ....
You can use += to say x = x +; e.g. x+=1 is x = x + 1
counter += 1
JI_Index_Length +=1
I'm trying to create a typewriter effect for text being blitted. By typewriter effect, I simply mean that Im trying to avoid the entirety of the text being blitted on screen at once. Instead, im trying to have each letter appear individually, with a slight delay before the next character in the string appears.
The catch is that im not using pygame's font.render. Instead, i've made my own custom fonts, each letter being saved as a separate image file. Now each alphanumeric character has it's own variable to which it's image is attached and each is appended to a list.
e.g:
letter_IMGs = []
a = "a" == pygame.image.load("IMG/letter_a.gif)
letter_IMG.append(a)
Lower, I have something along these lines:
letter_pos_x = 0
text = "Hello"
for i, c in enumerate(text):
screen.blit(letter_IMGs[i], (letter_pos_x,0))
letter_pos_x += 20
scroll_wait #this is a clock.delay variable. It's value was set outside the loop. I'm just calling it here.
Now as you'd guess, the result with that code is that the entire line of text appears simultaneously after the delay. I've been trying to code it as needed from there, but most of what I come up with returns with a "cannot iterate through surface objects" error.
I'm pretty much at a loss on how I should proceed next. Note that, ive been learning a bit of code on my own, on and off, for the past year and that I don't really know what im doing yet. Any and all help will be much appreciated.
Thanks in advance for your time.
Without getting into the pygame specifices too much, you just need to change the iterator so it returns substrings rather than letters:
def iterate_text(text):
for r in range(len(text)):
yield text[:r + 1]
which will return the substring iteratively:
for t in iterate_text('hello'):
print t
# h
# he
# hel
# hell
# hello
use a separate function to draw the string:
def draw_text(x, y, text):
characters = [letter_IMGs[t] for t in text]
cursor = x
for char in characters:
screen.blit(char, cursor, y)
cursor += 20
in your main loop you can decide when to get the next character. You'll basically do something like:
typewriter = iter_text('hello world')
text_to_draw = None
advance_text = False
at a level outside the loop that survive from frame to frame. When you want to draw the next character, you set advance_text to True, in and in the main loop:
if typewriter and advance_text:
text_to_draw = typewriter.next()
advance_text = False # until you set it again
if text_to_draw :
draw_text(0,0, draw_text)
You can start over by resetting the typewriter with new text, and control the timing of the new character appearing by setting advance_text to True before the next frame
Question
How can I insert text into a Tkinter Textbox? I am trying to create a word processor which inserts lists at the current position.
What I have tried so far
I have tried to use the CURRENT argument, but this is unreliable.
def listcmd(self): #THIS HAS BUGS!!! FIX IT SOON
number = self.listentry.get()
number = int(number)
listINT = 1
for x in xrange(number):
self.write.insert(CURRENT, "%s:" % (str(listINT) )) #This is used for the number
self.write.insert(CURRENT, "\n") #This inserts the newline
listINT += 1
You need to use the index "insert" or Tkinter.INSERT. That always refers to the insertion cursor.
Is there an easier way to change the order of items in a tkinter listbox than deleting the values for specific key, then re-entering new info?
For example, I want to be able to re-arrange items in a listbox. If I want to swap the position of two, this is what I've done. It works, but I just want to see if there's a quicker way to do this.
def moveup(self,selection):
value1 = int(selection[0]) - 1 #value to be moved down one position
value2 = selection #value to be moved up one position
nameAbove = self.fileListSorted.get(value1) #name to be moved down
nameBelow = self.fileListSorted.get(value2) #name to be moved up
self.fileListSorted.delete(value1,value1)
self.fileListSorted.insert(value1,nameBelow)
self.fileListSorted.delete(value2,value2)
self.fileListSorted.insert(value2,nameAbove)
Is there an easier way to change the order of items in a tkinter listbox than deleting the values for specific key, then re-entering new info?
No. Deleting and re-inserting is the only way. If you just want to move a single item up by one you can do it with only one delete and insert, though.
def move_up(self, pos):
""" Moves the item at position pos up by one """
if pos == 0:
return
text = self.fileListSorted.get(pos)
self.fileListSorted.delete(pos)
self.fileListSorted.insert(pos-1, text)
To expand on Tim's answer, it is possible to do this for multiple items as well if you use the currentselection() function of the tkinter.listbox.
l = self.lstListBox
posList = l.curselection()
# exit if the list is empty
if not posList:
return
for pos in posList:
# skip if item is at the top
if pos == 0:
continue
text = l.get(pos)
l.delete(pos)
l.insert(pos-1, text)
This would move all selected items up 1 position. It could also be easily adapted to move the items down. You would have to check if the item was at the end of the list instead of the top, and then add 1 to the index instead of subtract. You would also want to reverse the list for the loop so that the changing indexes wouldn't mess up future moves in the set.