This question already has an answer here:
Qt Widgets and derived classes
(1 answer)
Closed 3 years ago.
I am trying to make an application with a QGraphicsView in it. I tried to get the position of a mouse when the mouse was pressed with QGraphicsSceneMouseEvent, but it doesn't seem to work. The entire function is never called when I press the mouse.
I would like to make an application that can load an image into the QGraphicsView and then when you press the mouse button, it should add a small circle.
This is my code:
dialog.cpp:
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->graphicsView = new GraphicsView();
}
Dialog::~Dialog()
{
delete ui;
}
graphicsview.h:
#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H
#include <QGraphicsView>
#include <QMouseEvent>
#include <QDebug>
class GraphicsView : public QGraphicsView
{
public:
GraphicsView();
protected:
void mousePressEvent(QMouseEvent *event) override;
private:
QGraphicsScene *scene;
};
#endif // GRAPHICSVIEW_H
graphicsview.cpp:
#include "graphicsview.h"
GraphicsView::GraphicsView()
{
scene = new QGraphicsScene();
this->setScene(scene);
}
void GraphicsView::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
qDebug() << "The left button was pressed!";
}
}
You're almost there.
Just change GraphicsView::GraphicsView() to:
GraphicsView::GraphicsView(QWidget *parent=nullptr) : QGraphicsView(parent) {}
and change ui->graphicsView = new GraphicsView(); to
QVBoxLayout *verticalLayout = new QVBoxLayout(this);
verticalLayout->addWidget(new GraphicsView(this));
This will make the graphics view a parent of the dialog. Make sure that your dialog doesn't have anything in it when you add the code above. Alternatively, you can add a vertical layout to the dialog in your dialog.ui file and do
ui->verticalLayout->addWidget(new GraphicsView(this));
I believe that solves your problem. Respond if you still have any issues.
Make sure you always have a parent parameter. Without it, you're not telling your program where it's supposed to add your widget.
Related
I'm completely new with QT and find it quite confusing.
I created a QListView (called "listview") and would like to show that in my QMessageBox:
const int resultInfo = QMessageBox::information(this, tr("Generate Software"),
tr("The following files will be changed by the program:"),
=> Here the QListView should show up!
QMessageBox::Yes | QMessageBox::No);
if (resultInfo != QMessageBox::Yes) {
return;
}
Is that possible somehow?
QMessageBox is designed to serve texts and buttons only. See link.
If you just want to have a "More details" text, try using detail text property. In that case, you will have to create the message box using its constructor and set the icons, texts explicitly, not using the convenient information() function.
If you still want to have a list view in the message box, you should consider using QDialog, which is the base class of QMessageBox. Small example below:
#include "mainwindow.h"
#include <QDialog>
#include <QListView>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QDialog *dialog = new QDialog{this};
dialog->setWindowTitle(tr("Fancy title"));
auto button = new QPushButton{tr("OK"), this};
connect(button, &QPushButton::clicked, dialog, &QDialog::accept);
QVBoxLayout *layout = new QVBoxLayout{dialog};
layout->addWidget(new QLabel{tr("Description"), this});
layout->addWidget(new QListView{this});
layout->addWidget(button);
dialog->show();
}
MainWindow::~MainWindow()
{
}
I wrote a minimal working example of the problem and I believe it might be a Qt bug. But just in case I wanted to ask.
Here are My classes:
mydialog.h
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
class MyDialog : public QDialog
{
public:
MyDialog(QWidget *parent = 0);
};
mydialog.cpp
#include "mydialog.h"
MyDialog::MyDialog(QWidget *parent):QDialog(parent)
{
QLabel *label = new QLabel("Some random dialog",this);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(label);
this->setLayout(layout);
}
myitem.h
#include <QGraphicsTextItem>
#include <QPainter>
#include <QDebug>
#include "mydialog.h"
class MyItem : public QGraphicsItem
{
public:
MyItem();
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
QRectF boundingRect() const {return boundingBox;}
void setMyDialog(MyDialog *d){ dialog = d; }
private:
QRectF boundingBox;
MyDialog *dialog;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *e);
};
myitem.cpp
MyItem::MyItem()
{
boundingBox = QRectF(0,0,200,100);
}
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
painter->setBrush(QBrush(Qt::red));
painter->drawRect(boundingBox);
}
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *e){
//dialog->exec(); // BUG
//dialog->open(); // BUG
dialog->show(); // WORKS!
}
test.h
#include "myitem.h"
namespace Ui {
class Test;
}
class Test : public QMainWindow
{
Q_OBJECT
public:
explicit Test(QWidget *parent = 0);
~Test();
protected:
void resizeEvent(QResizeEvent *e);
private:
Ui::Test *ui;
MyDialog *diag;
};
And test.cpp
Test::Test(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Test)
{
ui->setupUi(this);
ui->graphicsView->setScene(new QGraphicsScene(this));
diag = new MyDialog(this);
}
void Test::resizeEvent(QResizeEvent *e){
ui->graphicsView->setSceneRect(0,0,ui->graphicsView->width(),ui->graphicsView->height());
ui->graphicsView->scene()->clear();
MyItem *item = new MyItem();
item->setMyDialog(diag);
ui->graphicsView->scene()->addItem(item);
}
Test::~Test()
{
delete ui;
}
So here is what happens (tested on Qt 5.7 and Qt 5.6). If the dialog is opened with either exec or open then, after it is closed ALL further mouse clicks ANYWHERE on the screen will open up the dialog again, making it impossible to interact with anything else drawn in there. This happens ONLY after it is opened for the first time. If i resize the screen, the item is recreated and I can click normally again. If I again click on the red box, then again all further clicks anywhere on the screen open up the dialog
However if the Dialog is opened by show, then it works as expected, only showing again if I click on the red rectangle.
Now the obvious problem is that exec make the dialog block execution until it is closed, but show doesn't. I can program around this using signals, but my question is why? and Is this a bug?
It seems that MyItem's reimplementation of mousePressEvent needs some behavior provided by default implementation. Here is the code, works fine in my machine:
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
dialog->exec(); // WORKS
//dialog->open(); // WORKS
//dialog->show(); // WORKS
QGraphicsItem::mousePressEvent(event);
}
I'm using QtCreator to build an interface application.
I'm just getting used to Qt and toying around trying to draw stuff on a QtGraphicsView.
Since I created my interface with the editor, I am retrieving my objects in the code like so (please tell me if this is wrong).
this->m_graphView = this->findChild<QGraphicsView *>("graphicsView");
this->m_graphScene = this->m_graphView->scene();
I have buttons on the interface and already created slots to react to the clicked event.
I'm just trying to draw something (anything) on the graphics view that is on my MainWindow (geometry : [(10,10), 320x240]).
I've been reading examples online and I can't make anything work.
My current code is as follows :
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->m_graphView = this->findChild<QGraphicsView *>("graphicsView");
this->m_graphScene = this->m_graphView->scene();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btnDown_clicked()
{
qDebug() << "STUB : button DOWN";
m_graphScene->addLine(0, 0, 42, 42, QPen(QBrush(Qt::black),1));
m_graphView->show();
}
void MainWindow::on_btnLeft_clicked()
{
qDebug() << "STUB : button LEFT";
}
void MainWindow::on_btnUp_clicked()
{
qDebug() << "STUB : button UP";
}
void MainWindow::on_btnRight_clicked()
{
qDebug() << "STUB : button RIGHT";
}
void MainWindow::on_btnShoot_clicked()
{
qDebug() << "STUB : button SHOOT";
}
But annoyingly, it doesn't draw anything and I even get this error when the addLine method is called
QGraphicsScene::addItem: item has already been added to this scene
What's wrong in my code and/or my ways of doing things?
I just want to draw something but can't make it, thank you.
retrieving widget in form
you can get graphicsView pointer (and its scene) more easier.
"ui" member has the pointer to widgets arranged in .form file.
(If you please, see "ui_mainwindow.h" file)
// assign to pointer
QGraphicsView *view = ui->graphicsView;
view->...
// or directly (I like this)
ui->graphicsView->...
so, Mainwindow class don't need "m_graphicsView" member.
graphics view
QGraphicsView need to set scene.(It has no scene at first)
We have to create QGraphicsScene ourselves.
So Mainwindow class need "m_graphicsScene" member.
m_graphicsScene = new QGraphicsScene(this);
ui->graphicsView->setScene(m_graphicsScene);
drawing more easier
If you just want to draw something, you can override "paintEvent" method.
PaintEvent is QWidget's virtual method.
in .h file:
protected:
void paintEvent(QPaintEvent *event);
in .cpp file:
void MainWindow::paintEvent(QPaintEvent *event)
{
// unuse
Q_UNUSED(event);
// pass "this" pointer to painter
QPainter painter(this);
// setPen
// QPen can take "Qt::black" directly as first arg (without QBrush() constructor)
painter.setPen(QPen(Qt::black), 1);
// draw line
painter.drawLine(0, 0, 42, 42);
}
please enjoy Qt!
hi i create a dockwidget which i am starting it as hidden. The problem is that afterwards i cannot show it, while i can get the status correctly from the isHidden() function. The weird thing is that if i start the dockwidget not hidden, it works perfect. I am including an example that reproduces this strange behaviour.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtGui>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void showDock();
private:
Ui::MainWindow *ui;
QDockWidget *dock;
QPushButton *button;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// QMainWindow mainWindow;
// QDockWidget *dock = new QDockWidget(&mainWindow);
QDialog *dockDialog = new QDialog(this); // <---------edit: you need to create a parent widget for the dock
dock = new QDockWidget(dockDialog);
dock->setStyleSheet("QDockWidget { font: bold }");
dock->setFloating(true);
dock->setFeatures(QDockWidget::DockWidgetVerticalTitleBar | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
QAbstractButton *floatButton = dock->findChild<QAbstractButton*>("qt_dockwidget_floatbutton");
if(floatButton)
floatButton->hide();
dock->setAllowedAreas( Qt::NoDockWidgetArea );
dock->setWindowTitle("Tools");
this->addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
QMainWindow *window = new QMainWindow(dock); // <------edit: set the dock to be the parent for the window
window->setWindowFlags(Qt::Widget);
QToolBar *bar = new QToolBar(window);
bar->setMovable(false);
bar->addAction("Select");
bar->addAction("Polygon");
bar->addAction("Brush");
bar->addAction("Erazer");
bar->addSeparator();
bar->addAction("Mark");
bar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
bar->setOrientation(Qt::Vertical);
window->addToolBar(Qt::LeftToolBarArea, bar);
window->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
window->setParent(dock);
dock->setWidget(window);
dock->hide(); // <------------ comment this line and it will work, edit: you do not need to do that anymore, it is working nicely now
button = new QPushButton("show", this);
button->setCheckable(true);
QObject::connect(button, SIGNAL(clicked()), this, SLOT(showDock()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showDock()
{
// qDebug() << "hello";
if(button->isChecked()){
if(dock->isHidden()){
qDebug() << "hidden";
dock->setFloating(true); // <-----edit: you need to add these lines in order to be able to see the dialog that contains the dock widget, i do not know why i need to that again since in the initialization i already specifying that my dock is floatable
QAbstractButton *floatButton = dock->findChild<QAbstractButton*>("qt_dockwidget_floatbutton"); // <---------edit: add this lines in order to get rid off the floating button
if(floatButton)
floatButton->hide(); // <----edit: the same as previous
dock->show();
}
}
if(!button->isChecked()){
if(!dock->isHidden()){
qDebug() << "not hidden";
dock->hide();
}
}
}
as it is now the above code the dockwidget is not appearing in the screen. If you try to comment the line that i am specifying in the mainwindow.cpp it works, but the point is that i want to start the dockwidget hidden. Does someone have any idea, of what is happening.
Thanks.
Parent of the QDock control should be your window widget, and not object of the MainWindow class.
So, you need to replace this:
dock = new QDockWidget(this);
with this:
QMainWindow *window = new QMainWindow(0); // I smell a potential leak here (set the parent!)
dock = new QDockWidget(window);
Control is visible when you press Show button in the first case, too, but it is somewhere outside the window, so you may not see it. Also, there is no need to create separate instance of QMainWindow inside class that is already inheriting QMainWindow.
I see you show()/hide() your dockwidget. I haven't tried it myself but maybe you should hide and show the dockwdiget with the QDockWidget::toggleViewAction method.
I have built an app in Qt that contains two buttons: an exit button and an import button. When the import button is pushed, a list of buttons is shown in a scrollarea on the screen (the file loggers.csv contains the data 1;2;3;4;5;).
It all works fine, but when I push the exit button (which of course should close everything), the app is not stopped properly (the stop button of Qt is still active, and the play button isn't). When I run the debugger and push the exit button it gives an error: Invalid address specified to RtlFreeHeap( 0ADF0000, 0028FE40 ). Can anybody help me?
main
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.showFullScreen();
return a.exec();
}
Mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtGui>
#include "logger.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QPushButton exit_btn;
QPushButton import_btn;
private slots:
void createMenus();
void exit();
void import();
private:
int window_width;
int window_height;
int numLoggers;
int numSelected;
QVector<Logger*> loggers;
QScrollArea * scroll_area;
QVBoxLayout scrollLayout;
QWidget viewport;
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QtGui"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
window_width = QApplication::desktop()->width();
window_height = QApplication::desktop()->height();
createMenus();
connect(&exit_btn,SIGNAL(clicked()),this,SLOT(exit()));
connect(&import_btn,SIGNAL(clicked()),this,SLOT(import()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::createMenus()
{
import_btn.setParent(ui->centralWidget);
import_btn.setGeometry(400,300,100,100);
import_btn.setText("IMPORT");
exit_btn.setText("EXIT");
exit_btn.setParent(ui->centralWidget);
exit_btn.setGeometry(window_width-50,12,32,32);
viewport.setLayout(&scrollLayout);
viewport.resize(0,0);
scroll_area = new QScrollArea(ui->centralWidget);
scroll_area->setGeometry(0,66,317,window_height-116);
scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setWidget(&viewport);
scroll_area->setGeometry(0,97,317,window_height-228);
scrollLayout.setMargin(0);
scrollLayout.setSpacing(0);
}
void MainWindow::exit()
{
close();
qApp->quit();
}
void MainWindow::import()
{
numSelected=0;
QFile f("Loggers3.csv");
if (f.open(QIODevice::ReadOnly))
{
numLoggers=0;
QString data;
data = f.readAll();
QStringList vals = data.split(';');
while(vals.size()>=1)
{
Logger * logger = new Logger;
logger->setNumber(vals[0].toInt());
vals.removeAt(0);
loggers<<logger;
numLoggers++;
}
f.close();
for(int i=0; i<numLoggers;i++)
{
loggers[i]->createButtons();
scrollLayout.addWidget(loggers[i]->button);
}
viewport.resize(367,numLoggers*60);
}
}
logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <QtGui>
class Logger : public QWidget
{
Q_OBJECT
public:
explicit Logger(QWidget *parent = 0);
~Logger();
int number;
QLabel num;
QToolButton * button;
bool checked;
signals:
public slots:
void setNumber(int number);
void createButtons();
};
#endif // LOGGER_H
logger.cpp
#include "logger.h"
#include <QtGui>
Logger::Logger(QWidget *parent) :
QWidget(parent)
{
button = new QToolButton;
button->setCheckable(true);
button->setMinimumSize(317,60);
button->setStyleSheet("QToolButton{background-image: url(images/btn_bg); border:none}");
}
Logger::~Logger()
{
}
void Logger::setNumber(int logNumber)
{
number=logNumber;
}
void Logger::createButtons()
{
QLayout * layout = new QHBoxLayout;
QSpacerItem *spacer = new QSpacerItem(120, 31, QSizePolicy::Maximum, SizePolicy::Maximum);
num.setStyleSheet("color: white; font: bold 16px");
num.setText(QString::number(number));
layout->addWidget(&num);
layout->addItem(spacer);
button->setLayout(layout);
}
I'm not entirely certain about what you are trying to achieve... but your problem lies with these two lines:
viewport.setLayout(&scrollLayout);
viewport.resize(0,0);
In the documentation for the QWidget class it states that:
If there already is a layout manager installed on this widget, QWidget
won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.
This is where your problem lies. Don't believe me, add this check before those two lines of code.
if(layout()){
qDebug() << "Another layout exists";
}
Source: QVBoxLayout Class Reference
The QVBoxLayout class lines up widgets vertically.
This class is used to construct vertical box layout objects. See QBoxLayout for details.
The simplest use of the class is like this:
QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
window->show();
First, we create the widgets we want in the layout. Then, we create the QVBoxLayout object and add the widgets into the layout. Finally, we call QWidget::setLayout() to install the QVBoxLayout object onto the widget. At that point, the widgets in the layout are reparented to have window as their parent.
Critical source of error in your project:
Widgets should be constructed on the heap because they will be deleted automatically when their parents are deleted. You have a custom widget class that you instantiate on the heap. The members should also go on the heap. Also, you should consider using the parent /child hierarchy in your GUI code to ensure proper memory management and proper deletion.
In my experience, if your program stops in RtlFreeHeap it is a good sign of memory corruption.
When calling
import_btn.setParent(ui->centralWidget);
centralWidget takes ownership of import_btn. That means, when centralWidget is deleted (which happens as part of delete ui;in your MainWindow's destructor), it will call delete on your member variable!
This leads to the reported memory corruption.
You need to allocate your QPushButton's dynamically, not as a plain member variable. So make them QPushButton*.
Here's how I did it from mainwindow.cpp, thanks to and this question: How to create a correct exit button in qt
QPushButton * quit_btn = new QPushButton(this);
quit_btn->setGeometry(540,440,93,27);
quit_btn->setText("Exit");
QObject::connect(quit_btn,SIGNAL(clicked()),qApp,SLOT(quit()));
Works flawlessly :D