Qt QStackedWidget Resizing Issue - qt

I have a simple QStackedWidget with 3 different QWidgets in it. The minimum sizes of the QWidgets are (350x200), (200x100), and (450x450).
So the problem I'm having is when I resize the QStackedWidget, it has a minimum size of the largest QWidget within it. So if I switch to the second QWidget, (which is the QWidget with the size of (200x100)), the QStackedWidget will only size down (450x450) because of the largest QWidget inside of it. I would like it to size down to fit the current QWidget being displayed, and remove that minimum size.

I think that the most straightforward solution is to subclass QStackedWidget and override sizeHint and minimumSizeHint to return the hint for the current widget instead of the maximum of all widgets. For example:
class StackedWidget : public QStackedWidget
{
QSize sizeHint() const override
{
return currentWidget()->sizeHint();
}
QSize minimumSizeHint() const override
{
return currentWidget()->minimumSizeHint();
}
};

Good question. Unfortunately Qt doesnt provide automatic mechanisms for sizing down depending on child widgets (By automatic I mean you don't have to do anything). Most of the focus is on expanding (see the size policies)
You have two options :
Use the signal and slots mechanism in the class which create those widgets. You will need to listen to the signal void QStackedWidget ::currentChanged ( int index ) and resize the stackedwidget to the size of the widget at index. This is quite fast to code.
Decorate QStackedWidget and define the size properties. Basically both sizeHint() and minimumSizeHint() should return the size of the current widget. Addwidget(QWidget*) also need to be modified. Useful if you are using stacked widgets everywhere.

step 1: overload method:
void resizeEvent(QResizeEvent*)
step 2: call 'resize' and 'select page' :
QRect rect;
rect.setBottomRight(stackedWidget->geometry().bottomRight());
currentWidget->setGeometry(rect);

An alternate solution is to not use stacked widget at all and instead make one Widget for each page and then use the setVisible(bool) to show and hide the pages. That way you get the resizing behavior without having to make custom widgets.

Related

Height of editor in QItemDelegate

I have QListView with custom implementation of QItemDelegate. MyItemDelegate reimplements createEditor() to show custom widget. Size of widget is dependant on content.
By default, each row height is about 20px (one row), but my editor has bigger height.
I was trying to override QItemDelegate::sizeHint() method, but is doesn't contains reference to editor, so I couldn't calculate correct size.
How can I make QListView resize rows to actual size of editor?
You should emit layoutChanged after creating an editor, if you could not override sizeHint correctly. But it should be enough to override sizeHint.

QScrollArea: auto-scroll to newly added widget

it's not the first time that I want a scroll area which behaves like the following (imagine a log or chat window, but too complex to use a simple QTextBrowser):
I want to add multiple widgets, which appear one below the other (like when placed in a QVBoxLayout)
Each widget within this layout should have a fixed size or a height-for-width (like a simple label)
The scroll area should auto-scroll to the most recently added one
(optional) When there is space left (scroll bar not yet enabled), the contents should be aligned to bottom
Using QScrollArea:
My attempt in the past was using a QScrollArea using a QVBoxLayout inside. But this seems to be not as simple as I thought: Whenever I add a widget to the layout, the layout doesn't resize the scroll area content widget immediately, resulting in a delayed adjustment of the contents. For one short moment, the widgets contained in the layout are resized so that the total size equals the total size before the add operation, resulting in a too small size per widget. Also, scrolling to the newly added widget is thus not possible until the layout corrected its size to the new total size of widgets, so even a QTimer::singleShot(0, ...) doesn't help here. Even with a timeout of 20 or so, there are situations in which the layout needs more time to resize. It's not deterministic, and thus far away from a nice solution.
In order to get the bottom alignment behaviour, I initially place a spacer item in the layout. It won't require any space as soon as there is no space left and scrolling gets enabled.
Using QListView:
As my items are too complex, they need to be QWidgets. They can't have the focus, aren't selectable, so an item-based solution seems to be just "the wrong way". Also, this solution sounds too "heavy".
I just can't believe that there is no easy way, so I think I just haven't seen it yet!
QListView should be fine. You claim that your items are static, there's no interaction with them: no focus, no selection. It'd seem that a QWidget is an overkill for such items. You only need something that has a fixed size and can draw itself. That is precisely what delegates in the Qt's model-view system are for. Just implement one or more QAbstractItemDelegates for your items, and provide an implementation of a model for the data they will be rendering. The QAbstractItemView is is already a QAbstractScrollArea!
If you want to paint HTML within a delegate, it's easy to do -- again, QWidget is an overkill for a static display! There is a very food reason why it's "hard" to use QWidget for this -- the API guides you to the correct solution. Assuming your model contains html for each item, here's how you can paint it. You can go fancy with the sizeHint, of course, and should be caching the text document, ideally storing it in the model I'd think.
void MyDelegate::paint(QPainter* p, const QStyleOptionViewItem & opt, const QModelIndex & index) const
{
QTextDocument doc;
doc.setHtml(index.data().toString());
doc.drawContents(p, QRect(QPoint(0,0), sizeHint(opt, index)));
}
QSize MyDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize(100, 200);
}

