I'm having a question about a weird (At least it was unexpected for me) behavior (It crashes) of qt when initializing pointers on a member class different than the constructor. I am attaching part of my code:
In mainwindow.h:
class MainWindow : public QMainWindow
{
...
private:
QPixmap *qpm_s1_yaw;
QPainter *s1_yaw_painter;
...
}
In mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
...
initGraph(qpm_s1_yaw, s1_yaw_painter, ui->YAW1);
...
}
void MainWindow::initGraph(QPixmap *map, QPainter *painter, QLabel *label)
{
map = new QPixmap(label->size());
map->fill(Qt::white);
painter = new QPainter(map);
... doing some stuff ...
label->setPixmap(*map); // ++(Remember this LINE)++
}
That actually works, but when I comment the line:
label->setPixmap(*map)
and instead set the Pixmap in the constructor (MainWindow::MainWindow) by writing:
ui->YAW1->setPixmap(*qpm_s1_yaw)
I got a segmentation Fault.
Could someone explain what is wrong with it? To make it work I had to initialize all the pointers in the constructor (and commenting those line in the classs member initGraph), like this:
qpm_s1_yaw = new QPixmap(ui->YAW1->size());
s1_yaw_painter = new QPainter(qpm_s1_yaw);
initGraph(qpm_s1_yaw, s1_yaw_painter, ui->YAW1);
ui->YAW1->setPixmap(*qpm_s1_yaw);
Thanks
This is a trivial misunderstanding of how C++ works, nothing to do with Qt.
Your code lies to you: you can equally well write: initGraph(0, 0, ui->YAW1). You're initializing local variables instead of class members. The values you pass as the first two arguments are not used for anything.
It's also completely unnecessary to hold the pixmap and the painter by pointer. Hold the pixmap by value, and only instantiate a painter for it when you do the painting.
Holding a painter to a pixmap when you're not painting on it can cause unnecessary copies of the pixmap to be made when the pixmap is consumed (read from): a pixmap with an active painter is considered "dirty".
What you should do then is to hold pixmaps by value and you can return the new value from initGraph - this decouples initGraph from the detail of the surrounding class where the pixmaps are stored. The user of initGraph has an option of not storing the pixmap, and e.g. querying the label itself for it.
class MainWindow : public QMainWindow
{
Ui::MainWindow ui; // hold by value
...
QPixmap qpm_s1_yaw; // hold by value
QPixmap initGraph(QLabel *label) {
QPixmap pixmap{label->size()};
pixmap.fill(Qt::white);
QPainter painter{&pixmap};
//... doing some stuff ...
label->setPixmap(pixmap);
return pixmap;
}
public:
explicit MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
ui.setupUi(this);
gpm_s1_yaw = initGraph(ui.YAW1);
}
};
Related
I want to use ResizeEvent to receive the size of the window and set it to the size of a QLabel, in order to make the image stretched and adapted to the window's dimensions.by a left click of the mouse I can resize my window and the image takes a new size.
You must have the following considerations:
It is not necessary to store QPixmap by means of a pointer since when passing it to the QLabel it is copied by value.
Therefore, if you change the size of the QPixmap p will not be reflected in the QLabel since the QPixmap that has the QLabel is a copy of the one you established at the beginning.
It is not necessary to use a layout for this task since it will create an infinite loop since this also intervenes in the resizeEvent of the widget where it has been established so if you change the size of the QLabel, it will change the size of the QWidget, and this again I will try to change the QLabel, and so on.
It is not advisable to modify the original QPixmap since changing its size modifies the pixels and you will get an unexpected effect.
Using the above we obtain the following code:
*.h
#ifndef TESTSIZE_H
#define TESTSIZE_H
#include <QWidget>
class QLabel;
class testsize : public QWidget
{
Q_OBJECT
public:
explicit testsize(QWidget *parent = 0);
~testsize();
private:
QLabel *image;
QPixmap original_px;
protected:
void resizeEvent(QResizeEvent *event);
};
#endif // TESTSIZE_H
*.cpp
#include "testsize.h"
#include <QLabel>
#include <QResizeEvent>
testsize::testsize(QWidget *parent) :
QWidget(parent)
{
image = new QLabel(this);
original_px = QPixmap(":/wallpaper.jpg");
image->setPixmap(original_px);
resize(640, 480);
}
testsize::~testsize()
{
}
void testsize::resizeEvent(QResizeEvent *event)
{
QPixmap px = original_px.scaled(event->size());
image->setPixmap(px);
image->resize(event->size());
QWidget::resizeEvent(event);
}
You can find the complete example in the following link.
I am using a developer defined QGraphicsScene pointer from the "dialog/mainwindow" class called wavesScene to return a QList of QGraphicsItem*'s using QGraphicsScene::items. Something seems to be wrong with my syntax, it says that the list "graphicsItemList" is never used/declared.
Here is my code, any ideas as to how I would properly implement this methodology
relevant code inside mainwindow/segyView.h
QGraphicsScene *wavesScene;
Constructor code *I think, may be called something different
SEGYView::SEGYView(QWidget *parent)
:QMainWindow(parent),
ui(new Ui::SEGYView)
{
ui->setupUi(this);
wavesScene = new QGraphicsScene(this);
ui->PixmapView->setScene(wavesScene);
}
Code inside slider_value_changed() slot
QList<QGraphicsItem*> graphicsItemList(wavesScene->items());
QGraphicsPixmapItem pixmapItem(graphicsItemList.at(0));
QPixmap wavesPixmap = pixmapItem.pixmap();
QPixmap wavesPixmapScaled = wavesPixmap.scaled((newSliderValue*INITIAL_WAVES_PIXMAP_X_SIZE), 1250);
pixmapItem.setPixmap((wavesPixmapScaled));
wavesScene->addItem(&pixmapItem);
relevant code inside actionOpen on triggered() slot
wavesScene->addItem(pixmapGraphicsItem);
ui->PixmapView->setScene(wavesScene);
Error is "graphicsItemList" was not declared in this scope
QGraphicsPixmapItem* pixmapItem = graphicsItemList.at(0);
^
What would the syntax look like if I wanted to even use QGraphicsView::scene() in the first place? My goal is to change the scale of a pixmap in my graphics view object with sliders. I want to use QgraphicsView::scene() and QScene::itemsAt() etc. to find the original pixmap and then use QPixmap::scaled() (this I found would be the only way to ensure my cosmetic property set for the pixmap holds true). However I am having issues with the syntax of QGraphicsView::scene(). My attempt is below. I am also creating a Qt widget application for context.
QGraphicsViewScene graphicsScene = ui->PixmapView->scene();
QGraphicsPixmapItem graphicsPixmapItem = graphicsScene.itemAt(0, 0);
edit
If I was to store my QPixmap pixmap* as a member variable I am not entirely sure how to implement that where it remains in scope for my slots.
edit
static member variables?
You can make your QGraphicsPixmapItem object a member variable of your class. Then you would be able to access it from any of your class member functions.
Here is a quick example:
class MyClass : public QWidget
{
Q_OBJECT
public:
MyClass(QWidget *parent = nullptr) : QWidget(parent)
{
// create graphics view, scene, etc..
}
public slots:
void openActionTriggered()
{
...
myItem = scene->addPixmap(myPixmap); // you can create your item however you want.. this is just an example
}
void mySlot()
{
if(myItem)
{
// do something with myItem
}
}
private:
QGraphicsPixmapItem *myItem = nullptr; // myItem is a member variable of
QGraphicsScene *scene = nullptr; // I made scene a member variable so it can be accessed from any member functions
}
I am trying to make a class that will make a dot graphic. The class inherits from QWidget. I want it to draw lines and dots on a QPixmap that will be displayed in a QLabel.
The constructor of the class looks like this:
MyClass::MyClass()
{
calcul_proprietes(); // Function that makes calculation of what to draw.
pix = new QPixmap(760,350);
dessiner_graphique(); // Function that does the drawing.
//Displaying the qpixmap
layout_principal = new QVBoxLayout(this);
label_pix = new QLabel(this);
label_pix->setPixmap(*pix);
layout_principal->addWidget(label_pix);
this->setLayout(layout_principal);
}
And a short part of the function that does the drawing
void MyClass::dessiner_graphique()
{
// ...
QPainter painter(pix);
QRect contour(x_depart,y_depart,largeur_grille,hauteur_grille);
painter.drawRect(contour);
// ...
}
I don't want to use the paintEvent function because it gets called all the time and i only need my graphic to be painted once. What do i do wrong?
Did you call the default base class constructor before Your class constructor ?
MyClass( QObject *parent )
: QWidget( parent )
{
// Your posted code.
}
In Qt Creator, I have a couple of widgets declared like so:
Header File:
class MapViewer : public QGraphicsView
{
Q_OBJECT
public:
explicit MapViewer(QGraphicsScene *scene, QWidget *parent = 0);
~MapViewer();
public slots:
void mousePressEvent(QMouseEvent *event);
};
// Declaration for the map editor window.
class MapEditor : public QMainWindow
{
Q_OBJECT
public:
explicit MapEditor(QWidget *parent = 0);
~MapEditor();
public:
QLayout *editorLayout;
QPushButton *btn;
QGraphicsScene *mapScene;
MapViewer *mapView;
private:
Ui::MapEditor *ui;
};
CPP File:
MapEditor::MapEditor(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MapEditor)
{
ui->setupUi(this);
this->setWindowTitle("2DXY :: Map Editor");
this->setGeometry(10,10,1170,750);
editorLayout = new QVBoxLayout; // Create a new layout
this->setLayout(editorLayout); // Set the widget's layout to our newly created layout.
mapScene = new QGraphicsScene(); // Create a new graphics scene to draw upon.
mapView = new MapViewer(mapScene,this); // Create a new graphics view to display our scene - set its parent to 'this' so that it doesn't open in a new window.
mapView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mapView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mapView->setGeometry(20,20,1178,546); // Width first, then height.
AND:
void MapViewer::mousePressEvent(QMouseEvent *event)
{
// Show an empty message box, just to check that the event handler works!
QMessageBox *notification = new QMessageBox();
notification->show();
notification->exec();
// Some how access the same QGraphicsScene and View (mapScene, mapView) as above, so
// I can update their contents on the open form / window.
}
And as you can see, I wish to access the Graphics Scene again to update it, then redraw it (or whatever). But I'm not able to access the graphics scene at all, despite a few hours of trial and error with declaring widgets in different ways.
I know that the listener itself works, because if it's set to open a new message box, or output to the debug window, then it works, it's just that I can't access the widgets I've already defined.
I feel that there is a (relatively) easy solution to this problem, and that I'm just missing something obvious.
You passed the QGraphicsScene to your MapRender object's constructor. What do you do with the scene within its constructor? Ideally, you should be storing it as a data member of MapRender. For example:
class MapRender {
public:
MapRender(QGraphicsScene* scene)
: scene_(scene)
{
}
public slots:
void mousePressEvent(QMouseEvent *event);
private:
QGraphicsScene* scene_;
}
Now in your implementation of mousePressEvent, you can access to the scene member:
void MapRender::mousePressEvent(QMouseEvent *event) {
int CursorX = event->globalX();
int CursorY = event->globalY();
QGraphicsRectItem *clickedBox = new QGraphicsRectItem(40,40,32,32);
clickedBox->setBrush(QBrush(Qt::blue));
scene_->addItem(clickedBox);
}
Keep in mind you should ideally be putting the implementation of the constructor in your cpp file, but my example does it in the declaration for brevity.
void MapViewer::mousePressEvent(QMouseEvent *event)
{
// Show an empty message box, just to check that the event handler works!
QMessageBox *notification = new QMessageBox();
notification->show();
notification->exec();
// To add something whenever the user clicks, you don't need the view,
// just the scene.
scene()->addItem( new MyItem() );
}
Remember MapViewer derives from QGraphicsView and the view must know about the scene it belongs to - so it has a scene() method to return it, which you inherited.