QT QGraphicsScene with QLabel and QPixmap - qt

I have a QGraphicsView, in that I have a QGraphicsScene, in that I have a QLabel and I set a .png picture as QPixmap into the QLabel. The .png is set in background.qrc resource file.
My QLabel's size is 600x400. Without the pixmap it's okay, the QGraphicsScene's size is 600x400 too. But when I set the pixmap to the QLabel and scale it, it fails.
The QLabel's size is the same, the pixmap is scaled well within the QLabel and only visible within it, but the QGraphicsScene adopts the real size of the QPixmap, which is 720x720. So the QLabel is visible with the QPixmap in it's correct size, but there is a gray place around it, since the scene is bigger.
How can I fix this and make it work? I want the QGraphicScene to stay on the size of the QLabel.
Here's the code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPixmap>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QLabel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsView *myView = new QGraphicsView(this);
QGraphicsScene *myScene= new QGraphicsScene();
QLabel *myLabel= new QLabel();
myLabel->setBaseSize(QSize(600, 400));
myLabel->resize(myLabel->baseSize());
myLabel->setScaledContents(true);
QPixmap pixmapBackground(":/new/cross.png");
myLabel->setPixmap(pixmapBackground);
myScene->addWidget(myLabel);
myView->setScene(myScene);
setCentralWidget(myView);
}
MainWindow::~MainWindow()
{
delete ui;
}

From your example code, you don't set the scene's size. You can do this with a call to setSceneRect. As the documentation states, when the rect is not set: -
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 without setting the scene rect, when the label is added to the scene, its size is changing, as in this example
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QLabel>
#include <QPixmap>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView *myView = new QGraphicsView;
QGraphicsScene *myScene= new QGraphicsScene();
// constrain the QGraphicsScene size
// without this, the defect as stated in the question is visible
myScene->setSceneRect(0,0,600,400);
QLabel *myLabel= new QLabel;
myLabel->setBaseSize(QSize(600, 400));
myLabel->resize(myLabel->baseSize());
myLabel->setScaledContents(true);
QPixmap pixmapBackground(720, 720);
pixmapBackground.fill(QColor(0, 255, 0)); // green pixmap
myLabel->setPixmap(pixmapBackground);
myScene->addWidget(myLabel);
myView->setScene(myScene);
myView->show();
return a.exec();
}
This should result in the correct scene size, as demonstrated here: -
As discussed in the comments, the example code above works as expected on OS X, but there still appears to be an issue when executing on Windows 10.

Related

Geometry of QLayout updates late after children's size change in Qt

I have a QScrollArea which holds a wrapper widget.
The wrapper widget is used to hold two 'blue' widgets which are positioned by QVBoxLayout.
The blue widgets have a fixed size.
The problem is in the code inside
mousePressEvent
Once the user presses the mouse button, I would like to resize the blue widgets and immediately move the scrollArea so that the top of the second blue widget is at the top of the viewport.
However, it seems that the geometry of the wrapper is updated later and therefore the position is not calculated correctly by calling mapTo function of wrapper.
Once I click the mouse again the position becomes correct, because this time the sizes don't change and the geometry was updated. How can I overcome this? Is there a way to force the wrapper to update its geometry? Using updateGeometry doesn't work...
mainwindow.c:
#include "mainwindow.h"
#include <QScrollArea>
#include <QVBoxLayout>
#include <QDebug>
#include <QScrollBar>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// pallete to color widgets blue
QPalette pal = palette();
pal.setColor(QPalette::Background, Qt::blue);
QSize widgetSize(200,500); // will be used to set size of widgets
scrollArea = new QScrollArea;
setCentralWidget(scrollArea);
wrapper = new QWidget(scrollArea); //wrapper to hold the layout and blue widgets
QVBoxLayout *layout = new QVBoxLayout(wrapper);
wrapper->setLayout(layout);
//first blue widget
a1 = new QWidget(wrapper);
a1->setAutoFillBackground(true);
a1->setPalette(pal);
a1->setFixedSize(widgetSize);
// second blue widget
a2 = new QWidget(wrapper);
a2->setAutoFillBackground(true);
a2->setPalette(pal);
a2->setFixedSize(widgetSize);
scrollArea->setWidgetResizable( true );
scrollArea->setWidget(wrapper);
layout->addWidget(a1);
layout->addWidget(a2);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
// Resize blue widgets and move scrollArea to top-left corner of a2
QSize newSize(200,510); // 10 px heigher than before
//!! This is where is seems to break...
a1->setFixedSize(newSize);
a2->setFixedSize(newSize);
//!! The mapTo uses the old geometry
scrollArea->verticalScrollBar()->setValue(a2->mapTo(wrapper, {0,0}).y());
}
MainWindow::~MainWindow() {};
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QScrollArea>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QScrollArea *scrollArea;
QWidget *wrapper;
QWidget *a1,*a2;
void mousePressEvent(QMouseEvent *event) override;
};
#endif // MAINWINDOW_H
Found a solution.
Simply add wrapper->adjustSize(); after the size changes.