Resizing qt widgets when their children are hidden

How would I go about resizing the widget when the retry child is hidden so that it looks as in the first image? The main layout is a QVBoxLayout, the retry child is a widget with a QVBoxLayout as well.
I've tried the following:
update()
updateGeometry()
setGeometry(childrenRect())
layout()->activate()
on the main widget as soon as I've set the retry widget to hidden. Do I need to intercept some event to do this?
The adjustSize function may do what you want.
Here is a basic example which does auto-resize upon widget hide/show.
dialog.h file:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QtGui>
class dialog : public QDialog
{
Q_OBJECT
public:
explicit dialog(QWidget *parent = 0)
{
vlayout.addWidget(&checkbox);
vlayout.addWidget(&label);
label.setText("Label");
setLayout(&vlayout);
this->layout()->setSizeConstraint(QLayout::SetFixedSize); // !!! This is the what makes it auto-resize
checkbox.setChecked(true);
connect(&checkbox,SIGNAL(toggled(bool)),&label,SLOT(setVisible(bool)));
}
private:
QVBoxLayout vlayout;
QCheckBox checkbox;
QLabel label;
};
#endif // DIALOG_H
and main.c
#include <QApplication>
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
dialog d;
d.show();
return a.exec();
}
My problem was a little different in that I had a top-level QWidget (instead of a QDialog or a QMainWindow) that had children within a layout. When those children were resizing to larger, the QWidget would resize to accommodate, but not when they became SMALLER. Much like above, the area just had empty space. I finally found a solution:
The layout used in my QWidget had a property called setSizeConstraint(). This is normally set to "QLayout::SetDefaultConstraint", which simply affects the "minimumSize" of the QWidget that owns it. Instead, I chose "QLayout::SetFixedSize" which made the layout adjust the fixed size of the owning QWidget, effectively making it SMALLER when it needed to be.
This is one I have just developed an answer for!!! ( Note: this will be given in PyQt4 form )
I have needed the ability to re-size my app window when showing and hiding extra panels.
However in the past I ended up just reconfiguring the UI and/or work flow so that re-sizing the window wasn't necessary. However now it is a needed must.
The window can be re-sized during its creation. The window can be re-sized directly (e.g. self.resize( xSize, ySize )
However if you create some lines of code that capture the needed sizes, hides the desired section, recalculates what the size should be, the re-size seems to be not take at all.
(example)
curHgt = self.size().height()
curWth = self.size().width()
myFrmHgt = self.ui.reviewFrm.size().height()
self.ui.reviewFrm.hide()
self.resize( curWth, (curHgt - myFrmHgt ) )
Nothing. Doesn't work. Why not?
It today's hunt for an answer still did not come up with a viable answer. There are several offerings for options. Hunt up the widget hierarchy, invalidate, adjustSize. A rather exhaustive layout shaking that just will not work if you are attempting to re-size the the main window. There was also an overwhelming response of saying to set your constraint to FixedSize. But that rather defeats the entire purpose, kinda like saying to cut your hand off at the wrist if you have a hang nail.
However I did stumble across someone that had some success with digging in and discovering that the reason the re-size isn't taking hold is because of the queue of Posted Events that will reset the resize.
His solution was to run the QApplication.sendPostedEvents() just before doing your application re-size, so that the queued events don't reset your window.
Yes, and No.
Yes, this is the solution, and I am sure there is a prettier way of doing it, but you need to really shake the the queue out like a washed paint roller first.
Here is my simple snippet that I set up that so far seems to be solid.
result = [ self.size().width(), self.size().height() ]
if self.ui.reviewFrm.isVisible():
result[1] -= self.ui.reviewFrm.size().height()
self.ui.reviewFrm.hide()
while self.size().height() > result[1]:
self.appy.sendPostedEvents()
self.resize( result[0], result[1] )
Nope. It ain't pretty. However at most I have only seen it shake the paint roller twice before the Posted Events allowed for the window to resize.
No need to lock constraints or shake the parent widgets or climb through the hierarchy looking for size hints.
If you want your widget to be resizable after setting QLayout::SetFixedSize you will also need to override QWidget::resizeEvent().
void resizeEvent(QResizeEvent* event) override {
QWidget::resizeEvent(event);
layout()->setSizeConstraint(QLayout::SetMinAndMaxSize);
};

How does QWidget size when used as a window?

Several examples on trolltech use QWidget rather than a QMainWindow as the main window for simple Qt applications.
Now I need just a simple window with a single content widget (QGlWidget) that covers the entire client area.
However, when I show a QWidget window with a single content window it automatically resizes itself to be tiny.
If I create the QWidget parent window without a child It is a nice large default size.
I don't want to resort to using Layouts for a single child window :/
What I understand is that you use a QWidget to display your QGIWidget. Try calling the show method of your QGIWidget directly (if your QGIWidget class inherits QWidget), Qt will create the window decoration for you.
Otherwise if you really need your widget to be inside one another, and fit its size, you'll have to use a layout.
Either follow gregseth's advice or you can simply resize the widget yourself.
(though this way you'll loose nice auto-resizing which is provided by Qt when you use layouts)
In your case you can basically do something like:
QGlWidget* myWidget = new QGlWidget;
myWidget->resize(QApplication::desktopWidget()->availableGeometry().size());
// or maybe instead of resizing show it in fullscreen:
myWidget->showFullScreen();
(actually I don't remember if showFullScreen() will do resizing for you, maybe you'll need both resize+showFullScreen :))
Cheers :)
P.S. Using layout is actually an option. It's not expensive and it's flexible. All it gets: "layout = new QVBoxLayout(myWidget); layout->addWidget(myWidget);" and you're done :)
Not always.
I've found that a QMainWindow will not work as the parent widget when using the QVBoxLayout and QHBoxLayout to arrange child widgets.
If you create a QWidget and use that in place of the QMainWindow then the layouts will work correctly.
QWidget* centralWidget = new QWidget( MainWindow );
MainWindow->setCentralWidget( centralWidget );
If you use QtCreator and look at the code it creates you can see it creating a 'hidden' widget if you try to use the layouts directly at the top level.
It's not obvious, intuitive, or documented anywhere that I've found.

