Tuesday 28 June 2011

Qt Dynamic Properties

I'm currently writing a model viewer which allows the user to load meshes and textures to see how things will look in a realtime engine.

One of the elements I'm currently working on is the lighting system. The main plan is to have very basic 3 point lighting and give the user some control over the lighting system and the ability to set the colours etc.

I designed a basic panel to start testing this as shown here


Signals and Slots

Usually we would associate a series of signals and slots with each of the components to allow for interactions. For example if the x double spinbox is changed the valueChanged(double) signal is emitted and we can connect it to a slot (method) to process it. In the case of this first panel I would have called the same slot for each of the signals of the widgets as the light class would have to be updated with all the values.

This multiple widget -> single slot would work fine for one panel, however as you see above I have three different light groups.

I could have 3 sets of different slots for each of the different light controls, however there would be a lot of duplicated code and maintenance would have been difficult, also at some stage I may wish to dynamically add lights so again the code would be redundant.

Dynamic Properties

What I need to do is to have some easy way to determine which widget belongs to which Light group so I can process them based on the actual group.

To do this we can use the Qt Dynamic properties tab in designer.

Clicking on the + tab in the properties inspector above give the option to add a String or Bool (or other) dynamic property. In this case I'm going to add a property called LightName and will set it to the name of the light panel the component belongs to (for example FillLight). 

Once the property has been added you should have a new element of the property panel as shown below

Accessing Dynamic Properties

Now we have associated the property to the components we need to access them. First we connect each of the widgets to a method 

// add all the fill light components
connect(m_ui->m_fillLightActive,SIGNAL(toggled(bool)),this,SLOT(updateLight()));
connect(m_ui->m_fillLightX,SIGNAL(valueChanged(double)),this,SLOT(updateLight()));
connect(m_ui->m_fillLightY,SIGNAL(valueChanged(double)),this,SLOT(updateLight()));
connect(m_ui->m_fillLightZ,SIGNAL(valueChanged(double)),this,SLOT(updateLight())); 

As you can see each of the widgets is connected to a single method called updateLight() in this method we are going to determine which widget called the method and determine it's dynamic property name.

void MainWindow::updateLight()
{
 // first we get which object sent the signal using the sender method
 QObject *obj=this->sender();
 // now we lookup dynamic property of the sender and look for LightName
 QVariant lightName=obj->property("LightName");
 // now see wich light group we have found
 if(lightName.toString() == "KeyLight")
 {
  std::cout<<"doing key light \n";
 }
 else if(lightName.toString() == "FillLight")
 {
  std::cout<<"doing fill light \n";
 }
 else if(lightName.toString() == "BackLight")
 {
  std::cout<<"doing back light \n";
 }
}

First we use the sender method of the QObject class to determine which object sent the signal. Once we have the object pointer we can call the property method to access the dynamic property "LightName". You will notice that the property method returns a QVariant object so we need to convert it to the correct data type (in this case a QString) to process it.

For now I'm just printing out the different light group values, but eventually this will be used to change all the light properties.

1 comment: