[SOLVED] Access 'local scope' objects

I have something like this menu shown in pseudo code;

# We get a call to either create or destroy the main menu;
def mainmenu(menustate):
      if menustate == True:
          showMainMenu()
      else:
          hideMainMenu()

def showMainMenu():
    # Here I have let's say three things
    someText = textObject(blah blah blah)
    someImage = OnscreenImage(blah blah blah)
    someButtn = DirectButton(blah blah blah)
    # this function just created these things no problem

def hideMainMenu():
    # Here is the problem
    textObject.destroy()    # Now won't work because,
    # I am in a 'local scope' of a function, same for the others
    # Trying to access them? How?

I had it in a sort of straight line script before working great, kinda like this example;
ArsThaumaturgis / Panda3DTutorial.io

but I’m trying to break up my game in pages and I am just having trouble,
here is my actual repo if you would like to see the actual project;
SHENKO.org on Github

I thank you in advance, any suggestions are welcome and appreciated.

You’d better use nodes to form pages. Pseudocode.


active_page = None

options = NodePath("options")
video = NodePath("video")

def show_page(page):
    if active_page != None:
        active_page.hide()
    page.show()
    active_page  = page

so;

    someText = textObject(blah blah blah)
    someImage = OnscreenImage(blah blah blah)
    someButtn = DirectButton(blah blah blah)

can somehow all be ‘reparentTo(page)’ right?
Thanx I will try this out

Alteratively, I might suggest that you use classes, thus giving you access to the automatic “self” reference and through it any instance-variables of that class. Like so:

class Cat:
    def __init__(self):
        self.mew = None

    def setupTextNode(self)
        self.mew = TextNode(
                            # Parameters here
                           )

    def hideTextNode(self):
        self.mew.hide()

(The tutorial itself gives a larger example of this approach, I believe.)

I started looking at this option before, so in main.py I would import mainMenu.py
that has this ‘class’ called 'mainMenu(). I would initialize the class with ‘menuState’
variable in order to toggle it on / off. Let me try

1 Like

Here is an example. I don’t think a good solution would be to change attributes at runtime. It is better to create an instance of DirectGUI, configure and hide.

from direct.showbase.ShowBase import ShowBase
from direct.gui.DirectGui import OnscreenText, DirectButton
from panda3d.core import NodePath

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.active_page = None

        page1 = NodePath("options")
        page1.hide()
        page1.reparentTo(aspect2d)

        text1 = OnscreenText(text='page1', pos=(-0.5, 0), scale=0.07)
        text1.reparentTo(page1)

        page2 = NodePath("video")
        page2.hide()
        page2.reparentTo(aspect2d)
        
        text2 = OnscreenText(text='page2', pos=(0.5, 0), scale=0.07)
        text2.reparentTo(page2)

        button_1 = DirectButton(text=("page1"), scale = 0.05, pos = (-0.2, 0, -0.4), command = self.show_page, extraArgs=[page1])
        button_2 = DirectButton(text=("page2"), scale = 0.05, pos = (0.2, 0, -0.4), command = self.show_page, extraArgs=[page2])

    def show_page(self, page):
        if self.active_page != None:
            self.active_page.hide()

        page.show()

        self.active_page  = page

w = Game()
w.run()
1 Like

On the issue of access. You should declare the desired variable as global.

x = 7

def set(v):
    global x
    x += v

set(8)

print(x)

I’m trying to write it now. the only catch is I’m separating the menu into it’s own .py file

Main.py

import folder/mainMenu as menu

class myApp(ShowBase):
    menuState = True or False
    initialize a class called 'mainMenu()'

And then in a separate folder and file;
mainMenu.py

class mainMenu:
    def __init__(self, menuState):

    show ---> hide ---> etc... etc...

I will let you guys know how it turns out

I would argue that global variables are prone to name-collisions and similar confusions, while a class-based approach can reduce that.

That said, I would suggest that the original poster go for the style that works for them, whether that involves global variables or not.

#—FINAL SOLUTION—#
in main.py we have something like;

import module as module     # SEE: module.py mentioned below

class MyApp(ShowBase):

    def __init__(self):
        module.mainMenu(mainMenuState, cameraActive)    # SEE: module.py below
        self.accept('m', self.menuToggle)    # This listens to keyboard for 'm' button!

    def menuToggle(self):
        module.mainMenu(mainMenuState, cameraActive)    # Pressing 'm' calls menu

Now in a seperate folder called module containing a empty init.py file
and mainMenu.py I do something like this;

from panda3d.core import AudioSound
from panda3d.core import TextNode
from direct.gui.OnscreenImage import OnscreenImage
from direct.gui.OnscreenText import OnscreenText

class mainMenu:
    def __init__(self, mainMenuState, cameraActive):
        # Just try and destroy a menu if it already exists
        try:
            self.hideMainMenu()
        except:
            # Here I create all menu items example
            self.textObject = OnscreenText(....)             # Create some text like 'loading....'
            self.imageObject = OnscreenImage(...)       # To create a Logo or something
            self.startbttn = DirectButton(...command=self.start)  # Click bttn calls self.start()

    def hideMainMenu(self):
        self.textObject.destroy()
        self.imageObject.destroy()
        # This one was a bit different
        self.startbttn['state'] = DGG.DISABLED
        self.startbttn.destroy()

    def start(self):
        # Let's say you click 'start game' button we destroy the menu and move on...
        self.hideMainMenu()
        callYourCustomFunctionHere()

(Thank you serega-kkz && Thaumaturge for the assist.)

Everything seems to be working fine, except for one glitch. Multiple presses of ‘m’
seems to require equal amounts of button clicks to counter it. As though the pressing of
the ‘m’ button queue’s up or stacks the menu. I can’t figure this out since the very first
thing I do is try to destroy the menu at the very begining. suggesting that the ‘try’ block is not actually succesfully destroying the previous menu. But otherwise works great!

You can find the latest implementation of this in practice on the Shenko.org repo here;
SHENKO on Github

1 Like

Note that in your “try”-block, you attempt to destroy elements of the instance that you’ve just created (as referenced by “self.”). It won’t touch on any previous instances of the “mainMenu” class.

I’d suggest, then, moving the logic that calls “hideMainMenu” out of “mainMenu” itself, and into the code that calls for the construction of a “mainMenu”.

Now, that alone won’t work, I daresay, because it doesn’t have access to the previous “mainMenu”. So, I suggest storing in a variable of your “MyApp” class a reference to the current “mainMenu” object, thus allowing you to destroy that object and replace it with the subsequent “mainMenu”.