I want to draw on a QMainWindow: Until now, I have set up a simple project and entered some code into the constructor. The QGraphicsView shows a blank canvas. What else do I have to do to make my drawings visible?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene scene(0,0,800,600);
this->view.setScene(&scene);
this->setCentralWidget(&view);
scene.addText("Hello, world!");
QPen pen(Qt::green);
scene.addLine(0,0,200,200,pen);
scene.addEllipse(400,300,100,100,pen);
//make a point:
double rad = 1;
scene.addEllipse(10-rad, 10-rad, rad*2.0, rad*2.0,pen,QBrush(Qt::SolidPattern));
scene.update();
view.show();
}
QGraphicsScene scene is a local variable of MainWindow constructor. Once constructor ended, the variable is destroyed, which leads to undefined behavior when the view tries to access the scene. Your program may crash.
Make it a member variable of the MainWindow class and initialize with new in constructor. Or at least try QGraphicsScene *scene = new QGraphicsScene(0,0,800,600,this), it will get deleted by Qt when MainWindow is destroyed.
Related
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
I created a .ui file using Qt Designer and in the file I created a PushButton which is disabled initially, I also have a LineEdit. I want to connect LineEdit and PushBotton so that when text changed in LineEdit , the PushButton will be enabled, But I don't find any such option in Signals and slots. Can anyone help?
You have to write a custom slot (which is pretty easy).
Add this to your MainWindow declaration (.h file):
private slots:
void checkInput(const QString &text);
Your .cpp file:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->lineEdit, SIGNAL(textChanged(QString)), this, SLOT(checkInput(QString)));
}
void MainWindow::checkInput(const QString &text)
{
ui->pushButton->setEnabled(!text.isEmpty());
}
To add this slot to Qt Designer, do the following:
Right click on your MainWindow, "Change signals/slots";
Add your custom slot ("Plus" button) by entering checkInput();
After this you will be able to connect your custom slot via Qt Designer.
In Qt 5, you generally don't need trivial private slots and should use lambdas instead:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->lineEdit, &QLineEdit::textChanged, [this](const QString & text) {
ui->pushButton->setEnabled(!text.isEmpty());
});
...
}
I'm a beginner with Qt and I haven't understand yet the layout on centralWidget.
I have a custom QWidget subclass (with a .ui and the cpp class) that I want to add to the central Widget.
I'd like to understand how to say to the QMainWindow subclass to resize and fit the content whenever I add something.
I've tried with adjustSize method both on mainwindow and on centralWidget objects but nothing change..
Anyway, I'm adding the widget in this way:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
MyWidget *w = new Undistort();
w->setParent(this->centralWidget());
}
some advice?
Given example, depending on Pixmap size, QMainWindow will resize. Generally this is not the ideal case, as a user MainWindow need to display on the desktop, It should not be more that your desktop screen size. I am not sure you are actually looking for this. Copied fromSO Ans
#include "mainwindow.h"
#include <QLabel>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindowClass)
{
ui->setupUi(this);
QPixmap pix;
pix.load("C:\\Users\\user\\Desktop\\Uninstallation failure2.png");
//Replace with ImageLabel2
QLabel* image = new QLabel(this);
image->setPixmap(pix);
QHBoxLayout* hbox = new QHBoxLayout(this);
hbox->addWidget(image);
QWidget* centreWidget = new QWidget();
//QMainwindow, having a feature called centreWidget, to set the layout.
centreWidget->setLayout( hbox );
setCentralWidget( centreWidget );
}
I'm using the answer to this SO question to make a custom image widget which automatically scales correctly. It works fine but now I am trying to center the image widget instance at the center of my main window.
My idea was to create a QHBoxLayout, add the image widget to that and then add the hBox instance to the ui->verticalLayout.
Doesn't work. The image still displays flush left with the error message: QLayout: Attempting to add QLayout "" to MainWindow "MainWindow", which already has a layout
I then tried a few variations on 'setAlignment` but then the image doesn't appear at all. My simple test code is below.
What am I missing here?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPixmap pix;
pix.load("/Users/home/Desktop/test.jpg");
ImageLabel2* image = new ImageLabel2(this);
image->setPixmap(pix);
QHBoxLayout* hbox = new QHBoxLayout(this);
hbox->addWidget(image);
// hbox->setAlignment(image,Qt::AlignCenter);
hbox->setAlignment(Qt::AlignHCenter);
// ui->verticalLayout->addLayout(hbox);
ui->verticalLayout->addLayout(hbox);
// ui->verticalLayout->addWidget(image);
// ui->verticalLayout->setAlignment(image,Qt::AlignCenter);
// ui->verticalLayout->setAlignment(Qt::AlignHCenter);
}
Try this:
QHBoxLayout* hbox = new QHBoxLayout(this);
hbox->addStretch();
hbox->addWidget(image);
hbox->addStretch();
None of the suggestions here worked for me. Don't know why and it seems hard to debug layout problems
However a reply to my question on the Qt Project.org site works perfectly. So not my solution but I am posting it here since this "centering/resizing image" issue seems a common problem.
class CustomWidget : public QWidget {
public:
CustomWidget(const QPixmap &p, QWidget* parent = 0)
: QWidget(parent), pixmap(p) {}
void paintEvent(QPaintEvent * e)
{
QRect srcRect(QPoint(), pixmap.size());
QSize dstSize = srcRect.size().scaled(
e->rect().size(), Qt::KeepAspectRatio);
QRect dstRect(QPoint((width() - dstSize.width())/2,
(height() - dstSize.height())/2), dstSize);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.drawPixmap(dstRect, pixmap, srcRect);
}
private:
QPixmap pixmap;
};
and then in the main window:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setCentralWidget(new CustomWidget(QPixmap("test.png")));
}
main window(QMainwindow) API setCentralWidget to align the widget on center.
Note:Concept of centrewidget, is to differentiate the docking area( Left, Right, Bottom, Top ).
with out a centre how some one know who is where. when we are developing with QMainWindow, you can see in UI_MainWindow.h, setting Centrewidget with dummy QWidget
Below code will work
#include "mainwindow.h"
#include <QLabel>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindowClass)
{
ui->setupUi(this);
QPixmap pix;
pix.load("C:\\Users\\user\\Desktop\\Uninstallation failure2.png");
//Replace with ImageLabel2
QLabel* image = new QLabel(this);
image->setPixmap(pix);
QHBoxLayout* hbox = new QHBoxLayout(this);
hbox->addWidget(image);
QWidget* centreWidget = new QWidget();
//QMainwindow, having a feature called centreWidget, to set the layout.
centreWidget->setLayout( hbox );
setCentralWidget( centreWidget );
}
MainWindow::~MainWindow()
{
}
I have the following hierarchy in my Qt Application:
QMainWindow > QWidget (centralWidget) > QWidget (subclassed) > QLabel
Initialization code in my QMainWindow code:
centralWidget = new QWidget();
centralWidget->setGeometry(0,0,width,height);
chatWidget=new ChatWidget(this); // the subclassed QWidget
setCentralWidget(centralWidget);
In my subclassed QWidget initialization (which happens at the same time than the Qt App initialization) I have the following code:
ChatWidget::ChatWidget(QWidget *parent):QWidget(parent)
{
QLabel *lbl;
lbl=new QLabel(this);
lbl->setText("Hello World 1"); <-- Is properly Display
}
void ChatWidget::displayChatAfterButtonPressed()
{
QLabel *lbl;
lbl=new QLabel(this);
lbl->setText("Hello World 2"); <-- Does NOT appear
}
When the QLabel is added from the class initialization then the message is well displayed in the widget.
However when I launch the same code after a button pressed (via a function in the same QWidget subclass), then the text does not appear on screen.
I don't want to use layouts as I need to exactly position my labels.
Tried to repaint, but didn't help neither.
How can I properly and dynamically display a label after the initialization is done ?
Widgets when they are visible for the first time call to be visible to their children, but since you are creating it afterwards they probably are not calling that method, a possible solution is to call the show method.
void ChatWidget::displayChatAfterButtonPressed()
{
QLabel *lbl;
lbl=new QLabel(this);
lbl->setText("Hello World 2");
lbl->show();
}
comment: it seems strange to me that the QMainWindow you set a central widget and then create the chatWidget as a parent to the QMainWindow, it is generally not recommended to add children to the QMainWindow because it has a given structure, what should be done is to place it inside the centralwidget.
We need to show the label created by button click, cause centralwidget was already painted.
Here is a working example, I added this as answer also I noticed better adding chatWidget as child to centralWidget where in your original code its added to the UI .. this is your choice.
Mainwindow:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
//
ui->setupUi(this);
centralWidget = new QWidget();
centralWidget->setGeometry(width,height);
chatWidget=new ChatWidget(centralWidget); // the subclassed QWidget
setCentralWidget(centralWidget);
// added testing
QPushButton *btn = new QPushButton("MyButton",centralWidget);
btn->setGeometry(100,100,100,100);
btn->setMaximumSize(100,100);
connect(btn,&QPushButton::clicked, chatWidget, &ChatWidget::displayChatAfterButtonPressed);
}
and chatWidget:
ChatWidget::ChatWidget(QWidget *parent):QWidget(parent)
{
QLabel *lbl;
lbl=new QLabel(this);
lbl->setText("Hello World 1");
}
void ChatWidget::displayChatAfterButtonPressed()
{
QLabel *lbl;
lbl=new QLabel(this);
lbl->setText("Hello World 2");
lbl->show();
}