I need to create a scrollable, owner-drawn widget that behaves a lot like QPlainTextEdit with word-wrapped text, in the sense that the height depends on the width - as the content width decreases, the content height increases.
What is the best approach to do it? I was thinking about putting my QWidget-derived class inside a QScrollArea, but QPlainTextEdit is derived from QScrollArea instead, should I go that route?
Also, I want to paint only the visible area in paintEvent(), it would be wasteful otherwise.
Right now I'm examining the code of QPlainTextEdit, but it is rather complex and not easy to read, so if anyone knows of a code example that's simpler on the web, you can give me a link, it would help a lot.
I'll post the solution I came up with. It's not the best, but it mostly works.
I did not derive from QAbstractScrollArea in the end, instead I simply embedded my widget in a QScrollArea with a vertical layout, which worked well-enough.
I implemented resizeEvent() (I saw this from QPlainTextEdit implementation), and each time the width changes, I recalculate the height, and I set the widget's minimum height to that. I set the minimum height because of how the layout works.
void MyWidget::resizeEvent(QResizeEvent *e)
{
// If the widget's width has changed, we recalculate the new height
// of our widget.
if (e->size().width() == e->oldSize().width()) {
return;
}
setMinimumHeight(calculateHeightFromWidth(e->size().width()));
}
For drawing only the visible area see Get visible area of QPainter
Related
I have a custom QDialog comprised of a QStackedWidget with QScrollArea widgets for each page of the stacked widget.
I want to set the size hint for the QDialog such that the dialog is just large enough that the scroll bars for the scroll area are not visible when the dialog is first shown (i.e. ensure size of QScrollArea viewport = size hint of child widget in scroll area). Currently, the default sizeHint() implementation for the QDialog has insufficient height, which causes the vertical scroll bar to be shown when first loaded.
I thought this could be achieved by re-implementing sizeHint() for the QDialog, whereby the size hint of the dialog would be adjusted by the amount required for the size of QScrollArea viewport to equal the size hint for child widget in the scroll area (for the first page of the stacked layout). Unfortunately, in sizeHint(), the size of the QScrollArea viewport is set to the default size of QStackedWidget (640x480), and only updates to the correct size once the QDialog is shown.
Is there some way to get the correct size of the QScrollArea viewport before it is shown, or another way to achieve the desired effect of adjusting the size hint of the dialog to prevent scroll bars from being shown when it is first displayed (aside from hard-coding the dialog size).
With the composition of your dialog as:
I have a custom QDialog comprised of a QStackedWidget with QScrollArea
widgets for each page of the stacked widget.
The tricky part is to answer:
Is there some way to get the correct size of the QScrollArea viewport
before it is shown?
Well, before switching to certain page you can estimate the scroll area viewport if it is either correctly set or you can just measure the content going inside the scrollarea. I usually force the widget to demand certain height from the scroll area like that:
wdgetInScrollArea->setMinimumSize( widgetInScrollArea->sizeHint() );
wdgetInScrollArea->adjustSize(); // sometimes it is needed
The the scroll area viewport hint is then more 'adequate':
qDebug() << scrollArea->viewPortSizeHint(); // report
I don't see the code but usually it is not even required to do any custom event handling here, just prepare all the nested widgets like that.
I've had a real challenge getting QScrollArea to take the minimum space possible up to a maximum height.
My GUI model is as follows: A QScrollArea contains a vertical layout which is populated with a widget of class TableRow. I want this class TableRow to take up the minimum height possible. It has a widget at the top which is always visible, and a QScrollArea below which has a label inside it whose visibility can be toggled. The label is for notes which may be 0 characters or may be infinite in length (hardware limitations aside).
I've found that for a label in class TableRow setting the vertical sizePolicy to Fixed will actually take up exactly how much it needs to fit all the contents (see: Qt Layout, resize to minimum after widget size changes). However this doesn't appear to work with QScrollArea. In fact every sizePolicy I've tried keeps the QScrollArea at a fixed height; except for Ignore, but then the QScrollArea goes to a height of 0, regardless of its contents.
I've created a git branch producing a simplified version of this problem.
Here is the result of applying a fixed vertical sizePolicy:
What I'm expecting from this test case:
The first widget's height should be almost 30px (the height of the upper widget) only showing the borders for the QLabel and QScrollArea
The second widget's height should be shorter than 130px (the maximum height of the QScrollArea being 100px) but large enough to show the label without scrolling
The third widget's height should be 130px, and the scrollbar should appear (this part is correct in every case I've tried except for when the vertical sizePolicy is set to Ignored )
I understand I may need to override some things to make this work, as by itself it's not obvious why a QScrollArea's height might be dependent on its child widgets (which is probably why it was not designed to make this easy, or at least it seems like it wasn't).
However, I think the case I'm trying to make is common enough, and my current approach is justifiable. If there's another/better way to make an individual widget scroll after it reaches a maximum height I'm open to that as an answer, provided it meets the three conditions I'm expecting.
This feels more like a hack than a solution, but it does work for me, at least in the short term. Because the text for lblNotes does not change runtime, I was able to add the following code in the constructor of my TableRow widget:
// Hack to resize QScrollAreas
ui->lblNotes->adjustSize(); // Otherwise lblNotes will think its height is still ~0px
int height = ui->lblNotes->height() + 12; // Borders and margins add up to 12px
if (height > 100) { height = 100; } // Cap the height at desired maximum value
ui->scrollArea->setFixedHeight(height);
Should I have to deal with the case of dynamically set text, I could wrap this in a function to be called anytime the text of lblNotes is set.
I'm still open to solutions that involve using the layout features Qt has natively as I believe that would be preferred if a solution exists. Some QScrollArea contents may not be as straight-forward to determine the height from in the future.
Ok, so I'd like to display an image with qt where the image resizes with the browser and maintains the aspect ratio while also remaining centered in the window. I can get the resizing with aspect ratio to work correctly, but when I align it with Qt::AlignCenter, the qwidget no longer resizes (remains a fixed size). So basically, I can get either option to work but not together.
A good example of what I'm trying to do would be the imshow() function in matlab. This resizes the image while maintaining the aspect ratio and also centering the image in the window. The code I have is soemthing like this:
void roilayout::resizeEvent(QResizeEvent *event)
{
QSize p(roiview->refimage->size());
p.scale(roiview->view->size(), Qt::KeepAspectRatio);
roiview->view->resize(p);
}
and in the constructor:
roiview = new roiwindow;
roiview->view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
roiview->view->setCursor(Qt::CrossCursor);
roiview->view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
roiview->view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QHBoxLayout *layoutContainer = new QHBoxLayout;
layoutContainer->addWidget(roiview->view);
setLayout(layoutContainer);
I searched google and couldnt find anything. Also asked a similar question a little while back but from the answers it appears I didn't ask the question clearly enough. Thanks.
A couple of things: First it would be helpful to know what type of control "view" is.
Also, I don't think you should need to resize the child control "view" (whatever type it is) within the parent's resizeEvent() callback.
A better solution might be to set the sizeHint policy on the child widget to automatically expand.
I am having a problem redrawing a QWidget window after its size has been adjusted. I have tried update(), repaint(), adjustSize(), but all seem to suffer from the same thing: only part of the window is redrawn, resulting in the window frame on the bottom and right sides to not show. The window is also not resized entirely.
Just in case it makes a difference, the window is in a QMdiArea.
Thanks.
// ... some subwidget resizing and moving.
calibrationWindowUIs[activeWindow].layoutWidget2->move(QPoint(oldXLeft, 30 + height + 21));
calibrationWindowUIs[activeWindow].layoutWidget1->move(QPoint(oldXRight, 30 + height + 21));
// Set window size.
calibrationWindows[activeWindow]->setMinimumSize(calibrationWindowUIs[activeWindow].tabWidget->geometry().width() + 40, calibrationWindowUIs[activeWindow].tabWidget->geometry().height() + 40);
calibrationWindows[activeWindow]->update();
Note: I'm new to Qt; perhaps I'm doing something wrong with layouts?
Edit: I may have not given enough information. Alright, to be quite honest, I still have to delve deeper into layouts and related material. What I had tried to do here was to use Qt Designer in order to design the window. I've done what perhaps amounts to a stupid mistake: I didn't use an overall parent layout for the entire window, but hacked it with a couple of smaller layouts that I therefore have to move about and resize individually. See the Qt Designer screen (the red rectangles are the sole layouts): .
What is happening is that in the frame to the right, I am playing a video clip that can be of various resolutions. I want the frame to resize depending on this resolution, which also means that the buttons and window have to move/resize accordingly. That is where the window resize comes in. I'm sure there is a more elegant solution than what I am doing here, but I am trying to handle several other scenarios here and hence the lack of quality of code.
The result is that when I load a clip, the window attempts to resize, but does so badly; the following is the result:
If the window is dragged, it 'pops' into its correct size; in the meantime, however, it just looks ugly.
A couple further questions: do you use the Qt Designer to design your UIs? I found that programmatically you can achieve much better control of your interfaces. One thing which I could not do in the designer was to have a layout parented by the main widget, i.e. the equivalent of having the following bit of code:
QVBoxLayout* layout = new QVBoxLayout;
this->setLayout(layout);
A layout placed in the designer always seems to create this 'layoutWidget' subwidget, which the layout you placed is then parented to. Any way around that?
We use a mix of designer and code to create layouts, the Qt layout system can be very unintuitive at times. But I would probably not layout a full series of tabs in one designer ui file, i would make each tab each own widget and then assemble them either through code or in the designer by promoting to custom classes. This gives you better separation of responsibilities, by putting all the functionality of all the tabs into one file you almost guarantee a large unwieldy class.
When a widget has child widgets in designer you can assign a layout to it by adding it from the context menu. Make sure nothing is selected and click on the background of the widget in which you want to create a layout, select the layout and all of the widgets children will be assigned the layout.
What does help is creating hierarchies of layouts. Looking at your first screenshot, i would probably use a vertical layout with spacers on top and bottom for the items on the right, an horizontal layout with spacers left and right for the button bar and a grid layout for all the items together. Without the spacers your items will extend when the window grows. The spacers will let you control the behavior under resizing better.
you are calling setMinimumSize(). That's fine, but you should also call resize()
I am trying to show a picture in it's full view using QGraphicsScene. But when ever I put the QgraphicsScene inside the QGraphicsView, I am getting a scroll bar. I tried so many ways But all are went to veins. So can anybody tell me how to obtain the full view without the scrollbar.
You might be getting scrollbars because the scene is larger than the usable area within the graphics view. By default, a QGraphicsView comes with a 1-pixel margin. To fix this, you can try:
QRect rcontent = graphicsView.contentsRect();
graphicsView.setSceneRect(0, 0, rcontent.width(), rcontent.height());
I had been getting scrollbars because I was manually setting the scene rect to the size of the graphics item I was adding -- which was as large as the QGraphicsView widget. I wasn't taking into account the margin.
QGraphicsView v;
v.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
v.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
To adjust the scrolling programmatically once these have been hidden, use one of the overloads of v.ensureVisible().