DirectScrolledFrame error on subclassing

Hello, it’s me again. I think I’ve run into a little bug here but I wanted to run it by experts in the community before reporting it. I can’t seem to subclass DirectScrolledFrame without it throwing this error:

Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)
Assertion failed: has_frame() at line 108 of c:\buildslave\sdk-windows-amd64\build\panda\src\pgui\pgItem.I
Traceback (most recent call last):
  File "C:\Panda3D-1.10.11-x64\direct\showbase\ShowBase.py", line 2156, in __igLoop
    self.graphicsEngine.renderFrame()
AssertionError: has_frame() at line 108 of c:\buildslave\sdk-windows-amd64\build\panda\src\pgui\pgItem.I
:task(error): Exception occurred in PythonTask igLoop
Traceback (most recent call last):
  File "C:\Users\User\Documents\GameTest\test.py", line 10, in <module>
    game.run()
  File "C:\Panda3D-1.10.11-x64\direct\showbase\ShowBase.py", line 3328, in run
    self.taskMgr.run()
  File "C:\Panda3D-1.10.11-x64\direct\task\Task.py", line 553, in run
    self.step()
  File "C:\Panda3D-1.10.11-x64\direct\task\Task.py", line 504, in step
    self.mgr.poll()
  File "C:\Panda3D-1.10.11-x64\direct\showbase\ShowBase.py", line 2156, in __igLoop
    self.graphicsEngine.renderFrame()
AssertionError: has_frame() at line 108 of c:\buildslave\sdk-windows-amd64\build\panda\src\pgui\pgItem.I

Here’s the code in question:

from direct.gui.DirectGui import *
from direct.showbase import ShowBase

game = ShowBase.ShowBase()

class test(DirectScrolledFrame):
    def __init__(self, parent = None):
        super().__init__(self)
        self.reparentTo(parent)

temp = test(parent = render2d)
game.run()

Funnily enough, this code works with DirectFrame, DirectButton, DirectLabel, and DirectScrolledList… just not with DirectScrolledFrame. I’ve tried several variants of this code, but so far none of them work. This is what I think the most accurate code is from reading the manual. Am I doing something wrong or is this a bug?

Thanks in advance for your time!

I’m… honestly a little surprised that it worked with those other classes. DirectGUI can be a little weird when it comes to sub-classing: it expects construction to be done in a certain way, with certain methods called.

Without those things, a DirectGUI sub-class may not work–and it’s possible (albeit that I won’t say that I’m confident of it) that this is causing the problem that you’re seeing, in some way.

Specifically, I believe that DirectGUI requires that you call the “initialiseoptions” method in your constructor. Something like this:

class test(DirectScrolledFrame):
    def __init__(self, parent = None):
        super().__init__(self)
        self.initialiseoptions(test)
        self.reparentTo(parent)

Note that the parameter passed to “initialiseoptions” is the new class itself (and not an instance of that class).

(And if you define custom options, you may want to call the “defineoptions” method–although that’s not yet relevant to your code, I daresay.)

1 Like

That did it! Thank you for the speedy reply. Since we’re already here, how would I use the “defineoptions” method to give the DirectScrolledFrame default options?

1 Like

It’s my pleasure. :slight_smile:

In short, and if I’m not much mistaken, you create a tuple that contains your various options, each itself in the form of a tuple containing: the name of the option, the default value of the option, and the function to call when setting the option. You then pass this, preceded by your DirectGUI keyword-arguments, if any, into “defineoptions”.

Where you intend to override the data for an extant option (if, for example, you want to apply a new default value for “frameSize”), you simply include that option in the option-tuple.

For an example, you might take a look at the code for DirectButton (either on your end or on GitHub).

1 Like

Neat! That’s a lot easier than I thought it’d be! Oh, last thing, I promise. Is there any way to get a list of valid options for a DirectGui element in code? I’d like to allow players to be able to change the visuals of all the displays from a single “theme” dict without needing to edit a separate dict for every element type.

Hmm… If I recall correctly, it is possible–but not straightforward. DirectGUI is a bit of a byzantine thing at times, I’m afraid.

Further, I think that–in at least some DirectGUI widgets, such as DirectOptionMenu–some options are actually discarded after initial setup.

I’m honestly not sure of how feasible it is offhand, I’m afraid.

Hmm… But how are you envisaging this singular “theme” dict to work? If you intend to just pass a single dict to all DirectGUI elements, then I doubt that doing so would work: DirectGUI elements don’t accept options that they don’t support; at most you would be able to support only those options common to all DirectGUI elements, I think.

1 Like

This is actually the problem I’m running into! And hope to avoid. I was thinking of doing something like this:

def make(dgui_type, text = "", theme = {})
	processed_theme = {k:v for k,v in theme.items() if k in dgui_type.getOptionsSomehowAsListIdk()}
	return dgui_type(text = text, **processed_theme)

make(DirectButton, text = "button1", {"text_fg": (1,0.5,0.6,1)})

This is just the initial idea though. If it provides better performance later on, I was thinking of having a global dict store the option names for each type of GUI Widget.

Hmm… Another thought that occurs to me is that DirectGUI elements can have quite large sets of options–are you sure that you want to expose quite so many to your users?

And further, some of those options are rather important–even potentially hazardous. Are you sure that you want users messing with a widget’s position, or frame-size–or command-function…?

My thought is that it might actually be preferable to expose only a specific sub-set of the available options to your users, anyway.

1 Like

Hmm… You might be right. This system is mostly for configuring in-game GUI, such as the heath bar, inventory screens, crafting menu, and whatnot. Mostly for colors, positions of elements, and even text. I already plan to filter out command and extraargs to prevent users from changing button functionality. But in the long run it might just be easier to explicitly select which options can be changed like you say.

1 Like