Fixed text inside or adjacent to QProgressBar with scaling font size in Qt

I am new with Qt (using Qt Creator) and the QProgressBar. I am interested in learning how to have a fixed text value (not the value of the progress bar) inside or adjacent to the left of a QProgressBar and have its font size scale according with the size of the progress bar.
For example:
or
I have considered using a QLabel but failed and I could not find any examples online.
Any code sample illustrating the solution for me to understand and learn from will be much appreciated.
If label inside the progressbar will do, then here is an example. This might not be exactly what you want, but it should send you in the right direction. I adjust the font size in the resize event. In this example the font size is calculated based on the size of the label, which is the same size as the progress bar.
#include <QApplication>
#include <QProgressBar>
#include <QWidget>
#include <QLabel>
#include <QLayout>
#include <QTimer>
class Widget : public QWidget
{
Q_OBJECT
QProgressBar progressBar;
QLabel *label;
public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
progressBar.setRange(0, 100);
progressBar.setValue(20);
progressBar.setTextVisible(false);
progressBar.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
label = new QLabel(&progressBar);
label->setText("Hello World!");
setLayout(new QHBoxLayout);
layout()->addWidget(&progressBar);
}
protected:
void resizeEvent(QResizeEvent *)
{
label->resize(progressBar.size());
QFontMetrics fm(label->font());
float multiplier_horizontal = (float)label->width() / fm.width(label->text());
float multiplier_vertical = (float)label->height() / fm.height();
QFont font = label->font();
font.setPointSize(font.pointSize() * qMin(multiplier_horizontal, multiplier_vertical));
label->setFont(font);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"

Understanding QGraphicsScene in Qt

I am trying to understand the basics of Qt. After going through some posts, I came to know that ui_mainwindow.h gets created by UIC tool and that ui_mainwindow.h contains the information about my form/ui that I created.
In my GUI, I have taken a pushbutton and a graphics view. I want a simple image (which I am creating inside the program itself) gets displayed in the graphicsView. I am trying to do it with two ways (for learning purpose):
I can write the code inside on_pushButton_clicked()(i.e. the slot of my push_button).
I am trying to put the image from the main()
Problem: I am done with the first method. I used the following lines of code inside on_pushButton_clicked() and it worked.
void MainWindow::on_pushButton_clicked()
{
//Display image in the graphics viewer
Mat img(200,200, CV_8UC3, Scalar(255,0,0));
QImage image( img.data, img.cols, img.rows, img.step, QImage::Format_RGB888 );
QGraphicsScene* scene = new QGraphicsScene();
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
scene->addItem(item);
ui->graphicsView->setScene(scene);
}
Now, I want to do the similar thing from the main(). To do that, now my main() looks like following:
#include "mainwindow.h"
#include <QApplication>
//For image
#include <QImage>
#include <QPixmap>
#include <QGraphicsPixmapItem>
//#include "ui_mainwindow.h"
//OPENCV Headers
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
using namespace cv;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
//Display image in the graphics viewer
Mat img(200,200, CV_8UC3, Scalar(255,0,0));
QImage image( img.data, img.cols, img.rows, img.step, QImage::Format_RGB888 );
QGraphicsScene* scene = new QGraphicsScene();
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
scene->addItem(item);
w.ui->graphicsView->setScene(scene);
w.show();
return a.exec();
}
The above code written inside the main() works if I put #include "ui_mainwindow.h" in main.cpp. But if I comment #include "ui_mainwindow.h" and w.ui->graphicsView->setScene(scene); then, it throws error for the QGraphicsScene* scene = new QGraphicsScene();.
Error is main.cpp:32: error: allocation of incomplete type 'QGraphicsScene' QGraphicsScene* scene = new QGraphicsScene();
QUESTIONS: Why is the connection between QGraphicsScene and "ui_mainwindow.h". I understand that I need "ui_mainwindow.h" for the line w.ui->graphicsView->setScene(scene); becasue there I am using my ui but I don't understand the need for QGraphicsScene.
If You want to draw only static image better use just QLabel and set in some image
QImage *image = new QImage (":/prefix/image/vl.jpg" );
QLabel *magelab = new QLabel();
magelab->setPixmap(QPixmap::fromImage(*image));
Generaly QGraphicsScene better using with connection QGraphicsView. That is mean QGraphicsView set some object (scene) from QGraphicsScene just like:
class SomeObject : public QGraphicsView
{
Q_OBJECT
public:
explicit Schemat(QWidget *parent = 0);
private:
QGraphicsScene *scene;
};
and source
Schemat::Schemat( QWidget *parent) : QGraphicsView(parent)
{
scene = new QGraphicsScene(this);
this->setScene(scene);
// to Your scene You can add some image for example
scene->addPixmap(SOME_PIXMAP)
}
Then You create main window and add with Your QGraphicsView, for example as some part QGroupBox
void MainWindow::SetupSchemat()
{
schema = new Schemat();
QGroupBox *schbox;
QHBoxLayout *hschbox;
schbox = new QGroupBox(this);
hschbox = new QHBoxLayout(this);
schbox->setTitle("SomeScene");
hschbox->addWidget(schema); //add scene to layout
schbox->setLayout(hschbox);
}
QGraphicsScene is like some part of Your MainWindow on which you can make some animation, You can something draw. QGraphicsScene is more better to using if You want use animation not only static image it supplies more option to manipulate image (ex scaling, catch mouse click, manage via cordinates others object), and other object each should be animate or just display. To QGraphicsScene You can add some QGraphicsItem in turn each QGraphicsItem can moving onto QGraphicsScene with particular conditions defined erly or in flow. Together QGraphicsView, QGraphicsScene and QGraphicsItem can created animation or just image in some part Your main window.
Also nice explained You will find here
https://www.youtube.com/watch?v=fmSs2mNGh9I

