Creating a basic paint program

Since we have learned so much about the QPainter class and how to use it to display graphics on screen, I guess it's time for us to do something fun so that we can put our knowledge into practice.

In this recipe, we will learn how to make a basic paint program that allows us to draw lines on a canvas with different brush sizes and colors. We will also learn how to use the QImage class and the mouse events in order to construct the paint program.

How to do it…

Let us start our fun project through the following steps:

  1. Again, we start by creating a new Qt Widgets Application project and removing the tool bar and status bar. We will keep the menu bar this time.
  2. After that, set up the menu bar like so:
    How to do it…
  3. We will leave the menu bar as it is for the moment and let's proceed to mainwindow.h. First, include the following header files as it's required for the project:
    #include <QPainter>
    #include <QMouseEvent>
    #include <QFileDialog>
  4. Next, declare the variables that we'll be using for this project, like so:
    private:
    Ui::MainWindow *ui;
    
    QImage image;
    bool drawing;
    QPoint lastPoint;
    int brushSize;
    QColor brushColor;
    
  5. Then, declare the event callback functions, which are inherited from the QWidget class. These functions will be triggered by Qt when the respective event happens. We will override these functions and tell Qt what to do when these events get called:
    public:
      explicit MainWindow(QWidget *parent = 0);
      ~MainWindow();
    
      virtual void mousePressEvent(QMouseEvent *event);
      virtual void mouseMoveEvent(QMouseEvent *event);
      virtual void mouseReleaseEvent(QMouseEvent *event);
      virtual void paintEvent(QPaintEvent *event);
      virtual void resizeEvent(QResizeEvent *event);
    
  6. After that, go to mainwindow.cpp and add the following code to the class constructor for setting up some of the variables:
    MainWindow::MainWindow(QWidget *parent) :
      QMainWindow(parent), ui(new Ui::MainWindow)
    {
      ui->setupUi(this);
    
      image = QImage(this->size(), QImage::Format_RGB32);
      image.fill(Qt::white);
    
      drawing = false;
      brushColor = Qt::black;
      brushSize = 2;
    }
  7. Next, we will construct the mousePressEvent() event and tell Qt what to do when the left mouse button is pressed:
    void MainWindow::mousePressEvent(QMouseEvent *event)
    {
      if (event->button() == Qt::LeftButton)
      {
        drawing = true;
        lastPoint = event->pos();
      }
    }
  8. Then, we will construct the mouseMoveEvent() event and tell Qt what to do when the mouse is moving. In this case, we want to draw the lines on the canvas if the left mouse button is being held:
    void MainWindow::mouseMoveEvent(QMouseEvent *event)
    {
      if ((event->buttons() & Qt::LeftButton) && drawing)
      {
        QPainter painter(&image);
        painter.setPen(QPen(brushColor, brushSize, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
        painter.drawLine(lastPoint, event->pos());
    
        lastPoint = event->pos();
        this->update();
      }
    }
  9. After that, we will also construct the mouseReleaseEvent() event, which will be triggered when the mouse button is released:
    void MainWindow::mouseReleaseEvent(QMouseEvent *event)
    {
      if (event->button() == Qt::LeftButton)
      {
        drawing = false;
      }
    }
  10. Once you're done with that, we will proceed to the paintEvent() event, which is surprisingly simple compared to the other examples we have seen in previous sections:
    void MainWindow::paintEvent(QPaintEvent *event)
    {
      QPainter canvasPainter(this);
      canvasPainter.drawImage(this->rect(), image, image.rect());
    }
  11. Remember we have a menu bar sitting around doing nothing? Let's right-click on each of the actions below the GUI editor and select Go to slot… in the pop-up menu. We want to tell Qt what to do when each of these options on the menu bar is selected:
    How to do it…
  12. Then, select the default slot called triggered() and press the OK button. Qt will automatically generate a new slot function in both your mainwindow.h and mainwindow.cpp. Once you are done with all the actions, you should see something like this in your mainwindow.h:
    private slots:
      void on_actionSave_triggered();
      void on_actionClear_triggered();
      void on_action2px_triggered();
      void on_action5px_triggered();
      void on_action10px_triggered();
      void on_actionBlack_triggered();
      void on_actionWhite_triggered();
      void on_actionRed_triggered();
      void on_actionGreen_triggered();
      void on_actionBlue_triggered();
  13. Next, we will tell Qt what to do when each of these slots is triggered:
    void MainWindow::on_actionSave_triggered()
    {
      QString filePath = QFileDialog::getSaveFileName(this, "Save Image", "", "PNG (*.png);;JPEG (*.jpg *.jpeg);;All files (*.*)");
    
      if (filePath == "")
        return;
    
      image.save(filePath);
    }
    void MainWindow::on_actionClear_triggered()
    {
      image.fill(Qt::white);
      this->update();
    }
    void MainWindow::on_action2px_triggered()
    {
      brushSize = 2;
    }
    void MainWindow::on_action5px_triggered()
    {
      brushSize = 5;
    }
    void MainWindow::on_action10px_triggered()
    {
      brushSize = 10;
    }
    void MainWindow::on_actionBlack_triggered()
    {
      brushColor = Qt::black;
    }
    
    void MainWindow::on_actionWhite_triggered()
    {
      brushColor = Qt::white;
    }
    void MainWindow::on_actionRed_triggered()
    {
      brushColor = Qt::red;
    }
    void MainWindow::on_actionGreen_triggered()
    {
      brushColor = Qt::green;
    }
    void MainWindow::on_actionBlue_triggered()
    {
      brushColor = Qt::blue;
    }
  14. If we compile and run the program now, we will get a simple but usable paint program:
    How to do it…

How it works...

In this example, we created a QImage widget when the program started. This widget acts as the canvas and it will follow the size of the window whenever the window gets resized.

In order to draw something on the canvas, we will need to use the mouse events provided by Qt. These events will tell us the position of the cursor and we will be able to use this information to change the pixels on the canvas.

We use a Boolean variable called drawing to let the program know whether it should start drawing when a mouse button is pressed. In this case, when the left mouse button is pressed, the variable drawing will be set to true. We also save the current cursor position to the lastPoint variable when the left mouse button is pressed, so that Qt will know where it should start drawing.

When the mouse moves, the mouseMoveEvent() event will be triggered by Qt. This is where we need to check whether the drawing variable is set to true. If it is, then QPainter can start drawing the lines onto the QImage widget based on the brush settings that we provide.

The brush settings consist of the brush color as well as the brush size. These settings are being saved as variables and can be altered by selecting a different setting from the menu bar.

Please remember to call the update() function when the user is drawing on the canvas. Otherwise, the canvas will remain empty even though we have changed the pixel information of the canvas. We also have to call the update() function when we select File | Clear from the menu bar to reset our canvas.

In this example, we use QImage::save() to save the image file, which is very easy and straightforward. We use the file dialog to let the user decide where to save the image and its desired file name. Then, we pass the information to QImage and it will do the rest by itself. If we don't specify the file format to the QImage::save() function, QImage will try to figure it out by looking at the extension of the desired file name.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.42.243