text input widget

textMayChange is an optimization. Its use to is to specify that the “text” member of a DirectGUI object might later be reset to some other string, allowing Panda to optimize the text object if it will never change after its creation. This is true for all DirectGUI objects, although some objects (like DirectEntry) don’t make use of a “text” member, so it doesn’t have a lot of value for these objects.

The DirectScrolledList will create buttons that, when clicked, will scroll the list up and down automatically, by the number of items specified in scrollSpeed. These buttons are called incButton and decButton, and their properties are specified like incButton_relief = RAISED, incButton_pos = (0.0, 0.0, -0.316), incButton_text = ‘Up’, etc.

itemMakeFunction, if defined, is called to create each item of the DirectScrolledList. It is passed the parameters (item, i, self[‘itemMakeExtraArgs’])), where item is the supplied element, i is the index, and self[‘itemMakeExtraArgs’] is whatever was passed as itemMakeExtraArgs. It should return a DirectGUI item of some kind. This function is only called when the item list is a list of strings.

If the item list is a list of strings and itemMakeFunction is not defined, then a DirectLabel is created for each item.

If the item list is not a list of strings, it is assumed to be a list of DirectGUI items. In this case itemMakeFunction is not used.

You should not pass a tuple to the items member. You should pass a list instead, e.g. [‘abc’, ‘def’], instead of (‘abc’, ‘def’). That is why you are getting the message “AttributeError: ‘tuple’ object has no attribute ‘append’”.

The DirectScrolledList’s command is called whenever the list is scrolled by the user.

makeAllItems calls the itemMakeFunction (or creates a DirectLabel) for every item on the list. It is useful only when you pass in a list of strings for the items. The DirectScrolledList normally uses lazy creation of these items; it does not create the items until they are actually rendered. This allows you to create a DirectScrolledList with thousands of string elements and not have to wait for them all to be created up front. Not sure if there’s any real reason to call makeAllItems() in practice.

Usually, item is a new item, either a string or a DirectGUI element (as described above), and itemID is an arbitrary identification number for each item (but not necessarily a zero-based index number). The itemID for a new item is the return value of addItem().

David

Another thing that is worth clarifying that might not be obvious:

Each DirectGUI item has some background geometry that represents how the item itself looks. This geometry may either be generated using the relief parameter, or loaded externally and specified using the image parameter.

If you don’t want a custom-looking widget, you will probably use the relief parameter. The relief parameter may be one of RAISED, SUNKEN, GROOVE, RIDGE, FLAT, or None. The default is FLAT, I believe.

If you do want to customize the look of the widget, the typical pipeline is to have an artist paint a texture for each of the various states of the widget (e.g. the ready, press, and/or rollover and disabled states). For instance, the artist might create three images, called button_ready.tif, button_press.tif, and button_rollover.tif; all of these are similar but with slightly different looks suggesting the different states of the button.

Then you generate an egg file containing all of these textures using egg-texture-cards:


egg-texture-cards -o button_images.egg -p 240,240 button_ready.tif button_press.tif button_rollover.tif

And when you create the button, you load the egg file and use the models within it for the image parameter:

images = loader.loadModel('button_images.egg')
button = DirectButton(image = (images.find('**/button_ready'),
                               images.find('**/button_press'),
                               images.find('**/button_rollover')),
                      relief = None,
                      text = 'Click me')

Note that when you specify your own geometry this way, you typically want to specify relief = None, to avoid generating the default geometry that would otherwise be generated.

When you specify your own geometry via the image parameter, you do not need to specify the frameSize parameter, since the size of that geometry determines the size of the widget.

(The egg-texture-cards command determines the size of the geometry based on the size of the source image, at least when you supply the -p 240,240 parameter, which sizes the buttons for a one-to-one scale as if they were painted for a 640x480 screen. Basically, the -p value should be half the value of the Y height of screen pixel size your artist was aiming for, if you want to use this one-to-one scaling feature. But the exact use of egg-texture-cards is a complex subject for another time.)

