How to update QGraphicsScene in a QMainWindow - c++

I'm trying to create a simple Mandlebrot viewer in Qt, I have a Mainwindow with a QGraphicsScene in it, I generate the QImage with the picture and then I have some buttons that I'd like to use to navigate the image (Move, zoom, etc.)
I can get the initial image to appear, however I'm not sure how to tell it to rerender after I've changed any of the coordinates.
For the life of me I can't work out how to refresh the QMainWindow, or alternatively remove the QGraphicsScene from the MainWindow and make a call to render it.
QImage renderImage(//loads in global variables)
{
//calculates the image and returns a QImage
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene *graphic = new QGraphicsScene( this );
graphic->addPixmap(QPixmap::fromImage(renderImage()));
ui->graphicsView->setScene(graphic);
}
void MainWindow::on_Left_clicked()
{
// Changes global variables and try to rerender the scene.
update(); //does nothing
}
UPDATE: Solved!
Thank you so much goug, that helped a lot. I'm new to Qt, so couldn't work out where the loop was that would update things. I added the code you suggested and it worked perfectly. Thanks :)
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene *graphic = new QGraphicsScene( this );
pixmap_item = graphic->addPixmap(QPixmap::fromImage(renderImage()));
ui->graphicsView->setScene(graphic);
}
void MainWindow::on_Left_clicked()
{
// Changes global variables and try to rerender the scene.
centerR -= 0.1;
pixmap_item->setPixmap(QPixmap::fromImage(renderImage()));
}

You don't show any code that changes any coordinates, or anything for that matter. For the built-in graphics items such as QGraphicsPixmapItem, which is what you create by call addPixmap, you generally don't have to force anything. Those objects repaint themselves as needed when you change something via their member functions.
I suspect that where you're going wrong is that you may be believing that there's a connection between the pixmap and the QGraphicsPixmapItem that you created in the constructor. There isn't; so if it's the pixmap that you're changing, then you need to reapply that pixmap to the pixmap item. You'll need a new member in your class to track:
QGraphicsPixmapItem *pixmap_item_;
And change your constructor code to:
pixmap_item_ = graphic->addPixmap(QPixmap::fromImage(renderImage()));
Then whenever you've updated your pixmap, reapply that pixmap to the graphics item you've created in the constructor:
pixmap_item_->setPixmap (QPixmap::fromImage(renderImage()));
The setPixmap call will trigger the pixmap item to repaint itself; you don't have to call update() separately. If this isn't the issue, then we need to see more of your code.

Related

crash when calling new operator outside constructor in qt

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);
}
};

Qt Update QPixmapItem in QGraphicsScene

I want to have an image in my main window with the buttons laid out on top of the image. I am using view, scene and pixmap item but when I try to change the pixmap item in the slot function, I crash.
Here is an example of what I have:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
//scene, view, pixitem and button are private variables in header
QGraphicsScene *scene = new QGraphicsScene(this);
QGraphicsView *view = new QGraphicsView(scene);
QGraphicsPixmapItem *pixitem = new QGraphicsPixmapItem;
pixitem->setPixmap(QPixmap(":/img/this.png"));
scene->addItem(pixitem);
QPushButton *button = new QPushButton(view);
this->connect(button,SIGNAL(clicked()),this,SLOT(slot()));
this->setCentralWidget(view);
}
void MainWindow:slot()
{
pixitem->setPixmap(QPixmap(":/img/that.png"));
}
Possible duplicate but this solution did not work for me:
Qt Update Pixmap of QGraphicsPixmapItem
You have shadowed the member variable pixitem because you are using
QGraphicsPixmapItem *pixitem = new QGraphicsPixmapItem;
The pixitem added to the scene will be reference by the local variable, not by the member variable. Just change to
pixitem = new QGraphicsPixmapItem;
In the constructor, you create the pixmap item:
QGraphicsPixmapItem *pixitem = new QGraphicsPixmapItem;
Then in the slot you call
pixitem->setPixmap(QPixmap(":/img/that.png"));
In order to compile successfully, you must have a QGraphicsPixmap* as a member of the class. Therefore, I conclude that this is NULL, or at least, invalid due to the creation of the item on the pointer in the constructor.
Assuming this is correct and you have indeed declared pixitem in the header of the class, change the creation in the constructor to: -
pixitem = new QGraphicsPixmapItem;

Qt build menu for touch screen

