Add QRadioButton into QWidget without layout - qt

I am trying to do something that I thought it would be terribly easy : Adding QRadioButton into my QWizardPage without any layout.
The following code that adds QRadioButton is being called when user clicks button Next (signal currentIdChanged calls the code):
int newPositionY = 0;
for (vector<Events::VCS::PnPDevice>::const_iterator it=m_devices.begin(); it!=m_devices.end(); it++)
{
if (it->type == Events::VCS::HEADSET)
{
//add a radio button
stringstream text;
text << (it->name) << " " << (it->serialNumber);
QRadioButton* radioButton = new QRadioButton(ui.wpSINGLE_USER);
radioButton->setGeometry(50, 20 + newPositionY, 260, 40);
radioButton->setText(text.str().c_str());
newPositionY = newPositionY + 40
}
}
}
I added this little piece of code to see what is going on with my QRadioButton
QList<QRadioButton*> listButton = ui.wpSINGLE_USER->findChildren<QRadioButton*>();
int size = listButton.size();
QRect rect1 = listButton[0]->rect();
QRect rect2 = listButton[1]->rect();
I then realized that it seems that the problem might be QRect.
The value for rect1 and rect2 are erroneous. rect1 = (0, 0, 259, 39) and rect2 = (0, 0, 259, 39) Correct value for rect1 should be (50, 20, 260, 40) andn for rect2 (50, 60, 260, 40)
So, what is the problem, how to add QRadioButton into QWidget without layout?
EDIT
That is strange, if, instead of adding the QRadioButton into the QWizardPage when user clicks button next I am adding them into the QWizard contructor, it works.
Can somebody tell me why I am not able to add QRadioButton into my QWizardPage into my slot function?
Thanks

QRect is behaving properly. You should check the geometry(), not the rect().
http://doc-snapshot.qt-project.org/4.8/qwidget.html#geometry-prop
http://doc-snapshot.qt-project.org/4.8/qwidget.html#rect-prop
http://doc-snapshot.qt-project.org/4.8/application-windows.html#window-geometry
Here are some QWizard examples that are worth studying...
http://qt-project.org/doc/qt-4.8/dialogs-trivialwizard.html
http://qt-project.org/doc/qt-4.8/dialogs-classwizard.html
http://qt-project.org/doc/qt-4.8/dialogs-licensewizard.html
Generally speaking...
Using Layouts, makes your life easier. There is a learning curve and the code looks funny at first, but you will be grateful that you did in the long run. If at all possible, I avoid using Qt Designer, and I use layouts, and nested layouts to position everything. Add in stretches and spacers and anything else you need. It's great. And then if you need something to show or hide later, you nest that in a QWidget, and you show and hide that widget to adjust when it is visible.
http://qt-project.org/doc/qt-4.8/layout.html
http://qt-project.org/doc/qt-4.8/tutorials-widgets-windowlayout.html
It might be that when a QRadioButton is added without a layout, it isn't updating your widget.
If you change what is in your widget, you need to post an update() event for it to be reflected.
http://doc-snapshot.qt-project.org/4.8/qwidget.html#update
Also when you don't use layouts, the layering and positioning can go a little crazy. Make sure that you are not drawing something else on top of the location where the button is added.
Here is some more things to keep in mind. Though it looks like you may be taking care of some of this already...
Another issue that can happen is if you add a QRadioButton not into a layout and forget to parent it, it probably won't get added at all. Parenting of QObjects is handled nicely by layouts, too.
And finally, you need to make sure that your button exists for the scope of your parent widget. So if you declare it in a slot, and it has a local scope, by the time you leave the slot, it is gone. (You put it on the heap, so it should be okay).
So make it a member variable for your subclassed QWizardPage, and make it a pointer. Qt handles the parenting of objects well, and the deletion correctly if they are on the object tree properly.
http://qt-project.org/doc/qt-4.8/objecttrees.html
Also be sure to look at using a 'QGroupBox' in your layout when you are using QRadioButtons. This handles the exclusive attribute well.
http://doc-snapshot.qt-project.org/4.8/widgets-groupbox.html
http://doc-snapshot.qt-project.org/4.8/qradiobutton.html#details
Hope that helps.

Related

Qt: How to disable the "shrink-to-fit" for a QGridLayout? [duplicate]

