Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Screenshot selection



  • Hello,
    I'd want to make a desktop application similar to the Windows Snipping Tool, wherein you could take a screenshots. I have a problem with implementation of screenshot selection - I have no idea, how to write this piece of code. I'd like to:

    • dim a bit the picture;
    • user selection has to be in default color;
    • user could resize this selection after taking a screenshot.

    Here is the example, how it should work:
    0_1539893499125_screenshot.jpg




  • Moderators

    Hi, welcome to the forum.

    It's pretty straightforward.
    To actually capture entire desktop (remember multiple screen setups) you can do something like this:

    QPixmap grabScreenshot()
    {
        QPixmap desktopPixmap = QPixmap(QApplication::desktop()->geometry().size());
        QPainter p(&desktopPixmap);
    
        for (QScreen* screen : QApplication::screens())
            p.drawPixmap(screen->geometry().topLeft(), screen->grabWindow(0));
    
        return desktopPixmap;
    }
    

    Then create a class like this:

    class SelectorWidget : public QDialog
    {
        Q_OBJECT
    public:
        explicit SelectorWidget(QWidget* parent = nullptr);
        QPixmap selectedPixmap;
    protected:
        void mousePressEvent(QMouseEvent* event) override;
        void mouseReleaseEvent(QMouseEvent* event) override;
        void mouseMoveEvent(QMouseEvent* event) override;
        void paintEvent(QPaintEvent* event) override;
    private:
        QPixmap desktopPixmap;
        QRect selectedRect;
    };
    

    Make the window borderless, translucent and covering entire desktop. Grab the screenshot in the constructor:

    SelectorWidget::SelectorWidget(QWidget* parent) : QDialog(parent, Qt::FramelessWindowHint)
    {
        setAttribute(Qt::WA_TranslucentBackground);
        setGeometry(QApplication::desktop()->geometry());
    
        desktopPixmap = grabScreenshot();
    }
    

    On mouse press store the first corner of the selection rectangle:

    void SelectorWidget::mousePressEvent(QMouseEvent* event)
    {
        selectedRect.setTopLeft(event->globalPos());
    }
    

    On mouse move store the second corner and repaint:

    void SelectorWidget::mouseMoveEvent(QMouseEvent* event)
    {
        selectedRect.setBottomRight(event->globalPos());
        update();
    }
    

    On mouse release cut the selected part from the original screenshot and end dialog via accept():

    void SelectorWidget::mouseReleaseEvent(QMouseEvent* event)
    {
        selectedPixmap = desktopPixmap.copy(selectedRect.normalized());
        accept();
    }
    

    On paint draw the original screenshot, then dim the part around selection and draw a red selection rectangle:

    void SelectorWidget::paintEvent(QPaintEvent*)
    {
        QPainter p (this);
        p.drawPixmap(0, 0, desktopPixmap);
    
        QPainterPath path;
        path.addRect(rect());
        path.addRect(selectedRect);
        p.fillPath(path, QColor::fromRgb(255,255,255,200));
    
        p.setPen(Qt::red);
        p.drawRect(selectedRect);
    }
    

    That's it. Now you can use that widget like this to for example copy that selection to clipboard:

    SelectorWidget w;
    if (w.exec() == QDialog::Accepted)
        qApp->clipboard()->setPixmap(w.selectedPixmap);
    


  • @Chris-Kawa, thank you so much for very helpful answer. I'll try edit this code and add another features which come to my mind.



  • Which class or classes should I use to make screenshot selection dragable and resizable? I don't think so, that I should use QPainter. I don't want to see a code, I'd like to write it by myself.
    0_1540307537487_4bce7cd0-81d2-4bc9-9645-0dc661a0b19e.jpg


  • Moderators

    You can modify my example not to accept() on mouse release. From there there are many possible options.

    You could modify mousePressEvent to detect you pressed near the corners or middles of the rectangle, store in some member info about which control point you clicked and then modify the rectangle accordingly in mouseMoveEvent. To draw the points you simply draw small rectangles in paintEvent();

    Another option would be to add a QGraphicsView and QGraphicsScene to the widget. You would then add the control points as QGraphicsRectItem, make them draggable via their item flags and modify the selection rectangle on their move events. This method is a bit heavier but it would save you from implementing the clicking and dragging yourself (it's not very hard anyway, but still).



  • Drawing corner rectangles in paintEvent(), that's what I tried to do, but I haven't been sure, it's correct way. Thank you again for reply, I'll try your tips.


Log in to reply