What function is used to create an on/off switch using the "L" keyboard key to toggle a gui element?

I want to use the L key to turn a gui element on/off using “element.destroy()” to turn it off and “entry = DirectEntry” to turn it on.

This is how far I got in my script, it turns on but not off. Press “L” key to activate:

from direct.showbase.ShowBase import ShowBase
from direct.gui.DirectGui import *
from direct.gui.OnscreenText import OnscreenText
from direct.gui.DirectEntry import DirectEntry
from panda3d.core import *

class myApp(ShowBase):
    def __init__(self):

        #draw window
        ShowBase.__init__(self)

        def onOff():
            map = base.win.get_keyboard_map()
            g=map.get_mapped_button_label("l")

            if g:
                print("On")
                # your other actions

                def setText(textEntered):
                    print(textEntered)
                    entry.enterText('')

                def clearText():
                    entry.enterText('')

                # add text entry
                global entry
                entry = DirectEntry(
                    text = "",
                    scale=.05,
                    command=setText,
                    initialText="",
                    numLines = 1,
                    focus=0,
                    focusInCommand = clearText,
                    obscured = False,
                    pos=( .5, 0, .5 )
                    )

            else:
                print("Off")
                # your other actions
                entry.destroy()

        self.accept('l', onOff)

# run app
app = myApp()
app.run()

I imagine that the problem is that, since your method is run when the “L”-key is pressed, when your method checks the state of the “L”-key it will always find it to be pressed.

One alternative might be to check, not the “L”-key, but the object stored in the “entry” variable: the status of that should naturally change in accordance with the gui-widget’s presence or absence.

I might then suggest that, when you destroy your object, you additionally assign the “entry” variable to “None”, and then check for a value of “None” where you currently check the state of the “L”-key.

Something like this:

def onOff():
    if entry is not None:
        print ("On")

        # < Your code here... >
    else:
        print ("Off")
        
        entry.destroy()
        entry = None

    # (Note that you can technically also just have "if entry:"--
    #  but I prefer the clarity of explicitly checking against "None".)

By the way, just to check: have you considered hiding your gui-object instead of destroying it? (You could then check its state not by seeing whether its variable has a value of “None”, but by whether it’s hidden, via the “isHidden” method.)

Thanks for your reply.

I added the “entry = None” and it isn’t being destroy. As for the “isHidden”, I tried:

else:
        print ("Off")
        
        entry.isHidden()
        entry = None

My script only runs the code in the if statement and seems to ignore anything in the else statement. Does “self.accept(‘l’, onOff)” not allow switching between “if” and “else” statements?

For the sake of clarity, are you still using the same “if”-statement that you posted above, or the one that I suggested?

Without the change to the “if”-statement the code will indeed continue to not work.

[edit]
Ah, actually, I think that I see another issue: you’re declaring the variable to be “global” in the code within your “if”-statement, but not in the code within your “else”-statement. I doubt that it’s the issue, but it may become one if we get past the current matter.

Sorry, I don’t use the same code-style as you (I don’t really use that sort of “global”, for one), and I’m a bit tired, so I missed that I fear.
[/edit]

Oh, you can absolutely have “if-else”-statements within the methods run by calls to “accept”, I do believe; there are no such restrictions that I’m aware of!

Well, two things:
First, “isHidden” is used to determine whether something is hidden, not to actually hide it–for the latter you would use “hide”.

And second, I wouldn’t suggest setting the variable to “None” in the case of using “hide”/“isHidden”, as it means that you wouldn’t be able to later test whether the object is hidden.

[edit]
But perhaps let’s get your code working with your original approach before we try mixing in another! That is perhaps asking for more confusion, I fear.

I really don’t suggest putting in a nested function into your constructor like this. Rather make your function another method of myApp, and store state (like self.entry) on the self instance. Otherwise it just gets really confusing very quickly with scoping and such.

get_mapped_button_label does something completely unrelated to what you want here, so I don’t think you can use it here.

2 Likes

I’m using “self.accept(‘l’, onOff)” to trigger the “onOff” function, and I’m using

map = base.win.get_keyboard_map()
g=map.get_mapped_button_label("l")

to read the “L” key input so the gui element would activate. There is probably a better way, but I don’t know of another method currently.

Well, as I said, I’d suggest checking the “entry”-object itself. The “L”-key is presumably always going to be found by your “if”-statement to be pressed, as the method is called when it’s pressed.

Perhaps it would help more if I gave you a fuller example than I did above:

from direct.showbase.ShowBase import ShowBase
from direct.gui.DirectEntry import DirectEntry

class myApp(ShowBase):
    def __init__(self):

        #open window
        ShowBase.__init__(self)

        # Create the entry just once,
        # And keep it in a variable of
        # the application-instance being
        # constructed
        #
        # Also, I'm simplifying the entry
        # a bit to ensure that this works
        # as a runnable example.
        self.entry = DirectEntry(
                    scale=.05,
                    numLines = 1,
                    pos=( .5, 0, .5 )
                    )
        
        self.accept('l', self.onOff)

    # Note that "onOff" is now a method of the class
    def onOff(self):
        # Since I'm rewriting this, let me show
        # the approach that I described above,
        # that simply hides and shows the entry...
        if self.entry.isHidden():
            self.entry.show()
        else:
            self.entry.hide()

# run app
app = myApp()
app.run()

I’m inclined to concur, actually.

1 Like

Thanks for the sample code. It helped me understand the self instance more. I was using the “destroy()” function because I was thinking that the “hide()” function would still hold focus after hiding, but it actually doesn’t after testing. This is the working code I combined with your sample code:

from direct.showbase.ShowBase import ShowBase
#from direct.gui.DirectGui import *
from direct.gui.DirectEntry import DirectEntry
#from panda3d.core import *

class myApp(ShowBase):
    def __init__(self):

        #draw window
        ShowBase.__init__(self)

        def setText(textEntered):
            print(textEntered)
            self.entry.enterText('')

        def clearText():
            self.entry.enterText('')

        # add text entry
        self.entry = DirectEntry(
            text = "",
            scale=.05,
            command=setText,
            initialText="",
            numLines = 1,
            focus=0,
            focusInCommand = clearText,
            obscured = False,
            pos=( .5, 0, .5 )
            )


        self.accept('l', self.onOff)

    def onOff(self):
        if self.entry.isHidden():
            self.entry.show()
            print("On")
        else:
            self.entry.hide()
            print("Off")

# run app
app = myApp()
app.run()

1 Like