Thanks to @logan and his post DirectSlider with progressBar I managed to have the effect I wanted.
The code still needs some cleaning up (for instance it doesn’t handle the horizontal vs. vertical case…), but I’m almost there… I post it below here in case it might be useful to someone else.
There are two points of improvements in particular that’s I’m struggling with:
-
When I pass the reference to command to the ControlSlider options, I need to rely on a dictionary where I define in the container control (ControlPanel) and pass this together with the slider’s name as extraArgs to the showValue function. I couldn’t think of a more elegant way to realize this, clearly I can’t do something like
slider = ControlSlider( ... command=command, extraArgs=slider )
inaddSliderControl
because the variable slider isn’t initialized yet. Can someone suggest any better solution? -
The .png images I’m using for the control have an Alpha channel with transparency to allow for rounded corners. Unfortunately I couldn’t find a way to make the ControlPanel background show trough it. Any suggestions?
from direct.gui.DirectGui import *
from panda3d.core import *
import direct.directbase.DirectStart
from panda3d.core import Vec4
from direct.gui.DirectGui import DGG,DirectButton,DirectSlider, OnscreenImage
from panda3d.core import TransparencyAttrib
class ControlPanel(DirectFrame):
def __init__(self, application):
self.application = application
super().__init__(frameColor=(1, 0, 0, 1), pos=(1.5, 0, 0))
background_cm = CardMaker("image_plane")
background_cm.set_frame(-1, 1, -1, 1)
self.background_plane = self.attach_new_node(background_cm.generate())
self.background_plane.setColor((.85, .85, .85, 1))
self.allSliders = {}
def addSliderControl( self, image, pos_x, pos_y, scale, command, name ):
slider = ControlSlider(
image=image,
pos=(pos_x,0,pos_y),
scale=scale,
value=50,
range=[0,100],
command=command,
extraArgs=[name, self.allSliders]
)
# self.attach_new_node(slider)
slider.reparentTo(self)
print(f"slider: {slider.getPos()}")
self.allSliders[name] = slider
class ControlSlider(DirectSlider):
def __init__(self, parent = None, **kw):
optiondefs = (
('allowprogressBar', True, self.__progressBar),
('image', None, None)
)
background_texture = self.createImageTexture(kw['image'])
image_width, image_height = self.getImageTextureDims(background_texture)
image_aspect_ratio = image_height / image_width
print(f"image_aspect_ratio = {image_aspect_ratio}")
optiondefs += (
('frameSize', (-1, 1, -.5, .5), None),
('frameVisibleScale', (1, 1), None),
('image_scale', (1, 1, image_aspect_ratio), None),
('orientation', DGG.HORIZONTAL, None ),
('relief', DGG.FLAT, None ),
('progressBar_relief', DGG.FLAT, None ),
('progressBar_frameColor', (1, 1, 1, .5), None ),
('progressBar_frameTexture', 'models/textures/blue.PNG', None ),
('thumb_relief', DGG.FLAT, None ),
('thumb_frameColor', (1.0,1.0,1.0,0), None )
)
self.orientation = None
self.progressBar = None
self.defineoptions(kw, optiondefs)
# Initialize superclasses
DirectSlider.__init__(self, parent)
self.progressBar = self.createcomponent("progressBar", (), None,
DirectButton, (self,),
borderWidth = self['borderWidth'],
state=DGG.DISABLED,
sortOrder=-1)
# Call option initialization functions
self.initialiseoptions(ControlSlider)
self.setTransparency(TransparencyAttrib.MAlpha)
self.progressBar.setTransparency(TransparencyAttrib.MAlpha)
self.component('image0').setTransparency(TransparencyAttrib.MAlpha)
def createImageTexture(self, image):
tex = loader.load_texture(image)
# Enable transparency on the texture
tex.set_format(Texture.F_rgba)
tex.set_wrap_u(Texture.WM_clamp)
tex.set_wrap_v(Texture.WM_clamp)
tex.set_minfilter(Texture.FT_linear_mipmap_linear)
tex.set_magfilter(Texture.FT_linear)
return tex
def getImageTextureDims(self, image_tex):
image_width = image_tex.get_x_size()
image_height = image_tex.get_y_size()
return image_width, image_height
# see code at https://discourse.panda3d.org/t/directslider-with-progressbar/29126
# for the following methods
def __progressBar(self):
...
def __updProgressBar(self):
...
#override
def setOrientation(self):
...
#override
def destroy(self):
...
#override
def commandFunc(self):
...
#override
def setFrameSize(self, fClearFrame = 0):
...
def showValue(name, sliders):
print(f">>>{name}: {sliders[name]['value']}")
control_panel = ControlPanel(base)
control_panel.addSliderControl("../models/textures/ctrl_base_heading.png",
-0.35, 0.1, 0.5,
command=showValue,
name="base_heading"
)
control_panel.addSliderControl("../models/textures/ctrl_1st_arm_heading.png",
-0.35, 0.65, 0.5,
command=showValue,
name="1st_arm_heading")
base.setBackgroundColor(0.8, 0.2, 0.2, 1.0) # R, G, B, A
base.run()