PDA

View Full Version : flashing widgets



tksharpless
19th March 2011, 21:17
I need a bunch of controls that have fixed signal/slot bindings but can be placed into various dialogs depending on app modes. My solution is a widget that creates the control widgets as children of itself, but does not show them; it then connects to their signals using QMetaObject::connectSlotsByName(). Later I insert the controls into layouts in the variable dialogs.

This works nicely except for one glitch: the control widgets "flash" briefly on screen as they are being created (on WinVista32, haven't tried others). The "flash" is not the whole widget, just a small window frame, -- whose style changes according to window flags passed to the widget c'tor. It happens whether the generating widget has a parent or not ( I create it parentless, otherwise a permanent small black rectangle shows in the parent's client area).

Can anyone suggest a solution?

Thanks, Tom

marcvanriet
19th March 2011, 21:32
I suppose you do setVisible( false ) after you create the widget but before you add it to the control widget ?

regards,
Marc

tksharpless
20th March 2011, 17:46
Hi Marc

Yes, I set the widgets invisible at creation and make them visible again before use. The flashing frames show up even if I set all the component widgets invisible too.

-- Tom

wysota
20th March 2011, 17:50
Can we see some code?

tksharpless
20th March 2011, 18:07
Here is the code that creates the flashing widgets


PaniniParameters::PaniniParameters( QWidget *parent )
: QWidget(0) // else get black hole in parent window
{
ppar = parent;
genCtl( "upPitch", "pitch", -90, 90, 0,
tr("pitch to world vertical, degrees"), QString());
genCtl( "upRoll", "roll", -90, 90, 0,
tr("roll to world vertical, degrees"), QString());
genCtl( "camZoom", "zoom", 0, 100, 0,
tr("lens zoom, % of range"),
QString("photo"));

... <10 more like those> ...

// connect signals to our slots
QMetaObject::connectSlotsByName( this );
}

void PaniniParameters::genCtl( const QString &name,
const QString &label,
double lo, double hi, double def,
const QString &tip,
const QString &chkLbl )
{
PaniniSlide * ps = new PaniniSlide( this, label, lo, hi, def, tip, chkLbl );
ps->setObjectName( name );
names_addrs.insert( name, ps );
}


Here is the c'tor of PaniniSlide:



PaniniSlide::PaniniSlide( QWidget * parent,
const QString & lblv,
float lo, float hi,
float initv,
const QString & tip,
const QString & checkText,
bool vert,
int minlen, int maxlen
)
: QWidget( parent )
{
if(minlen < 1) minlen = 121;
if(maxlen < minlen ) maxlen = 1001;
// set checkbox text, hide it if none
checkbox.setText( checkText );
checkbox.setVisible( !checkText.isEmpty() );
// set control data
setLabel( lblv );
setRange(lo, hi, initv );

// set control characteristics
label.setAlignment(Qt::AlignHCenter);
spinbox.setButtonSymbols(QAbstractSpinBox::NoButto ns);
slider.setTickInterval(10);
slider.setTickPosition(QSlider::TicksLeft);
// lay out the controls
if( vert ) {
slider.setOrientation ( Qt::Vertical );
slider.setMinimumHeight( minlen );
slider.setMaximumHeight( maxlen );
slider.setSizePolicy( QSizePolicy::Fixed,
QSizePolicy::MinimumExpanding );
} else {
slider.setOrientation ( Qt::Horizontal );
slider.setMinimumWidth( minlen );
slider.setMaximumWidth( maxlen );
slider.setSizePolicy( QSizePolicy::MinimumExpanding,
QSizePolicy::Fixed );
}
label.setFixedWidth( minW );
spinbox.setFixedWidth( minW );
QBoxLayout::Direction d = vert ? QBoxLayout::TopToBottom
: QBoxLayout::RightToLeft;
QBoxLayout * lay = new QBoxLayout( d, this );
lay->addWidget(&slider);
lay->addWidget(&label);
lay->addWidget(&spinbox);
lay->addWidget(&checkbox );
setLayout( lay );
// minimum widget size
setMinimumSize( lay->minimumSize() );
resize( minimumSize() );
// add tool tips
label.setToolTip( tip );
spinbox.setToolTip( tip );
slider.setToolTip(tr("\
Drag sliders or type numbers.\n\
Fine adjust: click number and\n\
use up and down arrow keys.\n\
Reset: left-dbl-click label.\n\
Options: right-dbl-click label.\
"));

//wire signals
connect( &slider, SIGNAL(valueChanged(int)),
this, SLOT(on_slider_valueChanged(int)));
connect( &spinbox, SIGNAL(valueChanged(double)),
this, SLOT(on_spinbox_valueChanged(double)));
connect( &checkbox, SIGNAL(stateChanged(int)),
this, SLOT(on_checkbox_stateChanged(int)));
// enable widget signals
slider.blockSignals(false);
spinbox.blockSignals(false);
checkbox.blockSignals(false);
}

wysota
20th March 2011, 18:17
Could you explain why you don't pass the parent pointer to QWidget constructor? How do you put the PaniniParameters widget in its parent?

tksharpless
20th March 2011, 18:25
PaniniParameters does not need a parent because it is never shown, does not emit any signals, and its slots all call global functions. I create it parentless because otherwise it shows as a black rectangle in the upper left corner of its parent (which is a QGLWidget). However, passing its parent pointer to the QWidget c'tor has no effect on the flashing that I can see.

wysota
20th March 2011, 18:28
PaniniParameters does not need a parent because it is never shown,
So why is it a widget?

tksharpless
20th March 2011, 18:49
That's what I thought. At first I declared : public QObject. But a QWidget c'tor will not accept a QObject * as parent, so it could not create the controls as its children until I made it a QWidget. This is an inconsistency in Qt, since connectSlotsByName() ( as well as signals and slots themselves ) belong to QObject and don't logically need a QWidget. But tha's how it is...

wysota
20th March 2011, 19:21
That's what I thought. At first I declared : public QObject. But a QWidget c'tor will not accept a QObject * as parent, so it could not create the controls as its children until I made it a QWidget.
Why should it create the controls as its own children? I think I completely miss the point of what you are doing. I don't even see the reason what you need this extra object for.

tksharpless
22nd March 2011, 14:58
Why should it create the controls as its own children? I think I completely miss the point of what you are doing. I don't even see the reason what you need this extra object for.

No doubt there are alternatives. But my question was about getting rid of little flashing window frames....

wysota
23rd March 2011, 08:39
To understand why they are flashing one has to understand what is going on in the program. One of the ways to get rid of flashing is to eliminate the reason for flashing which is almost certainly related to how the architecture of your solution looks like. We all have used child widgets many times in our apps and they haven't been flashing so obviously the problem is related to how you use them. So if you want a solution, please answer my question as best as you can. For me it seems your extra object is just some kind of storage for the child widgets you need but it doesn't have to be a widget. Especially if you place it in a QGLWidget and you do something with it which causes a black spot to appear on your GL widget. It is a good guess that you are doing something with your widgets which you are not aware of. For instance you said earlier that you explicitly hide the widgets and yet we can't see that in the code you posted. This is a perfectly good reason as for why your widgets might be flashing. And it would probably be eliminated if the parent-child relationship (obtained through your extra object) between those flashing widgets and widgets that are explicitly or implicitly visible was severed.

tksharpless
23rd March 2011, 15:42
The reason the creating container is a widget rather than a QObject is that I need it to receive signals from the control widgets it creates; and since there are a lot of those, I would like to use the connectSlotsByName method of QMetaObject to connect them. That method requires the signal sources be children of the object whose slots are to be connected to them. But the QWidget c'tor will only accept a QWidget, not a QObject, as its parent. Hence my container object has to be a QWidget. This is almost certainly a scenario the designers of Qt overlooked, or perhaps ruled out; otherwise it should be possible to make a Widget a direct child of an Object.

I shall now try the alternative of declaring the container a QObject, creating the control widgets parentless, and explicitly connecting their signals. And let you know if that fixes the flashing.

regards, Tom

high_flyer
23rd March 2011, 16:02
This is almost certainly a scenario the designers of Qt overlooked, or perhaps ruled out; otherwise it should be possible to make a Widget a direct child of an Object.
You can be assured its the later and not the former.
It makes no sense to have a visible child (QWidget) to a non visible QObject.
You might want to have a look at QSignalMapper.

wysota
23rd March 2011, 16:56
Could you explain why you need this containing object in the first place? connectSlotsByName() is not a problem, you can probably force a QObject parent to a QWidget child (however useless this would be) by calling setParent() or you can connect slots by name manually on whatever two objects you want. The point is WHY you want to do all that.

tksharpless
23rd March 2011, 21:21
I have a class for displaying images, that is non-Qt for sufficient reasons. I wrap it in various QGLwidgets to create different photography apps. It has lots of control parameters, whose functions the app is not going to modify. The Qt object that creates the controls converts messages from those controls into calls to image display methods, using a global pointer to the display object.

Thus, the app does not need to worry about the image display API. But it is concerned with how the controls are presented to the user, and must place them in various dialogs according to its mission.

Is that clear enough?

Re the SignalMapper: Leaving aside that it does not return values from the controls themselves, it requires not one but two explicit connect calls per control group. My problem here seems to result from my trying to avoid having to write any connect calls. I believe that if I were willing to do that, I could use a QObject instead of a QWidget, and the screen flashing would likely go away.

--Tom

wysota
23rd March 2011, 21:43
I have a class for displaying images, that is non-Qt for sufficient reasons. I wrap it in various QGLwidgets to create different photography apps. It has lots of control parameters, whose functions the app is not going to modify. The Qt object that creates the controls converts messages from those controls into calls to image display methods, using a global pointer to the display object.

Thus, the app does not need to worry about the image display API. But it is concerned with how the controls are presented to the user, and must place them in various dialogs according to its mission.

Is that clear enough?
No, not really. So far I don't see any reason for any artificial behaviour. From what I understand you just need a wrapper over Qt API to fit your non-Qt API. Usually this is handled by a set of factories that return hidden-API objects wrapped into exposed-API objects/classes. Something like:


class BrightnessControl {
public:
virtual void adjustBrightness(int level) = 0;
};

class BrightnessControlFactory {
public:
virtual BrightnessControl* createControl(...) = 0;
};

class QtBrightnessControlFactory : public BrightnessControlFactory {
public:
BrightnessControl *createControl(...) { return new BrightnessControlWidgetOrSomething(...); /* extends BrightnessControl */ }
};

Then you just manipulate the control using the general interface letting the internal implementation handle the details (emitting signals, calling slots, showing widgets and stuff like that). Of course it's likely createControl() should do more than just return a widget instance. The point is that when you need another brightness manipulator, you call the factory and you have a brightness manipulator. And you can have as many of those as you like.

tksharpless
24th March 2011, 16:02
Further information: Making the top class a QObject and creating the control widgets with parent = 0 does not stop the flashing; in fact makes it worse (frames are larger and stay onscreen longer).

Nor does explicitly hiding the control widget in its c'tor prior to creating any of its child widgets -- in fact that also makes the frames bigger.

When I step through the c'tor of the top object in gdb, the frames still flash, at unpredictable times not related to that code; and it seems there are calls to QWidget::setVisible() happening in another thread.

So I would guess this problem is not directly related to the matters we have been discussing. Any further ideas?

wysota
24th March 2011, 16:14
Please provide a minimal compilable example reproducing the problem. I use a lot of Qt based software and non of it is flashing hidden widgets. And certainly no other threads operate on your widgets.

tksharpless
26th March 2011, 15:13
Attached, a small QtCreator project that demonstrates the problem on my system (AMD64, WinVista32, Qt 4 Opensource). Look for little flashing windows in upper left corner of screen just before main window appears.

Thanks, Tom

norobro
26th March 2011, 17:30
In PaniniSlide.cpp the following causes your flash:
// set checkbox text, hide it if none
pcheckbox->setText( checkText );
pcheckbox->setVisible( !checkText.isEmpty() );You have four controls that pass a non-empty QString.

To eliminate the flash, try giving pcheckbox a parent:
pcheckbox = new QCheckBox(this);