QT4: Is it possible to make a QListView scroll smoothly?

I have a QListView in Icon mode with lots of icons, so that a scrollbar appears, but the scrolling is not smooth and this IMHO confuses the user since it jumps abruptly from one point to another at each scroll. I would like to make the scrolling smooth but I didn't find anything in the docs. Is it possible?
Maybe QListView.setVerticalScrollMode(QAbstractItemView::ScrollPerPixel)
If I understand your question correctly you would like to redefine the scrolling behavior of the widget. I guess what happens is that listview is getting scrolled by the item's height whenever users hits a scroll arrow (marked as b on the image below).
For a vertical scroll bar connected to a list view, scroll arrows typically move the current position one "line" up or down, and adjust the position of the slider by a small amount. I believe line in this case it is an icon's height. You can adjust items height by installing and item delegate (setItemDelegate) and overriding its sizeHint method. Though this would not help you to solve this problem. What you could try is to create a QListView descendant and override its updateGeometries method. There you can setup the vertical scrollbar step to the value you want, I guess 1 or 2 for this task. Below is an example of the custom listview:
class TestListView : public QListView
{
Q_OBJECT
public:
explicit TestListView(QWidget *parent = 0);
protected:
virtual void updateGeometries();
};
TestListView::TestListView(QWidget *parent) :
QListView(parent)
{
//???
}
void TestListView::updateGeometries()
{
QListView::updateGeometries();
verticalScrollBar()->setSingleStep(2);
}
hope this helps, regards
I have a QlistWidget* in ui->barra_scroll and I feel very smooth with this.
QScrollBar *qsb = ui->barra_scroll->verticalScrollBar();
qsb->setSingleStep(5);

Resources