I'm trying to program a textbox that I can write in using the keyboard; but the backspace key doesn't erase text, nothing happens when I press it.
extends Node2D
onready var Text = $Panel/RichTextLabel
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
func _unhandled_input(event):
if event is InputEventKey:
if event.pressed == true:
match event.scancode:
KEY_A:
Text.append_bbcode("a")
KEY_BACKSPACE:
if Text.get_total_character_count() > 0:
print("0")
Text.visible_characters -= 1
pass
Godot already has a built-in TextEdit node that does exactly what you are trying to do.
Related
I am trying to write an interactive command line program using input keys to print specific text to the terminal, and it has the following structure:
from pynput.keyboard import Listener, Key
def choix_case(tour='X'):
variable = 1
def goto_case(a):
print ('\033[u', end='') #Escape code to reset the cursor position
print ('\033[', a*2, 'B', sep='', end='') #Escape code to move [a] times to the right
def print_choice():
#Some escape codes to change the style of the next printed text:
blinking('on')
bg_color()
fg_color(0xd7,0x5f,0x5f)
print('X', end='') #The actual printed text
#Escape code to reset the style:
blinking('off')
fg_color()
bg_color()
goto_case(variable)
print_choice()
def on_press(key):
nonlocal variable
if key == Key.right:
variable+=1
if key == Key.enter:
return False
goto_case(variable)
print_choice()
with Listener(on_press = on_press) as clef:
clef.join()
It is supposed to execute the goto_case and print_choice functions each time a key is pressed, and then close the Listener when Enter key is pressed.
Basically, it prints an 'X', and adds another 'X' on the next position each time I press the Right key, but it prints nothing, and then prints them all at once (for example 'X X X' for 3 times) after I press enter.
The problem is that nothing is printed until the Enter key is pressed, and then everything is printed at once. It's as if the Listener saved all the times on_press() ran, and then released them once it closed.
And what is even weirder is that if I printed a '\n' at any point inside the on_press() function ( or inside goto_case or print_choice which are called inside on_press, or I remove the end='' option from any print), it prints what happens each time I press a key. So the problem seems to be in the fact that there is no newline char printed? Why would that block the printing while the Listener is still running?
I want to bind a widget to a single function so when its clicked and the "r" key is being pressed at the same time it gets called.
i have tryed <Button-1-R> and got the error "_tkinter.TclError: extra characters after detail in binding"
widged.bind("<Button-1>",function)
If you want to bind combinations, you can do so with a single bind by concatenating the events. You can optionally add whitespace between each event.
For example, to bind a click followed by pressing the "r" key you can do it like so:
widget.bind("<ButtonPress-1> <r>", function)
If you want the reverse -- the letter "r" followed by a click, just reverse them. However, you may have difficulties depending on your system since some systems have an autorepeat for keys.
widget.bind("<r><ButtonPress-1>", function).
It's important to know that tkinter processes events literally. For example, if you click the button and then press "r", the binding will fire. If you press "r" again the binding won't fire since it isn't immediately preceeded by a click.
It's unclear exactly what you're trying to accomplish, but the other solution is to set a flag in the handler for one event (either the click or the key), and check for the flag in the other.
For example:
def set_flag(value):
global flag
flag = True
def function(event):
if flag:
... process the event here ...
widget.bind("<ButtonPress-1>", lambda event: set_flag(True))
widget.bind("<ButtonRelease-1>", lambda event: set_flag(False))
widget.bind("<r>", function)
You can capture 3 events: <Button-1>, <r> and <ButtonRelease-1>. Fire your function only when both button 1 and r is triggered.
import tkinter as tk
root = tk.Tk()
entry = tk.Entry(root)
entry.insert(0,"Left click and press R")
entry.pack()
class Bindings:
def __init__(self):
self.but_1 = False
entry.bind("<Button-1>", self.mouse_clicked)
entry.bind("<r>", self.r_clicked)
entry.bind("<ButtonRelease-1>", self.mouse_release)
def mouse_release(self,event):
self.but_1 = False
def mouse_clicked(self,event):
self.but_1 = True
print ("Mouse button 1 clicked")
def r_clicked(self,event):
if self.but_1:
print ("Both keys clicked")
self.but_1 = False
else:
print ("Key R pressed")
Bindings()
root.mainloop()
I am trying to create a game using tkinter in which the players enter their names into an entry widget.
After entering their name, the user should press enter to call the function "player_names", which would ideally save the players name in a list, delete the text in the entry widget and then continue to the next loop (i.e player).
The script seems to be ignoring the bind and and moving straight to the line "self.name_entry.destroy()". How do I ensure that the script waits for the command before continuing?
def initialise_game(self, num_of_players):
self.players_list = []
for i in range(num_of_players):
player_num = i+1
self.name_label = tk.Label(self.bg_label, text='What is the name'
' of Player ' + str(player_num) + '?')
self.name_label.grid(row=0, padx=200, pady=120)
self.name_entry = tk.Entry(self.bg_label)
self.name_entry.grid(row=1, padx=200, pady=0)
self.name_entry.bind('<Return>', self.player_names)
self.name_entry.destroy()
def player_names(self, event):
self.players_list.append(self.name_entry.get())
self.name_entry.delete(0, 'end')
Entry doesn't work like input() it will not stop code and wait till you put text and press enter. GUI creates Entry and it executes code after Entry at once. You have bind to assign function which will be executed whey you press Enter and this function should get value from Entry, and replace widgets (or only text in Label). It also should remove Entry after last player so you have to count how many times function was executed (or how many players you already have - self.player_num)
I didn't test this code but it should works.
def initialise_game(self, num_of_players):
self.players_list = []
# remeber values in class variables, not local one
self.num_of_players = num_of_players
self.player_num = 0
# create only one Label - and change text in it
self.name_label = tk.Label(self.bg_label,
self.name_label.grid(row=0, padx=200, pady=120)
# create only one Entry and assign function `self.player_names`
self.name_entry = tk.Entry(self.bg_label)
self.name_entry.grid(row=1, padx=200, pady=0)
self.name_entry.bind('<Return>', self.player_names)
# set text for first player
self.player_num += 1
self.name_label["text"] = 'What is the name of Player {} ?'.format(player_num)
def player_names(self, event):
# get player's name from Entry
self.players_list.append(self.name_entry.get())
self.name_entry.delete(0, 'end')
# set text for next player or destroy Entry after last player
self.player_num += 1
if self.player_num <= self.num_of_players:
self.name_label["text"] = 'What is the name of Player {} ?'.format(player_num)
else:
self.name_label.destroy()
self.name_entry.destroy()
The same way it works in other GUIs (not only in tkinter) and other languages (not only in Python)
My idea of what are you trying to acomplish shuld be the same, to take an input from the player one by one until all player have added a name.
The ploblem with your code is the for, it needs to complite without an input from the player (tecnicly), insted i opted for the update mecanics that tkinter have, wen a value changes in a winget the winget will update itself and i bind to the button a change value from a increment.
If you want to reuse i , reference it a the start of the def the same wey
This example works, the input of initialise_game is the same as before, replacing the conde in the middle will work just fine, except if your code uses .grid() then replace ask_name.pack()
import tkinter as tk
root = tk.Tk()
root.bg_label = tk.Frame(root)
# Added code, top and bottom are for testing
#--------------------------------------------------------------------------------
root.players_list = [] # Global variabe witch can be accesd by all funcions
i = 1 # This global value is to keep a clear reference on the count of windows
def initialise_game(self, num_of_players):
ask_name = tk.Frame(self)
L = tk.Label(ask_name, text=('What is the name of Player ' + str(i) + '?'))
L.grid(row=0, padx=200, pady=120)
E = tk.Entry(ask_name)
def modify_entry(NULL):
root.players_list.append(E.get())
E.delete(0, 'end')
global i # Change the "search" of values to outside the funtion
i += 1
L.config(text=('What is the name of Player ' + str(i) + '?'))
if (i > num_of_players): # Close the Frame wen all were answered
root.players_list.append(E.get())
ask_name.destroy()
E.bind('<Return>', modify_entry)
E.grid(row=1, padx=200, pady=0)
ask_name.pack() # You can replace it with grin , this moves the Label + Entry were u want
#--------------------------------------------------------------------------------
initialise_game(root.bg_label, 3)
root.bg_label.pack()
root.mainloop()
I'm very new to threading am and still trying to get my head around how to code most of it. I am trying to make what is effectively a text editor-type input box and so, like every text editor I know, I need a cursor-bar thing to indicate the location at which the text is being typed to. Thus I also want to be able to flicker/blink the cursor, which i thought would also prove good practice for threading.
I have a class cursor that creates a rectangle on the canvas based on the bounding box of my canvas text, but I then need to change it's location as more characters are typed; stop the thread and instantaneously hide the cursor rectangle when the user clicks outside of the input box; and lastly restart the thread/a loop within the thread (once again, sharing a variable) - the idea here being that the cursor blinks 250 times and after then, disappears (though not necessary, I thought it would make a good learning exercise).
So assuming that I have captured the events needed to trigger these, what would be the best way to go about them? I have some code, but I really don't think it will work, and just keeps getting messier. My idea being that the blinking method itself was the thread. Would it be better to make the whole class a thread instead? Please don't feel restricted by the ideas in my code and feel free to improve it. I don't think that the stopping is working correctly because every time I alt+tab out of the window (which i have programmed to disengage from the input box) the Python shell and tkinter GUI stop responding.
from tkinter import *
import threading, time
class Cursor:
def __init__(self, parent, xy):
self.parent = parent
#xy is a tuple of 4 integers based on a text object's .bbox()
coords = [xy[2]] + list(xy[1:])
self.obj = self.parent.create_rectangle(coords)
self.parent.itemconfig(self.obj, state='hidden')
def blink(self):
blinks = 0
while not self.stop blinks <= 250:
self.parent.itemconfig(self.obj, state='normal')
for i in range(8):
time.sleep(0.1)
if self.stop: break
self.parent.itemconfig(self.obj, state='hidden')
time.sleep(0.2)
blinks += 1
self.parent.itemconfig(self.obj, state='hidden')
def startThread(self):
self.stop = False
self.blinking = threading.Thread(target=self.blink, args=[])
self.blinking.start()
def stopThread(self):
self.stop = True
self.blinking.join()
def adjustPos(self, xy):
#I am not overly sure if this will work because of the thread...
coords = [xy[2]] + list(xy[1:])
self.parent.coords(self.obj, coords)
#Below this comment, I have extracted relevant parts of classes to global
#and therefore, it may not be completely syntactically correct nor
#specifically how I initially wrote the code.
def keyPress(e):
text = canvas.itemcget(textObj, text)
if focused:
if '\\x' not in repr(e.char) and len(e.char)>0:
text += e.char
elif e.keysym == 'BackSpace':
text = text[:-1]
canvas.itemconfig(textObj, text=text)
cursor.adjustPos(canvas.bbox(textObj))
def toggle(e):
if cursor.blinking.isAlive(): #<< I'm not sure if that is right?
cursor.stopThread()
else:
cursor.startThread()
if __name__=="__main__":
root = Tk()
canvas = Canvas(root, width=600, height=400, borderwidth=0, hightlightthickness=0)
canvas.pack()
textObj = canvas.create_text(50, 50, text='', anchor=NW)
root.bind('<Key>', keyPress)
cursor = Cursor(canvas, canvas.bbox(textObj))
#Using left-click event to toggle thread start and stop
root.bind('<ButtonPress-1', toggle)
#Using right-click event to somehow restart thread or set blinks=0
#root.bind('<ButtonPress-3', cursor.dosomething_butimnotsurewhat)
root.mainloop()
If there is a better way to do something written above, please also tell me.
Thanks.
I am trying to make a simon says game and am having trouble collecting the users choice to compare against the computer's pattern. I am using four buttons to select the colors that flash on the screen. The problem I am having is my program continues to chug along even if my user hasn't selected a color yet, despite my best efforts to stop it, what can I do to stop the program? Here is my code...
Sequence[]
PosColors = ["Yellow","Red","Blue","Green"]
PlayerChoice[]
Score = 0
def Game(Sequence, PlayerChoice,Score):
Score += 1
PlayerChoice = []
Number = randint(0,3)
Sequence.append(PosColors[Number])
for i in Sequence:
RunTime = Score * 1000
Gui.after(1000, blink, rect, SquareCanvas , i)
RunTime = (Score + 1) * 1000
Gui.after(2000, blink, rect, SquareCanvas , White)
X = len(Sequence)
Index = 0
Color = " "
while Color != " ":
BlueButton.config(command = partial(setSelection,NeonBlue))
RedButton.config(command = partial(setSelection,NeonRed))
YellowButton.config(command = partial(setSelection,NeonYellow))
GreenButton.config(command = partial(setSelection,NeonGreen))
Color = getSelection()
print(Color)
while Color != " ":
PlayerTurn(Sequence,PlayerChoice,Score,Index)
X -= 1
Index +=1
def setSelection(Color):
if Color == NeonBlue:
return "NeonBlue"
elif Color == NeonRed:
return "NeonRed"
elif Color == NeonGreen:
return "NeonGreen"
elif Color == NeonYellow:
return "NeonYellow"
def getSelection():
return TheColor
def PlayerTurn(Sequence, PlayerChoice,Score,Index):
PlayerChoice.append(Color)
print(PlayerChoice)
Label1.config(text = 'Well done! \nYou advance to the next round!')
I am planning on passing this through a checker and to loop it until there is an error but I need to get past this front loop first... I know my logic works as I created it using just lists and the command line before moving on the the graphical portion I just need to know how to get the program to stop to collect the user guess, especially as the length of the pattern gets larger. Originally I had the command function included later where I build my Buttons but this placement seems to get as close as possible to what I am looking for. Any help is appreciated thank you
You're going to need to rethink your main program logic. Tkinter is designed such that you should never have your own infinite loop. Tkinter already has an infinite loop -- the mainloop() method of the root window. You can't write logic that waits for input from the user. You have to think of the GUI as in a perpetual state of waiting, and you need set up handlers (button callbacks or event bindings) to react to events.
You also need to understand that a button command can't "return" anything. "Can't" is a bit strong since it obviously can, but there's nothing waiting for the result -- no place to return it to. You need to design your button function such that it sets a global or instance variable.