This question already has an answer here:
QScrollArea with dynamically changing contents
(1 answer)
Closed 6 years ago.
I'm using Qt5 on Windows7.
In my current app I try to display and remove a number of push-buttons.
widget = new ButtonWidget(ui->frame); // frame is a QScrollArea
connect(ui->addBtns, SIGNAL(clicked()), widget, SLOT(addButtons()));
connect(ui->deleteBtns, SIGNAL(clicked()), widget, SLOT(deleteButtons()));
And the ButtonWidget class is here:
ButtonWidget::ButtonWidget(QWidget * parent) : QWidget(parent)
{
gridLayout = new QGridLayout(parent);
}
static QStringList texts{...}; // various content (see explanations below)
void ButtonWidget::addButtons()
{
for(auto i = 0; i < texts.size(); i++)
{
gridLayout->addWidget(new QPushButton(texts[i], this), i / 5, i % 5);
}
}
void ButtonWidget::deleteButtons()
{
while(gridLayout->count())
{
delete gridLayout->itemAt(0)->widget();
}
}
If I set QStringList texts{"1\nok"}; I get this:
...which is ugly (centered and expanded horizontally on all layout).
If I set QStringList texts{"1\nok",...,"24\nok"}; I get this:
...which is/seems quite ok.
And finally, if I set QStringList texts{"1\nok",...,"36\nok"}; I get this:
...which is very bad, garbled, etc.
So, the question: Is there any way to fix this, i.e. to disable somehow this "shrink-to-fit" on the layout? A default button size would be just fine.
I would like to take advantage of the vertical-scroll feature of the QScrollArea, not to stuff and cram all the buttons in the (limited) available space...
If you merely add child widgets to a QScrollArea, you won't get any scrolling, since these widgets are not managed by the area in any way. You need to set the widget using QScrollArea::setWidget instead:
widget = new ButtonWidget;
ui->frame->setWidget(widget);
So, the question: Is there any way to fix this, i.e. to disable
somehow this "shrink-to-fit" on the layout? A default button size
would be just fine.
Of the top of my head, one can set a minimum size per button. One can calculate what the minimum size should be from the font (but first just play with setting the minimum size).
Here is a reference. You will notice that minimumSize takes precedence over layout. It might also change the SizePolicy, which you can also look at. I suggest you play with the designer, as it respects sizing and layouts. One can learn allot from it.
Also look at minimumSizeHint, as the layout interacts with this. See also MasterBLB's comments here

Qt: Why does my paintEvent() erase everything before doing its job?

I'm trying to make my widget paint a rectangle with every paintEvent it receives. The rectangles are supposed to increase in size by 1px at a time, filling a square. What I get, however, is only the latest (and largest) rectangle.
void TestClass::paintEvent(QPaintEvent* e){
static int size = 1;
QStylePainter painter(this);
painter.setPen(Qt::blue);
painter.drawRect(QRect(50, 50, size, size));
size++;
}
I don't understand why it would be that way. I expected the painter to just paint on top of what is already there. Instead it seems to delete the previously drawn rectangle, leaving me with a single rectangle at any time. Any ideas?
setAutoFillBackground(true/false) does not change anything but the color of the background.
To evoke a paintEvent I update() inside mousePressEvent(). So my rectangles grow with every click.
Thanks a lot.
So, to answer my own question, I found out that:
(1) update(QRect area) erases the area specified by its argument before doing anything else. Calling update without argument erases the whole widget area.
(2) The area that has been cleared is the only area that any painting is done on, even if your paintEvent() looks to paint somewhere else. The untouched part of the widget is not affected.
For example, consider this paintEvent().
void myWidget::paintEvent(QPaintEvent* e){
QPainter painter(this);
static int counter = 1;
if (counter % 2){
painter.fillRect(0, 0, 199, 199, Qt::blue); //Fill with blue color
} else {
painter.fillRect(0, 0, 199, 199, Qt::green); //Fill with green color
}
counter++;
}
Calling update() repeatedly will toggle between green and blue color for every pixel. However, upon calling update(QRect(0, 0, 50, 50)) only the top left quarter of the widget's area will change its color, the other pixels will remain untouched, even if paintEvent(..) contains the instruction to always paint over the whole widget's area.
I do not know whether my answer is fully correct under any circumstances, but I expect more noobs to be confused about the relationship between update() and paintEvent() and so I'll offer this as a first aid.
Greets.
The QWidget::paintEvent docs state that
When the paint event occurs, the update region has normally been
erased, so you are painting on the widget's background.
So, probably that's the case in your example.
You're not guaranteed any sort of state preservation between invocations of paintEvent. That's the long and the short of it. The only correct semantics of your implementation of paintEvent are: paint (at least) the entire region that was passed to you, as in: touch every pixel, unless your widget was pre-cleared prior to painting.

QT Flowlayout - issue with multiline labels

