How to resize qt dock widget to fit qCustomPlot widget? - qt

A qDockWidget containing a qCustomPlot always starts with zero height. I am able to catch the qDockWidget resize event and change the qCustomPlot geometry using these answers, but the qCustomPlot is always hidden until it is manually stretched. Should this happen automatically, or do I need to calculate and set the dock height at startup?
This sample code creates a new qCustomPlot widget, places it in a layout, places that layout in another widget, and sets it to the dock. I have also tried placing the qCustomPlot widget directly into the dock. qCustomPlot setGeometry, setMinimumSize, and setSizePolicy seem to have no effect on the dock height.
#include <QMainWindow>
#include "qcustomplot.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(){
QCustomPlot *m_customPlot;
QDockWidget *dock;
resize(1200, 600);
//create the plot
QWidget *plot_frame_temp= new QWidget();
plot_frame_temp->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding);
m_customPlot = new QCustomPlot(plot_frame_temp);
m_customPlot->axisRect()->setupFullAxesBox(true);
m_customPlot->setBackground(Qt::black);
//size and margin settings
m_customPlot->setGeometry(QRect(0, 0, 500, 400));
m_customPlot->axisRect()->setMinimumSize(500,400);
m_customPlot->axisRect()->setAutoMargins(QCP::msLeft|QCP::msBottom|QCP::msRight|QCP::msTop);
// zoom and drag only on horrizontal axis
m_customPlot->axisRect()->setRangeZoomAxes(m_customPlot->xAxis,nullptr);
m_customPlot->axisRect()->setRangeDragAxes(m_customPlot->xAxis,nullptr);
//setup the top axis and labels
m_customPlot->xAxis->setVisible(false);
m_customPlot->xAxis2->setVisible(true);
m_customPlot->xAxis2->setTicks(true);
m_customPlot->xAxis2->setTickLabels(true);
m_customPlot->xAxis2->setTickPen(QColor(136, 136, 136));
m_customPlot->xAxis2->setTickLength(0,10);
m_customPlot->xAxis2->setTickLabelColor(QColor(136, 136, 136));
m_customPlot->xAxis2->setSubTickPen(Qt::NoPen);
m_customPlot->xAxis2->setBasePen(Qt::NoPen);
QFont font;
font.setStyleStrategy(QFont::PreferOutline);
m_customPlot->xAxis2->setTickLabelFont(font);
//setup the left axis and hide
m_customPlot->yAxis->setVisible(false);
m_customPlot->yAxis->setRangeReversed(true);
m_customPlot->yAxis2->setVisible(false);
//first graph
m_customPlot->addGraph();
m_customPlot->graph()->setPen(QPen(QColor(165, 165, 165)));
m_customPlot->graph()->setLineStyle((QCPGraph::lsStepLeft));
//second graph
m_customPlot->addGraph();
m_customPlot->graph()->setPen(QPen(QColor(165, 165, 165)));
m_customPlot->graph()->setLineStyle((QCPGraph::lsStepLeft));
// make some data
QVector<double> x(500), y0(500), y1(500);
for (int i=0; i<500; ++i)
{
x[i] = i;
y0[i] = (rand() % 2 + 0.2)/2;
y1[i] = (rand() % 2 + 1.4)/2;
}
//add data to graph
m_customPlot->graph(0)->setData(x, y0);
m_customPlot->graph(1)->setData(x, y1);
// set some options
m_customPlot->setNotAntialiasedElements(QCP::aeAll);
m_customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
//add plot widget to layout
QHBoxLayout *laLayout = new QHBoxLayout();
laLayout->addWidget(plot_frame_temp);
//add layout to another widget
QWidget *laWidget=new QWidget();
laWidget->setLayout(laLayout);
//add second widget to dock
dock = new QDockWidget(tr("qcustomplot"), this);
dock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
dock->setWidget(laWidget);
addDockWidget(Qt::BottomDockWidgetArea, dock);
QWidget *centralWidget=new QWidget();
setCentralWidget(centralWidget);
}
};
Here is a minimum example done directly in mainwindow.h. A dock with a central widget and a QDockWidget with a qCustomPlot. I am using the compiled DLL version of qCustomPlot
Image: dock starts with zero height:
Initially the dock looks like this. The plot is hidden and the dock is claiming no height in an otherwise empty layout.
Image: dock stretched to show the plot
The plot is visible when the user stretches the dock.
I strongly suspect there is a way for the dock to adjust to the height of the qCustomPlot automatically. I can set the dock height from code, but that seems like a hack.

The most direct approach to solve your issue might be to define a minimum size for your QCustomPlot Widget. This can easily achieved with the following reduced example. Actually, the problems has nothing to do with QCustomPlot at all. It could have been any kind of widget with minimum size (0,0).
#include <QMainWindow>
#include "qcustomplot.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(){
auto m_customPlot = new QCustomPlot();
m_customPlot->axisRect()->setupFullAxesBox(true);
auto dock = new QDockWidget(tr("qcustomplot"), this);
dock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
dock->setWidget(m_customPlot);
m_customPlot->setMinimumSize(QSize(500,500));
dock->setMinimumSize(m_customPlot->minimumSize());
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, dock);
setCentralWidget(new QWidget);
}
};
A better solution might to save and to restore the geometry of your dock widget configuration in the registry. This leaves the user with just the dock widget configuration that he finds desirable.
void MainWindow::closeEvent(QCloseEvent *event)
{
QSettings settings("MyCompany", "MyApp");
settings.setValue("geometry", saveGeometry());
QMainWindow::closeEvent(event);
}
QSettings settings("MyCompany", "MyApp");
dock->restoreGeometry(settings.value("myWidget/geometry").toByteArray());

Related

Qt size policy and stretch factors

How do the options size policy and stretch factors influence the size of a widget?
The image below shows previews of three differently arranged windows. For all three windows (W1-W3) the widget on the right is a QFrame widget with the horizontal and vertical size policies set to Preferred (this is the default setting). The horizontal stretch factor is set to 2. The widget on the left is a QListView widget which also has the size policies set to Preferred (by default this would be Expanding) and the horizontal stretch factor set to 1.
The three windows differ in the way the two widget are layout against each other.
(W1) The window on the left in the image above has a central widget set to a horizontal layout resulting in a size ratio of 2/3 of the widgets that I would expect because of the stretch factors set to 2 and 1.
(W2) The two widget are "connected" with each other through a QSplitter widget. The central widget is set to t horizontal layout. The results of the size of the widgets differ from W1 and are not in the ratio of 2/3.
(W3) The right window "connects" the two widget also with a QSplitter like in window W2. Howevert, The QListView widget is child to a QVBoxLayout. So the QSplitter has the QFrame and a QVBoxLayout as its children.
In detail the setup for the three different windows is shown in the image below:
I have the following questions:
Why do the ratios of the two widgets differ between W1, W2 and W3?
The ratio in W2 seems to be affected by the stretch factors, however not with the results expected. But W3 does not seem to be influenced by the stretch factors at all. Why is that the case?
The behavior of the splitter with relation to stretch factors is documented to be different than the behavior of a layout: you shouldn't expect them to look the same.
The W2 & W3 should look identical if implemented as you claim. Your ui file has a mistake in it.
Here's a test case that doesn't use a .ui file:
// https://github.com/KubaO/stackoverflown/tree/master/questions/layout-stretch-triad-37680657
#include <QtWidgets>
struct Window : public QMainWindow {
QWidget central;
QHBoxLayout layout{&central};
QListView view;
QFrame frame;
Window(const QString & title) {
setWindowTitle(title);
setCentralWidget(&central);
view.resize(300, 300);
view.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
frame.resize(300, 300);
frame.setLineWidth(3);
frame.setFrameStyle(QFrame::Box);
resize(500, 200);
show();
}
};
struct Window1 : Window {
Window1() : Window("W1") {
layout.addWidget(&view, 1);
layout.addWidget(&frame, 2);
}
};
struct Window2 : Window {
QSplitter splitter;
Window2() : Window("W2") {
layout.addWidget(&splitter);
splitter.addWidget(&view);
splitter.addWidget(&frame);
splitter.setStretchFactor(0, 1);
splitter.setStretchFactor(1, 2);
}
~Window2() { frame.setParent(0); view.setParent(0); }
};
struct Window3 : Window {
QSplitter splitter;
QWidget leftWidget;
QVBoxLayout leftLayout{&leftWidget};
Window3() : Window("W3") {
layout.addWidget(&splitter);
splitter.addWidget(&leftWidget);
splitter.addWidget(&frame);
splitter.setStretchFactor(0, 1);
splitter.setStretchFactor(1, 2);
leftLayout.setMargin(0);
leftLayout.addWidget(&view);
}
~Window3() { frame.setParent(0); view.setParent(0); }
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Window1 w1;
Window2 w2;
Window3 w3;
w2.move(w1.pos() + QPoint(0, 75));
w3.move(w2.pos() + QPoint(0, 75));
return app.exec();
}

Different alignment of widgets in QGridLayout

The following code (Qt 5, same behaviour with Qt 4.8) uses a QGridLayout to add widgets above and to the left of a QScrollArea (to serve as a kind of header later):
#include <QApplication>
#include <QMainWindow>
#include <QGridLayout>
#include <QScrollArea>
class ColoredWidget : public QWidget {
public:
ColoredWidget(const QColor& color, QWidget* parent) : QWidget(parent) {
QPalette pal;
QBrush brush(color);
brush.setStyle(Qt::SolidPattern);
pal.setBrush(QPalette::Active, QPalette::Window, brush);
setPalette(pal);
setAutoFillBackground(true);
}
};
class MainWindow : public QMainWindow {
public:
MainWindow(QWidget* parent) : QMainWindow(parent) {
resize(300, 400);
QWidget* centralwidget = new ColoredWidget(QColor(0xff, 0xf0, 0xb5), this);
QGridLayout* layout = new QGridLayout();
centralwidget->setLayout(layout);
setCentralWidget(centralwidget);
// create widget with fixed height of 20 px and maximum width of 200
QWidget* topHeader = new ColoredWidget(Qt::green, centralwidget);
topHeader->setMaximumWidth(200);
topHeader->setFixedHeight(20);
// create widget with fixed width of 20 px and maximum height of 200
QWidget* leftHeader = new ColoredWidget(Qt::blue, centralwidget);
leftHeader->setFixedWidth(20);
leftHeader->setMaximumHeight(200);
// create scroll area as main widget
QWidget* view = new QScrollArea();
layout->addWidget(topHeader, 0, 1);
layout->addWidget(leftHeader, 1, 0);
// layout->addWidget(leftHeader, 1, 0, Qt::AlignTop); // widget not displayed at all!
layout->addWidget(view, 1, 1);
}
};
int main(int argc, char ** argv) {
QApplication app( argc, argv );
MainWindow win(0);
win.show();
return app.exec();
}
The QGridLayout documentation says that "Columns and rows behave identically", thus I would expect that the two widgets are layouted the same way.
However, the left one is automatically vertically centered, while the top widget is aligned to the left within the cell (and not centered horizontally). I would like to have the left widget also at a fixed position, means aligned to the top:
Which property causes this different behaviour between the two widgets, one beeing centered, the other being at a fixed position?
How can I influence the alignment (I can probably add a flexible spacer to fix it, but ideally I would like to avoid that)? I tried with Qt::AlignTop, but that made the blue widget disappear.
Althought this question is pretty old, I will add my answer for all those that come here afterwards, like me.
Setting the alignment either with
layout->setAlignment(leftHeader, Qt::AlignHCenter | Qt::AlignTop);
or with
layout->addWidget(leftHeader, 1, 0, Qt::AlignTop);
is intuitiv and seems to be the right way. If it is setup completly it is also working as expected.
But why does the Widget vanish then?
Long story short: because leftHeader has a size of 0.
In my pretty similar situation I've setfixedHeight() and the widget reappeared, this should also work by setting a value for minimalHeight() for example.
Long Story - Details
I've put myself in position of gridLayout and tried to determine the size of leftHeader:
Outside of the layout leftHeader got a fixed width of 20 - ok, but no given height. With the defaults of QWidget this leads to a default-height of 0.
Documentation of void QGridLayout::addWidget() and void QGridLayout::addLayout() states:
... The alignment is specified by alignment. The default alignment is
0, which means that the widget fills the entire cell. ...
source
Hence for default alignment of 0 the height of leftHeader is obviously set as height of its row.
But in case a different Qt::Alignment is used, as in the question (Qt::AlignTop ), it is not that simple.
From my point of view the layout is unable to determine the correct height without further settings from the designer.
Having a quick look at the implementation of void QGridLayout::addWidget(), we can detect that a QLayoutItem(as part of QGridBox) is created inside the QGridLayout for each added widget resp. Layout.
So it seems that the QGridLayout relies on default layout mechanisms of Qt based on the settings from the added widget/Layout (like minSize, maxSize, BaseSize, Size Increment, etc ).
In combination with 1.) this implies a height of 0 for leftHeader and therefore nothing to draw resp. it seems that the widget has vanished.
Although not a minimal solution you could probably fix the alignments by adding a QHBoxLayout in the cell for the green widget and a QVBoxLayout in the cell for the blue widget. Add the green widget to the QHBoxLayout and the blue widget to the QVBoxLayout. Then apply addStretch(n) to both the QHBoxLayout and QVBoxLayout. Maybe this is what you already mentioned you could do and then we are at the same level of knowledge concerning this. I believe this is the way I have solved these kind of issues and I haven't spent more time on it.
Try this:
setAlignment(Qt::AlignHCenter | Qt::AlignTop );

Qt::Pixmap in Qwidget doesn't show up in MainWindow

Here is another newbie to Qt.
What I need to do is to have a scrollable Area in the center of MainWindow, which displays images, and allows user to paint on the image.
Since I cannot add a QPixmap directly to a scrollable Area, I tried to create a subclass of QWidget, like below:
class Canvas: public QWidget
{
public:
Canvas(){
image = new QPixmap(480,320);
image->fill(Qt::red);
}
QPixmap *image;
};
Then I declared Canvas *c in the header file.
In the implementation, I wrote:
canvas = new Canvas;
setCentralWidget(canvas);
However, apparently this does not help to show up the QPixmap. I do not know what to do.
You don't need to subclass QWidget for this. QPixmap is not a widget, so it is not shown anywhere. You need to add your pixmap to some widget, this will work:
in header:
QLabel* imageLabel;
in cpp:
imageLabel = new QLabel(this);
QPixmap image(480,320);
image.fill(Qt::red);
imageLabel->setPixmap(image);
setCentralWidget(imageLabel);

QDockWidget sized wrong when docked on right side of Main Window

I am new to Qt and I am trying to create a DockWidget that docks on the right of the window. I set a maximum and minimum width for the dock (as you will see in the code below). This works if the dock widget is added with Qt::LeftDockWidgetArea, but when it is added with Qt::RightDockWidgetArea, The dock is "padded" out to the center of the window, like this:
I am probably not sizing the dock in the correct way.. Here is the code for this window:
int main(int argv, char** args)
{
QApplication app(argv, args);
QMainWindow window;
QDesktopWidget* desktop = QApplication::desktop();
//Docks
QDockWidget* propertyDock = new QDockWidget("",&window);
QWidget* propertyDockContents = new QWidget;
//This sets the window in the center of the screen.
int wWidth = 800; int wHeight = 600;
window.setGeometry(QRect( (desktop->width()-wWidth)/2 , (desktop->height()-wHeight)/2 ,wWidth,wHeight));
propertyDock->setAllowedAreas(Qt::RightDockWidgetArea);
propertyDockContents->setMaximumWidth(200);
propertyDockContents->setMinimumWidth(20);
propertyDock->setWidget(propertyDockContents);
window.addDockWidget(Qt::RightDockWidgetArea,propertyDock);
window.show();
return app.exec();
}
Is there a "correct" way to do this?
As stated in the documentation:
Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.
Yes! You can't creating a main window without a central widget, But you can set central widget's height to zero.
MainWindow.cpp
centralWidget()->setMaximumHeight(0);

QWidget/QFrame not showing background or border when added to layout

I have a QFrame derived object:
class SubjectLineDisplay : public QFrame
{
Q_OBJECT
private:
// Members
public:
explicit SubjectLineDisplay(const QString&, const QString&, quint32, QWidget *parent = 0);
};
In the constructor, I set a background for it and a border:
QPalette p(palette());
p.setColor(QPalette::Background, QColor(255, 255, 255));
setPalette(p);
setLayout(mainLayout); // The mainLayout is a VBoxLayout which is a collection of a few QLabels
setFixedHeight(lTitle->size().height() + lId->size().height());
When i do this in main():
SubjectLineDisplay* x = new SubjectLineDisplay("NETWORK", "Network Centric Programming", 4);
x->show();
The widget shows up in a window, with the background and frame displaying properly, just as I would want. However, when I add it to another layout to show it:
SubjectLineDisplay* lineDisplay = new SubjectLineDisplay(
subjectNameLE->text(), idLE->text(), creditSpin->value()
);
emit newSubjectAdded(Course(subjectNameLE->text(), idLE->text(), creditSpin->value()));
subjectNameLE->clear();
creditSpin->setValue(3);
idLE->clear();
subjectLineLayout->addWidget(lineDisplay); //Adding the widget to a layout
Now, I don't see the frame or the border. How do I get the layout to display the frame and the border? What am I doing wrong?
Could you try using setAutoFillBackground(true)?
For as far as I'm aware foregrounds are always drawn, but backgrounds are not.

Resources