On the other hand, when you generate implicit geometry via the relief parameter, you may need to specify the frameSize parameter to specify the intended size of the widget explicitly. If you do not, the DirectGUI object will attempt to guess the appropriate size, based on the other values in the widget, such as text or geom. It sometimes guesses wrong, especially if you modify these parameters after the widget has been created (necessitating the use of the resetFrameSize() method).

For instance, the DirectEntry bug that you first posted about is really a bug in the DirectEntry failing to guess the size correctly in the absence of an image or frameSize parameter.

David

Thanks again for the detailed answers.

Is there an easy way to find this information (number/name of states) for all the other objects, since they have to be set explicitly?

Also, when I try to do button.setState(ROLLOVER) or button.setState(‘rollover’), (or DirectButton(state=ROLLOVER), it says it is not defined…are only NORMAL and DISABLED acceptable as parameters to this function/option?

Furthermore, I can’t get the scroll buttons for the scroll list to show up at all. I called scroll.incButton.show() and tried to use scroll.incButton_pos to set its position to various places, and even tried reparenting it to aspect2d, but it doesn’t appear. I assume I’m missing something obvious, but I don’t know what.

Only the DirectButton class has implicit states defined. All of the rest of them leave it up to the user. In most cases, the user doesn’t care, and this is unused.

I’m not sure about the button.setState() method. That doesn’t seem to take any parameters. Maybe you should be using the button[‘state’] = NORMAL interface instead; in general, this is the correct way to set properties on a DirectGUI object (rather than calling the setter explicitly).

The scroll buttons should be visible by default. But any button is invisible if it ends up with a zero-size frame (see above). Maybe you should give the buttons an explicit frame with something like:

dl = DirectScrolledList(incButton_frameSize = (0, 1, 0, 1),
                        decButton_frameSize = (0, 1, 0, 1),
                        ...)

Or something implicit like:

dl = DirectScrolledList(incButton_text = "up",
                        decButton_text = "down",
                        ...)

David

OK, back to this. I want to change the colors of the buttons in the scroll list while it’s running. I can’t do

self.scrolledList['items'][i]['frameColor'] = self.color1

because items is a list of strings, not buttons. I tried to use makeAllItems(), assuming that it would call the make function on the buttons and set the frameSize to color1, which is different from what it used to be, but the function I supplied to itemMakeFunction doesn’t get called again. I did get some message in the console about making new buttons, but the print statement in the actual function is never called.

I apologize if any of this is hard to understand, but basically: is there any way to change properties of the buttons in a scrolled list?

Update:
Oddly enough,

self.scrolledList['items'][i]['frameColor'] = self.color1

works right after I construct it, but when I try to do the same thing in another method, it returns an error (TypeError: object does not support item assignment). Any ideas why this would happen and how to fix it?

Update 2:

OK, now I’m really lost.

self.button1 = self.scrolledList['items'][0]
...
self.button1['frameColor'] = self.color1

works perfectly. But when I try

self.button2 = self.scrolledList['items'][1]
...
self.button2['frameColor'] = self.color1

I get the same error as before (same with button3). Any help on this would really be appreciated.

I suspect you’re just experiencing the luck of whether the particular item has been converted to a DirectLabel or not.

I haven’t had a chance to experiment with this, but I think it’s worth another look to see if makeAllItems() is working as advertised. You can also see if works as expected if you premake the items and pass a list of DirectGUI objects instead of a list of strings.

But in general, this isn’t a usual interface for DirectScrolledList. You could try implementing this sort of thing with state, rather than explicitly changing the colors–that might be better implemented.

Try walking through the DirectScrolledList code (it’s all in Python) to see how it is working; it may give some insight here.

David

Seems the makeFunction is called not when the list is created but when scrollTo is called (only on the buttons that appear at that time); that explains why the strings weren’t buttons when the list was created I suppose, although it’s pretty counterintuitive. Adding a call to makeAllItems() right after the scrolled list was created solved the problem.

Right; that’s the create-on-demand that I mentioned, which allows you to populate a DirectScrolledList with thousands of strings but not have to pay the cost up front for creating all of those DirectLabel objects. Instead, it converts each label as you scroll to it, unless you call makeAllItems() first.

David