Random beginner-question: LineEdit entry via Signal

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By pferft

Hi everyone,

I set up a custom made number pad from self made buttons and I’d like to connect those (presumably via the according Signals from each one) to an entry-field made from a LineEdit node. So no keyboard input should apply, only clicking/tapping the buttons should fill in the row of numbers (and a comma, and there should be an erase-button as well).

I’d assume for this I need to use the Input Map… somehow? Quite frankly I have no idea how to approach this, so any thought and idea would be very welcome!

:bust_in_silhouette: Reply From: jgodfrey

Here’s one way…

  • Set the LineEdit.Editable property to false to prevent keyboard interaction
  • Wire the pressed() signal of a button (say the 1 button) in the inspector. Wire it to a method named _on_Button_pressed. Additionally, in the Add Extra Call Argument section of the Edit Connection dialog, add a single String argument and set its value to the character you want the key to insert (in this case, the char 1).
  • Click the ‘Connect’ button.

Now, replace the automatically created _on_Button_pressed function with this:

func _on_Button_pressed(the_char):
	var text = $LineEdit.text
	if the_char == "del":
		if text.length() > 0:
			text.erase(text.length() - 1, 1)
	else:
		text += the_char
	$LineEdit.text = text

Wire the remaining buttons in exactly the same way, with a few caveats:

  • For each additional button, add them the same way, but define the extra argument string as the value you want the button to insert (2, 3, 4, 5, …)
  • For the Del button, set the value of the extra argument to the string del. Really, that could be anything unique, but the script is currently expecting the stringdel.
  • As you’re wiring the signals, be sure to change the name of the Receiver Method to the same method for each (_on_Button_pressed).

With that done, when each button is pressed, it’ll call the _on_Button_pressed handler and pass in the value it represents.

If the value is del, the script will remove the last character from the text in the LineEdit. If the value is anything else, it’ll simply be added to the end of the LineEdit.text value.

I’m on it… getting the error Invalid operands for operator in line if the_char == "del":

pferft | 2020-11-23 17:45

Additionally, you could probably use a simple Label for the display of the buttons’ output instead of a non-interactable LineEdit. The sample code I provided above should work with either as it doesn’t use any in-built LineEdit functionality to edit the string. Instead, it just edits the string directly, which should work for either node-type.

jgodfrey | 2020-11-23 17:46

I’m on it… getting the error Invalid operands for operator in line
if the_char == “del”:

The provided code works, so you must have something wrong. Post your code if you can’t find it…

jgodfrey | 2020-11-23 17:48

extends Node2D

func _on_Button1Area_input_event(_viewport, _event, _shape_idx):
	if Input.is_action_just_pressed("mousebuttonclick"):
		print ("Tapped! Timer started!")

	if Input.is_action_just_released(8): # used to be "mousebuttonclick"
		var text = $LabelInvisible.text
		if 8 == "del":
			if text.length() > 0:
				text.erase(text.length() - 1, 1)
		else:
			text += 8
		$LabelInvisible.text = text

I tried “” here and there, but to no avail.
This is one of my custom made buttons, not a “real” one, but I don’t think that could be the problem. I think I’ll make a project from scratch following your template and figure more out that way.

pferft | 2020-11-23 17:57

Yeah, there’s any number of issues there… Your idea of starting with a clean project to understand the solution is probably a good one…

The code I posted is very simple (and short) because it’s just a single script that every button uses. That’s why it needs the button’s value to be passed in, so it knows what button was pressed and how it should be handled.

In your case, you’re (apparently) trying to use individual actions to handle the input. In that case, you already know exactly what button was pressed. So, if (in your example), the 8 button was pressed, you really just need to add 8 to the running string. There’s no need to process a deletion there as you already know (in that block of code) that the del was not pressed - it was an 8.

For your error, that’s caused by this:

if 8 == "del"

In that case, the 8 is the literal number 8, which can never be equal to the string del.

Your code should probably look more like this (completely untested):

if Input.is_action_just_released("1"):
    $LabelInvisible.text += "1"
elif Input.is_action_just_released("2"):
    $LabelInvisible.text += "2"