I have a small problem with QT layout.
I have a toolbox, and I want to populate it with some checkable buttons with a description. So I create a QWidget with a QGridLayout and put the QButton in the first cell and the QLabel in the second.
This is the most important part of the code (I removed dependancies from other irrelevant code in the app):
QWidget *createCellWidget()
{
QToolButton *button = new QToolButton(this);
button->setCheckable(true);
button->setMinimumSize(57,57);
button->setMaximumSize(57,57);
QWidget *widget = new QWidget(this);
QGridLayout *layout = new QGridLayout(widget);
layout->addWidget(button, 0, 0, Qt::AlignHCenter);
QLabel *lbl = new QLabel("my very long caption");
lbl->setWordWrap(true);
lbl->setAlignment(Qt::AlignCenter);
layout->addWidget(lbl, 1, 0, Qt::AlignTop);
widget->setMaximumWidth(80);
widget->setMinimumWidth(80);
return widget;
}
then I create a QGridLayout and populate it with these controls:
QWidget *itemWidget_FlowControl = new QWidget(this);
QGridLayout *_flowControl_layout = new QGridLayout(itemWidget_FlowControl);
_flowControl_layout->addWidget(createCellWidget(), 0, 0);
This works well and produces this output:
this is a nice layout. Unluckily if I enlarge the window the controls do not "flow", so I tried to replace the QGridLayout with a flowlayout (here are the source files).
Now the behavior is much better. BUT...
this is what I get. The longer captions are laid out as if they were single-lined, so the text overlaps with the button.
What can I do to make it look like before, but keeping it as a "flow layout"? Or do you know if there is any alternative in QT 5.2?
Thank you
What's wrong?
You are on the right track of using flowlayout.
The only problem is the internal layouts (QGridLayout in your case) of your cell widgets also stretch in response to the resize events.
Solution
The solution is surprisingly simple:
Try to limit the stretch of the internal layout.
Within the factory function QWidget *createCellWidget():
[Option 1]
Add lbl->setMaximumWidth(60); to manually limit the stretch of label width. This makes the internal layout not stretch so "freely."
[Option 2]
Add layout->setSizeConstraint(QLayout::SetFixedSize); to restrain the internal layout stretch. By doing this, you may want to manually add some line break code (\n) to your "very long caption" in case the label width automatically decided by Qt doesn't suit your need.
Result
IMO the best approach would be use off style sheets (flow layout is Ok).
Simply use QButton but customize its view by altering its stye sheet.
Currently I have no time to write an example.

Qt QTableView draw border around active cells

I'm trying to implement behavior similar Excel in a QTableView, where a border is painted around the entire current selection. I have tried this what feels like a hundred different ways and keep getting problems. I can draw the border easily enough, but remnants of the border are left whenever the selection changes. Here is one example I've tried in QTableView::paintEvent ...
void MyTableView::paintEvent(QPaintEvent* event)
{
// call QTableView's paint event first so we can draw over it
QTableView::paintEvent(event);
// activeSelection is a list of indexes that is updated in another function
// the function also calls QTableView::repaint whenever this list changes
// in an attempt to erase the previously drawn border
if(!activeSelection.size())
return;
QRect rect = visualRect(activeSelection.at(0)) |
visualRect(activeSelection.at(activeSelection.size() - 1));
// temporarily draw smaller border so it doesn't lie on the grid lines
rect.adjust(4, 4, -4, -4);
QPen pen(Qt::black, 2);
QPainter painter(viewport());
painter.setPen(pen);
painter.drawRect(rect);
}
That code produces results such as this
I would love any suggestions on how to make this run more smoothly. I had tried doing this in the delegate, but then the delegate needs to know all the indexes that are selected and it can't paint over the grid lines drawn by the QTableView. Plus, my table class needs to know where the border has been drawn.
try to call update(); in your selectionChanged function. this will slow out your implementation, but will remove garbage.

Qt QLayout inside QDesktopWidget

I want to do universal method to set position of my widget.
All I wanna get it is set right coordinates for my widget wich must always be in right bottom corner of desktop. My widget can change his height (or maybe width) but it must have adjusted size by both ordinates... (too many words)
My idea is using QDesktopWidget as basic widget to put into my QLayout with stratch items (to align inner (my) widget to right and to bottom sides)
my code prototype is here:
QDesktopWidget * desktopWidget = QApplication::desktop();
MyWidget * myWidget = new MyWidget(desktopWidget);
QVBoxLayout * vlayout = new QVBoxLayout;
vlayout->addStretch();
vlayout->addWidget(myWidget);
QHBoxLayout * hlayout = new QHBoxLayout(desktopWidget);
hlayout->addStretch();
hlayout->addLayout(vlayout);
but it doesn't work...
Help me please implement my idea if you know how.
At this moment I know only one work way to do it - it is manually set pos of widget and handle many events (resize etc.) - but this is not good... (because i do it bad of cause ;-)
)
PS: idea with qlayout inside other widget is working for example with QTextBrowser with sandclock at certer of view, etc.
A QDesktopWidget isn't intended to be used like a typical widget (at least as far as I'm aware, I'm surprised the documentation isn't more explicit about that). So you shouldn't try to parent widgets to it or try to assign it a layout. You call its methods to obtain information about the desktop environment or connect to its signals to be informed of changes.
Using this information, you would then set the geometry of your own application widgets so that they appear on the correct screen and position you want.
This page shows some basic functionality.

Resources