i want to build a QT application for windows/android.
I have one mainwindow in which i have different buttons to call submenus, as you can see on the pictures. What do you think is the best implementation for that?
I thought about replacing the central widget
Using QStackwidget
Or open a new widget on the same position and size and close after.
What do you think? Do you have some favourite implementation or do i miss some important?
Edit
My Mainwindow constructor
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_stacked = new QStackedWidget;
HomeScreen *homescreen=new HomeScreen(m_stacked);
Control *manual=new Control(m_stacked);
m_stacked->addWidget(homescreen);
m_stacked->addWidget(manual);
this->setCentralWidget(m_stacked);
}
Implementation of every Screen
class HomeScreen : public QWidget
{
Q_OBJECT
public:
explicit HomeScreen(QStackedWidget* stack, QWidget *parent = 0);
~HomeScreen();
private slots:
void on_pushButton_clicked();
private:
Ui::HomeScreen *ui;
QStackedWidget *m_stacks;
};
HomeScreen::HomeScreen(QStackedWidget* stack,QWidget *parent) :
m_stacks(stack),QWidget(parent),
ui(new Ui::HomeScreen)
{
ui->setupUi(this);
}
HomeScreen::~HomeScreen()
{
delete ui;
}
void HomeScreen::on_pushButton_clicked()
{
m_stacks->setCurrentIndex(1);
}
What is your solution to change the current Widget, inside a widget of the QStackwidget? I used the solution above what do you think about it?
QStackedWidget was made right for that, I don't see any reason why you should incline to other options. You can always removeWidget(widget) and delete widget; if you want to free the memory of some rarely used menu.
Edit:
Your code is fine, there can be only a few enhancements made. You can create enum for your indexes in some separate header file.
Option 1:
If you use only QStackedWidget as a parent menu, you can adjust the constructor:
HomeScreen(QStackedWidget* parent) :
QWidget(parent),
ui(new Ui::HomeScreen)
{
ui->setupUi(this);
}
If you'd want to access QStackedWidget and change an index, you spare m_stacks pointer and use the parent: dynamic_cast<QStackedWidget*>(parent())->setCurrentIndex(1 /* or enum */);.
Option 2:
Leave index changing to the parent MainWindow. Each menu will have a request signal:
signals:
void goToMenuRequest(int index /* or enum */);
which you connect to m_stacked's setCurrentIndex in the constructor of MainWindow.
Not a big things, but It'll make coding easier.

qTreeView scrollToBottom() ignored

Overflowers!
I'm getting crazy trying using scrollTo() in qTreeView (or QListView) widget. To make my question simple I've reduced my code to a simple scrollToBottom() which I can't manage to use as well. Here is the mainWindow code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <qfilesystemmodel.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFileSystemModel *model = new QFileSystemModel(this);
QModelIndex modelRootIndex = model->setRootPath(QDir::rootPath());
ui->treeView->setModel(model);
ui->treeView->setRootIndex(modelRootIndex);
ui->treeView->scrollToBottom();
if(modelRootIndex.isValid()) std::cout << "validIndex" << std::endl;
}
MainWindow::~MainWindow()
{
delete ui;
}
As far as I know it's all ok (I get the "ValidIndex" string on standard output), but the widget doesn't scroll to bottom at all.
I'm using Desktop QT5.0.2 msvc2010 32bit.
Any Idea? Thanks. L
QFileSystemModel and the QFileSystemWatcher are kept up-to-date in a separate thread. Thus, simply setting the model on the tree view does not ensure that the model will be fully populated by the time you make the call to scrollToBottom. Use a single shot timer with a small delay to give the model time to populate.
QTimer::singleShot(1000, ui->treeView, SLOT(scrollToBottom()));
Additionally, (and I don't know your application, so this may or may not be true) it may be confusing to your users that the data they need to see is at the bottom of the view anyway. You may think about whether you can sort the view items in reverse order (thus having the data you need at the top) to avoid scrolling and potentially make the usage more intuitive.
This is because of the asynchronous way that QFileSystemModel works, and what appears to be a bug in Qt that was never fixed: https://bugreports.qt.io/browse/QTBUG-9326
You can work around it by doing QApplication::sendPostedEvents() immediately before the call to scrollTo(), but you must call them in a function that is connected to the directoryLoaded signal:
MyFileBrowser::MyFileBrowser(QWidget *parent) : QWidget(parent), ui(new Ui::MyFileBrowser) {
//...
connect(model, SIGNAL(directoryLoaded(QString)), this, SLOT(dirLoaded(QString)));
QModelIndex folderIndex = model->index("path/to/dir");
files->setCurrentIndex(folderIndex);
files->expand(folderIndex);
}
void WFileBrowser::dirLoaded(QString dir) {
if (dir == model->filePath(files->currentIndex())) {
QApplication::sendPostedEvents(); // booyah!!
files->scrollTo(files->currentIndex(), QAbstractItemView::PositionAtTop);
}
}

Unable to display graphics scene in Qt

I am creating a graphics scene in Qt widgets application. The application runs successfully but no graphics scene is displayed.
The code that I am using to create scene is:
cadviewer::cadviewer(QGraphicsScene *parent) :
QGraphicsScene(parent)
{
QGraphicsScene scene;
scene.addLine(10,10,20,20);
QGraphicsView view(&scene);
view.show();
qDebug() << "cadviewer";
}
The call to the above class is made in another class. The code for the same is:
graphicsarea::graphicsarea(QWidget *parent) :
QWidget(parent),
ui(new Ui::graphicsarea)
{
ui->setupUi(this);
cadviewer viewer;
qDebug() << "graphicsarea";
}
The qDebug used in the two classes is working.
I am unable to figure out what's missing. Do help me out how to display the graphics scene in the main window?
I will not repeat what ratchet freak told you. One of the solution to overcome this problem is just to add QGraphicsScene scene; and QGraphicsView view; in your class attributes.
That way, at the end of the constructor, they will still be alive and displayed !
You have declared only a local Variable in the constructor.
After the Program leaves the constructor your "cadviewer viewer" will be deleted.
graphicsarea::graphicsarea(QWidget *parent) :
QWidget(parent),
ui(new Ui::graphicsarea)
{
ui->setupUi(this);
cadviewer viewer; //delete after constructor
qDebug() << "graphicsarea";
}
Try to use it as a class attribute/member. A Class Member is still alive when the Application leaves the constructor.
Update:
Here is a little example how to add your graphicscene to the area:
QLayout* testLayout = new QVBoxLayout();
cadviewer* view = new cadviewer();
layout->addWidget(view);
graphicsarea* area = new graphicsarea();
area->setLayout(testLayout);
area->show();
Other layouts are possible too.