QGraphicsProxyWidget has clipped context menu in QGraphicsScene

The following code is based on the documentation of Graphics View Framework. I embed a QLineEdit in a QGraphicsScene and run the program. When I right click the line edit in the scene I get a clipped context menu. The context menu of a QGraphicsProxyWidget is drawn by the scene as a child QGraphicsProxyWidget so it get's clipped if the window is too small. I want all embedded widgets to show their context menus as top-level windows like they do when not being embedded in a QGraphicsScene. I have tried the BypassGraphicsProxyWidget flag in two ways but it doesn't work as I want. Tested on Qt 4.8 / 5.0 on Linux and Windows. Same issue on all platforms.
How can I make the embedded widgets display normal, top-level context menus with native look? Overloading QGraphicsView's contextMenuEvent gives a native top-level context menu - could I do some sort of delegation and make QGraphicsView display the context menu of embedded widgets in the scene?
#include <QApplication>
#include <QLineEdit>
#include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QGraphicsScene scene;
QGraphicsProxyWidget *proxy = scene.addWidget(new QLineEdit(), Qt::BypassGraphicsProxyWidget);
QGraphicsView view(&scene);
view.setWindowFlags(Qt::BypassGraphicsProxyWidget);
view.show();
return app.exec();
}
Unfortunately, this is a known bug QTBUG-10683. A workaround is suggested in the last comment to the bug report.
You get native context menus by adding a QWidget that has the Qt::BypassGraphicsProxyWidget set. Children will render it's context menus as pop-ups native style.
#ifndef QGLPARENT_H
#define QGLPARENT_H
#include <QGLWidget>
#include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QGraphicsView>
class QGLParent : public QGraphicsView
{
private:
QGraphicsProxyWidget *child;
public:
QGLParent(QWidget *parent, QWidget *child) : QGraphicsView(parent)
{
setFrameShape(QFrame::NoFrame);
QGLFormat format(QGL::SampleBuffers);
format.setSwapInterval(1);
setScene(new QGraphicsScene());
setViewport(new QGLWidget(format));
//setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
child->setWindowFlags(Qt::BypassGraphicsProxyWidget);
this->child = scene()->addWidget(child);
}
protected:
void resizeEvent(QResizeEvent *event)
{
scene()->setSceneRect(QRect(QPoint(0, 0), event->size()));
child->resize(event->size().width(), event->size().height());
QGraphicsView::resizeEvent(event);
}
};
#endif

qlabel centering

I have a qlabel L inside a qwidget W. L is vertically and horizontally aligned.
When I resize W, L doesn't get centered.
Is this expected?
What's a good implementation to have L centered again?
To align text in a QLabel by calling QLabel::setAlignment works like expected for me.
Maybe you miss to add your Label to a Layout (so your label would automatically resized if your widget is resized). See also Layout Management. A minimal example:
#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QWidget>
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
QLabel* label=new QLabel("Hello World!");
label->setAlignment(Qt::AlignCenter);
QWidget* widget=new QWidget;
// create horizontal layout
QHBoxLayout* layout=new QHBoxLayout;
// and add label to it
layout->addWidget(label);
// set layout to widget
widget->setLayout(layout);
widget->show();
return app.exec();
}

Resources