Unable to display graphics scene in Qt - c++

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.

Related

Qt3D input handling system works differently depending on the place it was created

I was implementing a custom camera controller for Qt3DRender::QCamera and I faced a rather strange behavior of the Qt3DInput::QMouseHandler. Depending on the environment it was created it either responds to mouse events or not. There are two cases: create both the device and the handler in the MainWindow object or create them inside my camera controller class (it only works in the first case).
First case:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto* window = new Qt3DExtras::Qt3DWindow();
auto* window_container = QWidget::createWindowContainer(window, this, Qt::Widget);
setCentralWidget(window_container);
auto* scene = new Qt3DCore::QEntity();
window->setRootEntity(scene);
auto* mouse_device = new Qt3DInput::QMouseDevice(scene);
auto* mouse_handler = new Qt3DInput::QMouseHandler(scene);
mouse_handler->setSourceDevice(mouse_device);
connect(mouse_handler, &Qt3DInput::QMouseHandler::positionChanged,
[](Qt3DInput::QMouseEvent* event)
{
qDebug() << "I am actually printing to console!!!";
});
}
Second case:
class CustomCameraController final : public Qt3DCore::QNode
{
Q_OBJECT
public:
explicit CustomCameraController(Qt3DCore::QNode* parent = nullptr)
: Qt3DCore::QNode(parent),
mouse_device(new Qt3DInput::QMouseDevice(this)),
mouse_handler(new Qt3DInput::QMouseHandler(this))
{
mouse_handler->setSourceDevice(mouse_device);
connect(mouse_handler, &Qt3DInput::QMouseHandler::pressAndHold, this,
&CustomCameraController::MousePositionChanged);
}
public slots:
void MousePositionChanged(Qt3DInput::QMouseEvent* event)
{
qDebug() << "I am not printing anything...";
}
protected:
Qt3DInput::QMouseDevice* mouse_device;
Qt3DInput::QMouseHandler* mouse_handler;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto* window = new Qt3DExtras::Qt3DWindow();
auto* window_container = QWidget::createWindowContainer(window, this, Qt::Widget);
setCentralWidget(window_container);
auto* scene = new Qt3DCore::QEntity();
window->setRootEntity(scene);
auto* controller = new CustomCameraController(scene);
}
All the unnecessary code was removed. The main.cpp file was automatically generated by Qt Framework
I searched all through the Qt documentation and found nothing that would help me in this scenario. I also noticed that if the device and the handler are initialized in the constructor with parent as the parameter it does not help. Furthermore, I tried to create the device and the handler in the MainWindow scope and pass them to the controller via some setter function but it does not help neither.
So the questions I want to ask: what is the proper way of using Qt3DInput::QMouseDevice and Qt3DInput::QMouseHandler? Is there a better workaround for implementing input handling for my custom camera controller?
Update 1:
You should add the controller class declaration to the header instead of main window source file. Here are all the includes and the qmake options you are going to need:
#include <Qt3DCore/QNode>
#include <Qt3DInput/QMouseDevice>
#include <Qt3DInput/QMouseHandler>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DCore/QEntity>
3dinput 3dcore 3dextras
Looks like you just connected to different signals.
In first case you are using &Qt3DInput::QMouseHandler::positionChanged which will be sended quite often. In second one - &Qt3DInput::QMouseHandler::pressAndHold which will be sended when a mouse button is pressed and held down.
After fix both logging functions are called.

How to update QGraphicsScene in a QMainWindow

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.

Update MainWindow for dialog

I have a MainWindow with a menu that opens a dialog for registration. How can I update the tableView in the MainWindow after the registration?
Here is my MainWindow implementation:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
ui->setupUi(this);
}
void MainWindow::list()
{
qDebug() << "test";
QSqlQueryModel *model = new QSqlQueryModel();
//model->clear();
model->setQuery("SELECT test_qt FROM db_qt WHERE strftime('%Y-%m-%d', date)='"+dateTime.toString("yyyy-MM-dd")+"'");
model->setHeaderData(0, Qt::Horizontal, tr("qt_test"));
ui->tableView->setModel(model);
}
void MainWindow::on_actionMenu_triggered()
{
dialog_test->show();
}
Here is my Dialog implementation
Dialog_test::Dialog_test(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog_test)
{
ui->setupUi(this);
}
void Dialog_test::insert_date(){
QSqlQuery qry;
qry.prepare("INSERT INTO db_qt(test_qt) VALUES (?)");
qry.addBindValue(id);
if (qry.lastInsertId()>0){
QMessageBox::information(this,"test", "Success");
MainWindow *mw = new MainWindow(this);
mw->list(); // I call back list, but not update the tableView the MainWindow.
}
}
The following line in your code
MainWindow *mw = new MainWindow(this);
creates a new main window and updates the list of it. I assume this actually happens, but the window is never shown so you do not see any of it. What you actually want to do is update the list of your existing main window.
There are basically two ways of doing that. You can either obtain a pointer to the existing main window (which can be provided to the constructor of the dialog or a method of its own) or use the Signals and Slots concept of Qt which is the way to go in my opinion.
First of all, you define the signal in the header of the dialog:
...
signals:
void user_registered();
...
Then you emit the signal in your function
//MainWindow *mw = new MainWindow(this);
//mw->list();
emit this->user_registered();
Make sure the list() method is declared as a SLOT in the MainWindow header
Connect the signal in the MainWindow constructor to call the list() slot:
...
QObject::connect(this->dialog_test, SIGNAL(user_registered()), this, SLOT(list()));
...
With this approach, the dialog does not need to know the main window at all. It basically just tells anyone who is interested that a user registered and the main window acts on it completly by itself.

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.