elif ...
elif Input.is_action_just_released("delete"):
    if $LabelInvisible.text.length() > 0:
        $LabelInvisible.text.erase($LabelInvisible.text.length() - 1, 1)

jgodfrey | 2020-11-23 18:18

Also, what’s the reason for the custom buttons? Is that just for the visual look of the buttons? If so, could you not just use a set of TextureButton nodes and assign appropriate images as necessary? At least that’d give you relatively standard button-behavior, which would play nicely with my originally posted code…

jgodfrey | 2020-11-23 19:08

Yes indeed, each of the single buttons has its own exact purpose, so for now I managed to make it work with a dedicated function for each one, like

func _on_Button1_button_up():
	$LabelInvisible.text += "1"

func _on_Button2_button_up():
	$LabelInvisible.text += "2"

and so on and then for erasure

func _on_ButtonErase_button_up():
	var linetext = $LabelInvisible.text
	linetext.erase(linetext.length()-1,1)
	$LabelInvisible.text = linetext

which is basically exactly what you suggested, except that - if I understand correctly - in your example everything is covered within one single function refering to each button by its name (“1”) (“2”) etc. (which appears more elegant really).
By the way, what if I left all your "elif"s in your listing just as plain "if"s… would that have any downsides??

There are some further details I wonder if you could help me with:

  • One thing generally: is there any way to make a certain entry, say, the comma, invisible (albeit “existing”)?
  • Would it be possible to have the digits before and those after the comma appear separated in their own label respectively? (But still being recognized as one complete entry! For doing the maths with them later on etc…)
  • And how could I stop entries after a certain amount, say, e.g. only two digits after the comma are allowed and only four in front? (Maybe printing an error message when the limit is reached?)

I obviously am very much in some flow here and I hope not to annoy with all these questions!

By the way, the buttons: the only reason I chose to use my self-made ones is that those provided by Godot don’t seem to support “instant mouse_exited”, meaning a signal right at the moment when my cursor/finger exits the dedicated area while still being held down. (On-board buttons trigger only at release then.) So it’s not just for the cosmetics but for a real practical matter.

pferft | 2020-11-24 11:38

in your example everything is covered within one single function
refering to each button by its name (“1”) (“2”) etc

My function doesn’t refer to each button’s name, but instead to the argument that each button passes into the function via the connected signal.

Regarding the other specific questions about managing the entered information… Certainly, anything that’s completely defined and logical can be done via code. Before starting down that path though, you should fully define how the control should work. That definition should cover all possible situations.

Without a complete definition of the problem, you (or me, or someone else) will end up creating code that works for the current definition, but isn’t easy to extend to cover that one thing you forgot to mention

Based on the questions you’re asking, I’d think the best general approach would be to manage the string itself completely independently of the visible GUI components. That is, just edit (add, remove chars) from a string variable while applying your rules (whatever those are - comma handling, number of char handling, …).

With the string properly managed, you could then secondarily take the contents of that string and render into your on-screen controls - again, using whatever rules you’ve defined (splitting pieces between various labels, …)

So, a method that does something like:

  • Accepts the next char (either an addition or deletion)
  • Modifies the string accordingly (including enforcing your rules of correctness)
  • Inserts the resulting string into the on-screen GUI (again, according to your rules)

Again, the hardest part is probably fully and accurately defining how it should work.

Additionally, I think it’s important that all character handling passes through the same, single function for processing. Trying to maintain a complex rule set in a bunch of independent functions will be a maintenance nightmare and will be very prone to errors.

jgodfrey | 2020-11-24 15:25

You describe clearly what I had in mind vaguely. I called it Label-“Invisible” because I thought that I need to first deal with things “off-screen” before dissecting them for practical usage - thinking about such concepts and details can be the fun part of all this, and I sometimes wonder if I work more on them in front of the computer or while taking a walk/cooking/sleeping… it really is the technical challenge then that quickly puts a beginner like me in their place, well knowing that I still don’t even overlook the tools this platform offers me in the first place… not to mention how to use/combine them.

Mentioning such essentials from your professional perspective is therefore very much appreciated! Your suggestions show that you practically read my mind (again - which is always quite impressing).

Thanks again! Now I guess I need to practice more syntax…

pferft | 2020-11-24 16:51