Why DirectButton doesn't show up?

hello,
here is my code :

def make_button(self, name, action, width, height, proportions, position):
    """this function returns a DirectButton object that can be used for any GUI in the game"""
    maps = self.loader.loadModel(f"models/buttons/{name}")
    print("button creation")
    button = DirectButton(text=name, pos=(width * position[0], 0, height * position[1]), command=action)
    print("button created")
    return button

In fact, I have a model for my buttons… but it didn’t show up too. So I thought I should simplify the code to see wherever it comes from. I tried to reparent it to render2d but it didn’t work. There is no errors shown at all.
position contains a percentage of where I want the button to be on screen (so something like (0.2, 0.3) for example). name contains the name of the button in a string. action is a function. width and height are the width and height of the screen.
I am all new to panda3d soo maybe that’s a blatant mistake but right now I am really stuck.

you forgot to pass the model to your DirectButton with the geom tag.

e.g.

maps = loader.loadModel('button_maps')
b = DirectButton(geom=(maps.find('**/button_ready'),
                       maps.find('**/button_click'),
                       maps.find('**/button_rollover'),
                       maps.find('**/button_disabled')))

Thanks for the answer. Yeah I know. But even when I did it, it didn’t work. So I made this code to see if it was coming from the fact I was loading the models or something else. But if you want, here is the other code. The button still doesn’t show up.

def make_button(self, name, action, width, height, proportions, position):
    """this function returns a DirectButton object that can be used for any GUI in the game"""
    maps = self.loader.loadModel(f"models/buttons/{name}")
    print("button creation")
    button = DirectButton(geom=(maps.find(f"**/{name}"),
                                maps.find(f"**/{name}_click"),
                                maps.find(f"**/{name}_rollover"),
                                maps.find(f"**/{name}")
                                ),
                          pos=(width * position[0],
                               0,
                               height * position[1]),
                          geom_scale=((width * proportions[0]) / BUTTON_WIDTH,
                                      0.0001,
                                      (height * proportions[1]) / BUTTON_HEIGHT),
                          command=action,
                          relief=0,
                          )
    print("button created")
    return button

proportions is a tuple which contains the percentage of place I want the button to take.
I really don’t know what to do… There is no error message. It just doesn’t work.

I think that your problem lies here.

The position of a DirectGUI object, when parented as by default below aspect2d, functions within the range of aspect2d. That range is by default -1 to 1 in the vertical, and something similar on the horizontal, varying according to the current aspect ratio.

So, for a window of dimensions 800x600, and a button placed at the centre, your code will place the button at (400, 300), if I understand correctly. However, at that aspect ratio aspect2d will have a vertical range of -1 to 1, and a horizontal range of -1.3(recurring) to 1.3(recurring), if I’m not much mistaken.

Thus your system will place the button well beyond the bounds of aspect2d, and the button won’t be visible!

Note too that the point with coordinates (0, 0) is at the centre of the screen.

Now, all of this changes if you use pixel2d instead of aspect2d. That does indeed have dimensions that match the size of the window, I believe!

