Introduction
I am working with the Designer inside the Qt Creator and have a QMainWindow with a QLabel in it. Because the program loads pictures and displays them inside the label i want that the label resizes with a ratio of 1.25 inside setted boundaries when expanding or shrinking the QMainWindow. The label should resize INDEPENDENT, again INDEPENDENT from its content.
What i want:
Open the main window for first time:
width: 640, height: 512
Shrinking the main window:
label shrinks with constant ratio (640/512) till minimum size (320 x 256)
Expanding the main window:
label expands with constant ratio (640/512) till maximum size (1280 x 1024)
1. Approach:
Therefor i...
added a QLabel(called imageLabel) inside the centralWidget of the QMainWindow
set the centralWidget's layout to grid layout (QGridLayout)
sed the following properties to the QLabel:
geometry - Can not set the values because of grid layout usages!
minimumSize > width: 320, height: 256 (Minimum values)
maximumSize > width: 1280, height: 1024 (Maximum values)
sizePolicy > Horizontal Policy == Vertical Policy == Expanding
Element structure:
This doesn't work because i can not set an initial size in the 'geometry' section. The label does not scale with fixed ratio although it respects minimum and maximum values.
2. Approach:
Following that answer i set an initial pixmap:
QPixmap p;
ui->imageLabel->setPixmap(p.scaled(640, 512, Qt::KeepAspectRatio));
Which didn't change anything.
3. Approach:
I also applied the other answer's class and promoted it to the widget:
That didn't change anything too.
4. Approach:
I then combined the 2. and 3. approach and set an initial pixmap which...
...didn't change anything.
Here is what it does for the approaches 1. - 4.:
5. Approach
Adding the label of 4. approach into a widget:
Well the label doesn't resize at all:
So, how can get the label to have an initial size of 640 x 512 and scale with fixed ratio between 1280 x 1024 and 320 x 256 ?
A possible solution is to install an eventFilter to the centralwidget so doing the required calculation is set the size.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->centralWidget->installEventFilter(this);
ui->imageLabel->setPixmap(QPixmap(":/image.png"));
ui->imageLabel->setScaledContents(true);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
const float ratio = 1.25;
if(watched == ui->centralWidget && event->type() == QEvent::Resize
&& ui->centralWidget->width() > 0
&& ui->centralWidget->height() > 0){
float central_ratio = 1.0*ui->centralWidget->width()/ui->centralWidget->height(); QSize s;
if(central_ratio > ratio){
s = QSize(ratio*ui->centralWidget->height(), ui->centralWidget->height());
}
else{
s = QSize(ui->centralWidget->width(), ui->centralWidget->width()/ratio);
}
ui->imageLabel->resize(s);
}
return QMainWindow::eventFilter(watched, event);
}
Related
I currently have a problem with a QGridLayout.
Each square is a widget and I have a loop like this
for(int i = 0; i < 100; i++;)
{
ui->layout->addWidget(new Square(this),rowNr,colNr);
}
The QGridLayout is part of a QFrame.
My question is: Why is there so much whitespace between each square (horizontally)
This is the code for a square
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(QBrush("#c56c00"));
painter.drawRect(0, 0, 30, 30);
Where is my problem? I want to have each cell 1 by 1 without any space between them. I dont know why it is vertically correct.. Im completely new to C++ and Qt..
Try that:
#include "mainwindow.h"
#include <QGridLayout>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
resize(800, 800);
auto widget = new QWidget(this);
setCentralWidget(widget);
auto gl = new QGridLayout(widget);
gl->setSpacing(0);
gl->setAlignment(Qt::AlignTop | Qt::AlignLeft);
widget->setLayout(gl);
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
gl->addWidget(new QPushButton(QString::number(i*10 + j), this), i, j);
}
Since you have fixed size widgets (30x30), each widget is exactly 30x30 pixels. If your layout is larger horizontally/vertically, the spacing is increased to allow even distribution.
Example:
Let's say you have a frame with a width of 100px with 3 squares aligned horizontally. 3 times 30 equals 90 so you have 10px remaining. Since layouts in general try to evenly distribute the components that they align you will get approx. 3px spacing between each square.
You either have to play with the sizing of your QFrame (my guess is that it is not fixed size and increases/decreases in size when you resize it) or avoid using fixed size widgets inside it.
In general I would recommend either sticking with fixed size for all of your components or make the children (here: squares) to properly resize.
PS: For the task at hand it's quite easy to provide a minimal working example. ;)
I have a QGraphicsScene "scene" and QGraphicsView "graphicsView".
I have a drawing method. When I need redraw all the graphics, I call this method. Everything is OK. But I realized that scene->clear() doesn't change the sceneRect.
Also I tried:
graphicsView->items().clear();
scene->clear();
graphicsView->viewport()->update();
After that, if I get the sceneRect by
QRectF bound = scene->sceneRect();
qDebug() << bound.width();
qDebug() << bound.height();
I expect the bound.width and bound.height to be '0'. But they aren't. I see the previous values everytime. How to clear sceneRect when I clear the scene itself?
It gives some problems that sceneRect remains the same, while using graphicsView->fitInView() method.I use following code:
QRectF bounds = scene->sceneRect();
bounds.setWidth(bounds.width()*1.007); // to give some margins
bounds.setHeight(bounds.height()); // same as above
graphicsView->fitInView(bounds);
Although I completely cleared the scene and added only one rather small rectangle, the rectangle didn't fit into view because of sceneRect remains too big.
I hope I could explain my problem.
From the Qt Docs (emphasis mine):
This property holds the scene rectangle; the bounding rectangle of the scene
The scene rectangle defines the extent of the scene. It is primarily used by QGraphicsView to determine the view's default scrollable area, and by QGraphicsScene to manage item indexing.
If unset, or if set to a null QRectF, sceneRect() will return the largest bounding rect of all items on the scene since the scene was created (i.e., a rectangle that grows when items are added to or moved in the scene, but never shrinks).
Therefore, the only way to shrink the sceneRect is to use setSceneRect.
The better question is why do you need to set scene rectangle? In case you have a smaller scene don't set it. Instead add items to the scene and fit in view based on items bounding rectangle as in my example below:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsRectItem>
#include <QPointF>
#include <QDebug>
#include <qglobal.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
_scene = new QGraphicsScene(this);
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->setScene(_scene);
connect(ui->button, SIGNAL(released()), this, SLOT(_handleRelease()));
}
MainWindow::~MainWindow()
{
delete ui;
}
int MainWindow::_random(int min, int max)
{
return qrand() % ((max + 1) - min) + min;
}
void MainWindow::_handleRelease()
{
_scene->clear();
QGraphicsRectItem* pRect1 = _scene->addRect(0, 0, _random(50,100), _random(50,100));
QGraphicsRectItem* pRect2 = _scene->addRect(0, 0, _random(20,50), _random(20,50));
pRect1->setPos(QPoint(40,40));
pRect2->setPos(QPoint(20,20));
ui->graphicsView->fitInView(_scene->itemsBoundingRect(),Qt::KeepAspectRatio);
}
In case you have a large scene with hundreds of items this approach will be slow because:
If the scene rect is unset, QGraphicsScene will use the bounding area
of all items, as returned by itemsBoundingRect(), as the scene rect.
However, itemsBoundingRect() is a relatively time consuming function,
as it operates by collecting positional information for every item on
the scene. Because of this, you should always set the scene rect when
operating on large scenes.
I want to modify the background-color of QPushButtons. The problem is that modifying the background color using setStyleSheet("QPushButton { background-color: #00FF00 }"); , the button size also scales.
before
after
I understand that by changing the background-color, the entire button style sheet gets overwritten and reset to some default (How to override just one property:value pair in Qt StyleSheet).
My question is: how can I set the size of the button such that it is the same size as the original?
I am using MacOSX and I tried all combinations of height, minimum-height, padding.
If it's just about setting the background color, then using the QPalette would be an option.
QPushButton button;
QPalette palette = button.palette();
palette.setColor(QPalette::Background, QColor("#00FF00");
button.setPalette(palette);
I thinks it's because min-width and min-height are not specified.
According to the documentation:
If this property is not specified, the minimum width is derived based
on the widget's contents and the style.
One possibility could be to get the size of the button before setting the style, and set that size when you apply the background color.
I.e. (in this example, we have a UI file with a pushbutton called pushButton):
QString width ("min-width: " +
QString::number(ui.pushButton->size().width()) +
" px; " +
"max-width: " +
QString::number(ui.pushButton->size().width()) +
" px;");
QString height ("min-height: " +
QString::number(ui.pushButton->size().height()) +
" px; " +
"max-height: " +
QString::number(ui.pushButton->size().height()) +
" px;");
QString style ("#pushButton { " + width + height +
"background-color: black; }");
qApp->setStyleSheet(style);
I'm setting both min and max width and height because the reference says
If you want a widget with a fixed width, set the min-width and
max-width to the same value.
Of course, an easier solution could be to resize the pushbutton after setting the background color. Something like this:
int width = ui.pushButton->size().width();
int height = ui.pushButton->size().height();
QString style ("#pushButton { background-color: black; }");
qApp->setStyleSheet(style);
ui.pushButton->resize(width, height);
I'm creating a new widget, by subclassing the QWidget class. I'd like to be able to set a ratio (for its height and its width) for this widget, which will always be maintained.
For this, I've always searched, using the Qt5 documentation, Google, and Stackoverflow. Obviously, I've found answers: in particular, this one. But, unfortunately, not even one is fully effective:
Setting the sizeIncrement does totally nothing, even if the widget is a window
I tried to overload resizeEvent, but I really don't know how to do this...
If I follow this answer, two things:
If the widget is a top-level window, the ratio isn't maintained at all, I can resize it as I want.
If I place this widget in a layout, if I just increase both width and height of the window, the ratio is maintained. But as soon as I increase the width or the height to much, the widget is flattened. Instead, I would like that the layout automatically adjust its size to keep the widget's ratio.
So, how could I manage to keep the aspect ratio of a subclassed QWidget?
Create a parent widget (e.g., AspectRatioWidget) in which to place your widget. For the parent widget, subclass QWidget and give it a QBoxLayout. Put your widget into the center, and QSpacerItems on either side. Then in the parent widget's QWidget::resizeEvent adjust the direction and stretches as needed. I've provided an example below. To use, just create an instance of AspectRatioWidget and pass the constructor a pointer to your widget and the desired aspect ratio.
// header
class AspectRatioWidget : public QWidget
{
public:
AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent = 0);
void resizeEvent(QResizeEvent *event);
private:
QBoxLayout *layout;
float arWidth; // aspect ratio width
float arHeight; // aspect ratio height
};
// cpp
AspectRatioWidget::AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent) :
QWidget(parent), arWidth(width), arHeight(height)
{
layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
// add spacer, then your widget, then spacer
layout->addItem(new QSpacerItem(0, 0));
layout->addWidget(widget);
layout->addItem(new QSpacerItem(0, 0));
}
void AspectRatioWidget::resizeEvent(QResizeEvent *event)
{
float thisAspectRatio = (float)event->size().width() / event->size().height();
int widgetStretch, outerStretch;
if (thisAspectRatio > (arWidth/arHeight)) // too wide
{
layout->setDirection(QBoxLayout::LeftToRight);
widgetStretch = height() * (arWidth/arHeight); // i.e., my width
outerStretch = (width() - widgetStretch) / 2 + 0.5;
}
else // too tall
{
layout->setDirection(QBoxLayout::TopToBottom);
widgetStretch = width() * (arHeight/arWidth); // i.e., my height
outerStretch = (height() - widgetStretch) / 2 + 0.5;
}
layout->setStretch(0, outerStretch);
layout->setStretch(1, widgetStretch);
layout->setStretch(2, outerStretch);
}
I have rewritten Anthony's code in Python/PySide2:
from PySide2.QtWidgets import QBoxLayout, QSpacerItem, QWidget
class AspectRatioWidget(QWidget):
def __init__(self, widget, parent):
super().__init__(parent)
self.aspect_ratio = widget.size().width() / widget.size().height()
self.setLayout(QBoxLayout(QBoxLayout.LeftToRight, self))
# add spacer, then widget, then spacer
self.layout().addItem(QSpacerItem(0, 0))
self.layout().addWidget(widget)
self.layout().addItem(QSpacerItem(0, 0))
def resizeEvent(self, e):
w = e.size().width()
h = e.size().height()
if w / h > self.aspect_ratio: # too wide
self.layout().setDirection(QBoxLayout.LeftToRight)
widget_stretch = h * self.aspect_ratio
outer_stretch = (w - widget_stretch) / 2 + 0.5
else: # too tall
self.layout().setDirection(QBoxLayout.TopToBottom)
widget_stretch = w / self.aspect_ratio
outer_stretch = (h - widget_stretch) / 2 + 0.5
self.layout().setStretch(0, outer_stretch)
self.layout().setStretch(1, widget_stretch)
self.layout().setStretch(2, outer_stretch)
I am doing a software with a drawing surface that represent a plot (like a sin function) (A child of QWidget) and I would like to have a QScrollBar acting like a QScrollArea. So if my drawing widget show show 750 dots (my plot is made of dots), but there are 1000 dots, I would like the slider of the ScrollBar to fill 75% of the available space.
I can't use a QScrollArea because the scroll is proportionnal with the size of the widget it contains. In my case, the scroll must be proportionnal with the number of dots on the screen. I know how to get my ratio, but I don't know how to setup correctly the QScrollBar
Example: I edited the value of PageStep, but I don't understand how this work. I can set pageStep to 100 with a range of [0,99] and it will fill the half of the QScrollBar.
My interface:
QWidget (Vertical Layout) //Main Widget
Drawing Surface (Child of QWidget)
QScrollBar (Horizontal)
Well, I think I am able to do something with this:
http://harmattan-dev.nokia.com/docs/library/html/qt4/qscrollbar.html
The relationship between a document length, the range of values used in a scroll bar, and the page step is simple in many common situations. The scroll bar's range of values is determined by subtracting a chosen page step from some value representing the length of the document. In such cases, the following equation is useful: document length = maximum() - minimum() + pageStep().
So in my case the length is the number of dots and I can set minimum() to 0. So, as you can see on the picture, to do something like QScrollArea. The proportion is: PercentageVisible = PageStep / Length and another equation is Length = PageStep + Max.
I have two equations, two missing values (PageStep and Maximum) and two known values (PercentageVisible and Length).
Example: I have 1024 dots, but only 75% of them are shown.
0.75 = PageStep / 1024 ----------> PageStep = 768
1024 = Max + 768 ----------------> Max = 256
You can try it in your software and it will works. I know there's not so much people that will needs to reproduce this because QScrollArea will do the job in most of the case.
By example, this code is in a slot reacting from a resize event:
ui.sbarRange->setPageStep(u64SampleCount * dRatio);
ui.sbarRange->setMaximum(u64SampleCount - ui.sbarRange->pageStep());
You can create a new QWidget subclass and reimplement sizeHint and paintEvent. In the paintEvent you can use event->rect() to determine which area is currently exposed and needs to be drawn. Note that paintEvent must be fast if you don't want your window to freeze. Also you need to put created widget in QScrollArea.
Here is a simple example that draws a sinusoid:
class SinWidget : public QWidget {
public:
QSize sizeHint() const {
return QSize(10000, 200);
}
void paintEvent(QPaintEvent* event) {
QPainter painter(this);
for(int x = event->rect().left(); x <= event->rect().right(); x++) {
painter.drawPoint(x, 100.0 + qSin(0.05 * x) * 20.0);
}
}
};
QScrollArea area;
area.setWidget(new SinWidget());
area.show();
This example will work fine with very large widget size (e.g. 100 000 pixels). So full repaint or memory allocation doesn't happen. You only need to keep your paintEvent fast.