YESSS you were right !! I rewrote the coordinates. Now the button is displayed ! However, it only displays a white rectangle which doesn’t change at all if I click on it / put my mouse over it. ("O_o) Maybe there is a problem with my egg file… :sigh:
Anyway, this was a prototype for the button so I can still remake the images.

Ah, I’m glad! :slight_smile:

Hmm… That seems likely.

A quick test might be to remove your custom geometry and see whether the button renders correctly with its default geometry.

If the problem does lie with your geometry, and you’re using a material to colour it, I think that adding a light to the button (or a common root if you have multiple such DirectGUI objects) might help.

yeah so I replaced it with a test button :

button = DirectButton(text=name, pos=(position[0], 0, position[1]), scale=((width * proportions[0]) / BUTTON_WIDTH,
                                      0.0001,
                                      (height * proportions[1]) / BUTTON_HEIGHT),
                          command=action)

and… everything seemed to work fine at first glance. However, it didn’t do anything when I clicked on it or roll over. So, I checked if the “action” parameter was really a function, I added a print inside to check if it was really not executed. It still didn’t work. XD

Hmm… At a guess, is it possible that you have something else using the name “action” that might be overriding the function by that name?

Otherwise, perhaps it would help if we could see the code in which this is set, including the definition of “action”.

In addition to what Thaumaturge said, can you print out the values you pass to pos and scale? Since you also said, the button didn’t do anything when you roll over it or click it. Usually it should at least change visually if you click it.

Wait no… i forgot to delete the test where I reparented the button to render2d. I just deleted it. Now the button changes when I click on it and it works as intended. Right now, I just replaced the test button with the normal button. Now when I rollover it changes the image (I see it because some buttons are bigger than others ("OvO). So everything works fine now except it is still white (so it should come from my file).

1 Like

Just as a side note, dependent on how your model is built, you may face “Z-Fighting” with your Y-Scale of 0.0001 (this may also be the cause why your button is looking white). You should preferably set this to 1 if you don’t intend to actually change the depth of the model.

1 Like

ok thanks :slight_smile:

hello, so I made new buttons with a resolution in power of 2 (2048x512) and this problem is still there… I don’t think adding a light would solve it since I don’t use any right now and, if I am not wrong, my test button with text wouldn’t have worked if it was the case. I bring here all my code (sorry if it’s not the best) :
guitools.py :

from direct.gui.DirectGui import *
from direct.gui.OnscreenImage import OnscreenImage
from panda3d.core import TransparencyAttrib

BUTTON_WIDTH = 2048
BUTTON_HEIGHT = 512


# buttons dimensions :   width : 2048 height : 512
def make_button(self, name, action, width, height, proportions, position):
    """this function returns a DirectButton object that can be used for any GUI in the game"""
    maps = self.loader.loadModel(f"models/buttons/{name}")
    print("button creation")
    button = DirectButton(geom=(maps.find(f"**/{name}"),
                                maps.find(f"**/{name}_click"),
                                maps.find(f"**/{name}_rollover"),
                                ),
                          pos=(position[0],
                               0,
                               position[1]),
                          geom_scale=((width * proportions[0]) / BUTTON_WIDTH,
                                      1,
                                      (height * proportions[1]) / BUTTON_HEIGHT),
                          command=action,
                          relief=0,
                          rolloverSound=self.loader.loadSfx("sounds/menu/rollover_sound.wav"),
                          clickSound=self.loader.loadSfx("sounds/menu/click_sound.wav")
                          )
    button.setTransparency(TransparencyAttrib.MAlpha)
    print("action : ", action)
    print("button created")
    return button
def make_image(self, name, position, width, height, proportions, imageWidth=None, imageHeight=None, transparent=False,
               parent=None):
    """this function create an image rendered on screen"""
    if (imageWidth is None) or (imageHeight is None):
        image = self.loader.loadTexture(name)
        imageWidth = image.getXSize()
        imageHeight = image.getYSize()
        del image

    print(f"{name} = width : {width}  proportions : {proportions}  imageWidth : {imageWidth}")
    print(f"then scale of x is {(width * proportions[0]) / imageWidth}")
    image = OnscreenImage(image=name,
                          pos=position,
                          scale=((width * proportions[0]) / imageWidth,
                                 1,
                                 (height * proportions[1]) / imageHeight),
                          )

    if parent is not None:
        image.reparentTo(parent)
    if transparent:
        image.setTransparency(TransparencyAttrib.MAlpha)
    return image


menu.py :

import guitools


class Menu:
    def __init__(self, loader, listButtons, listActions, width, height, render):
        """initialization of the menu object"""
        # First, we will have to load all the GUI components
        self.LIST_BUTTONS = listButtons
        self.WIDTH = width
        self.HEIGHT = height

        self.loader = loader
        self.buttonClicked = False

        self.font = guitools.make_image(self, "images/menu/font.jpg", (0, 0, 0), self.WIDTH, self.HEIGHT,
                                        (1.6, 1.9), parent=render)
        self.title = guitools.make_image(self, "images/menu/title.png", (-.5, 00, 0.7), self.WIDTH, self.HEIGHT,
                                         (0.25, 0.02), transparent=True, parent=render)

        for i, e in enumerate(self.LIST_BUTTONS):
            print(i, e)
            self.__dict__[e] = guitools.make_button(self, e, listActions[i], self.WIDTH, self.HEIGHT, (0.2, 0.1),
                                                    (0.8, 0.6 - 0.5 * i))

    def update(self):
        """update of the menu"""

    def __del__(self):
        """ we will just free space and delete any object that has been loaded before."""
        for e in self.LIST_BUTTONS:
            self.__dict__[e].destroy()
        self.title.destroy()
        self.font.destroy()

main.py :

 def loadMenu(self):
        """ load the menu menu"""
        global menu
        menu = "menu"
        print("loadMenu")
        self.player = Menu(self.loader, ("play", "option", "quit"), (self.startGame, self.startOption, self.doExit),
                           WIDTH, HEIGHT, self.render2d)  # load menu
        taskMgr.add(self.updateMenu, "update-menu")  # update menu
    # end of menu

    def updateMenu(self, task):
        """the update function for the menu menu"""
        print("updateMenu")
        self.player.update()
        return task.cont
                         # / ! \ buttons work when I click on them. so we don't care about the next functions
    def startGame(self):
        print("START GAMEEEE !!!")
        taskMgr.remove("update-menu")
        print("end menu")
        del self.player
        self.loadGame()  # loadGame
        taskMgr.add(self.loop, "loop")

    def startOption(self):  #this one doesnt work.. but I prefer to correct this texture problem first
        taskMgr.remove("update-menu")
        print("end menu, go option")
        self.lastMenu.append(self.player)
        self.player = None
        self.loadOption()
        taskMgr.add(self.updateOption, "loop")

    def doExit(self):
        taskMgr.remove("update-menu")
        del self.player
        sys.exit(0)

The .egg files are made with 3 png (with transparency).

In fact, my friend gave me the images via discord (it was in .PNG)

I tried to display the menu.egg (it’s another button that was made at the same time at the others so it should give the same error with play.egg, quit.egg or option.egg) file in pview and it displayed me also a white rectangle but it gave me also this error :
Known pipe types:
wglGraphicsPipe
(all display modules loaded.)
:display:gsg:glgsg(warning): Driver possibly misreported GL_VERSION! Unable to detect correct OpenGL version.
:gobj(error): Texture::read() - couldn’t read: menu.png
:gobj(error): Unable to find texture “menu.png” on model-path /c/Panda3D-1.10.8-x64/bin;/c/Panda3D-1.10.8-x64/etc/…;/c/Panda3D-1.10.8-x64/etc/…/models
:gobj(error): Texture::read() - couldn’t read: menu_click.png
:gobj(error): Unable to find texture “menu_click.png” on model-path /c/Panda3D-1.10.8-x64/bin;/c/Panda3D-1.10.8-x64/etc/…;/c/Panda3D-1.10.8-x64/etc/…/models
:gobj(error): Texture::read() - couldn’t read: menu_rollover.png
:gobj(error): Unable to find texture “menu_rollover.png” on model-path /c/Panda3D-1.10.8-x64/bin;/c/Panda3D-1.10.8-x64/etc/…;/c/Panda3D-1.10.8-x64/etc/…/models

hummm I have different menus and this one is the menu at the beginning… that’s why it’s the menu menu XDD

WELL, I may just have found why

Oh? What have you found?

That doesn’t necessarily hold: Materials, if I recall correctly, require a light in order to work–if your button-geometry uses a material, then it may require a light. However, the default button-geometry doesn’t use materials (as far as I’m aware), and thus doesn’t require lighting.

ok I have understood. I didn’t undertsand the true nature of the .egg file generated. It’s just a model with a link to the .png files for the texture. I thought the .png was inside the .egg file now. I just put both the .egg file and the .png file at the same place, where I generated them and now it works.

1 Like

Aaah, fair enough! Indeed, that makes sense!

Well, I’m glad that you found the problem, and that you have it solved! :slight_smile: