Problem with QT MousePressedEvent / MouseMoveEvent



  • Hey guys,

    first of all let me tell you that I am really new in programming with QT and C++ and that my english is very bad, sorry for that!

    I tried to implement a 2D game with an Menu Bar. My Classes are already done. My only Problem is the GUI especially my ClickableLabels. My gameboard needs to be Clickable (MousePressedEvent) and as well MousePressed + MouseMoveEvent.
    I implemented a QList to coordinate my dynamic amount of Clickable Labels which are shown in a Gridlayout on the GUI.

    What I need is a function which realises MousedPressedEvent and MouseMoveEvent as MousePressedEvent in an specific period of time (= so that clicked Labels don´t get clicked back...). And of course that more than one Label gets clicked in an MouseMoveEvent. I have really no idea how I can program that.

    I know it would be easier if paint the gameboard instead of having ClickableLabels.. but I want to try it like that :). Would be great if someone understand my problem and can manage to show me how to get to it.

    Please help me!
    Sorry for my bad english and my programming skills, I´ll improve both !

    Thank you for your time!

    (This is my second thread, i forgot to select a category don´t know where it´s gone) :)

    Atm I am not at home, i´ll add a part of my Code especially the Clickable Labels when I am at home.


  • Qt Champions 2016

    Hi and welcome to the forums.
    Dont worry, your english is fine.
    Im not sure however, what you mean by
    " MousePressedEvent in an specific period of time (= so that clicked Labels don´t get clicked back...). "

    You mean like, once the user have clicked a clickLabel one time, he should not able to click on it again until you
    sort of say "reset board/new game" so all are free again ?
    Im not sure what the "clicked back" means :)

    btw: is this your label?
    https://wiki.qt.io/Clickable_QLabel



  • Thanks for your fast answer. Yes you are right! I took those labels.```
    //#ifndef CLICKABLELABEL_H
    #define CLICKABLELABEL_H

    #include <QLabel>
    #include <QWidget>
    #include <Qt>
    #include <QString>

    class ClickableLabel : public QLabel {
    Q_OBJECT

    public:

    bool pressed;
    QString color = "black";
    explicit ClickableLabel(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
    ~ClickableLabel();
    

    signals:
    void clicked();

    protected:
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event); // to implement

    };

    #endif // CLICKABLELABEL_H

    
    

    #include <clickablelable.h>
    #include <QMouseEvent>

    ClickableLabel::ClickableLabel(QWidget* parent, Qt::WindowFlags f)
    : QLabel(parent)
    {
    pressed = false;
    }

    ClickableLabel::~ClickableLabel() {}

    void ClickableLabel::mousePressEvent(QMouseEvent *event) {

    if (text() == " "){
        pressed = true;
        setText("  ");
        setStyleSheet(QString("QLabel { background-color:").append(color).append(QString(" }")));
    }
    else{
        pressed = false;
        setText(" ");
        setStyleSheet(QString("QLabel { background-color: white }"));
    }
    
    emit clicked();
    

    }

    void ClickableLabel::mouseMoveEvent(QMouseEvent *event) // to implement
    {
    if(event->buttons() == Qt::LeftButton)
    {
    emit clicked();

     }
    

    }

    I wanted to say with :
    " MousePressedEvent in an specific period of time (= so that clicked Labels don´t get clicked back...). " 
    that if I pressed my Mouse Button and move it around my Gridlayout, I want to click all label I crossed once.
    
    I thought about doing it like : MouseMoveEvent = several MousePressedEvents and to don´t click any label twice I need to say that those click´s have a good timing.. :/ ? But I dont have any idea to implement this, I`ll have to buy me one QT Book I guess.
    
    
    "You mean like, once the user have clicked a clickLabel one time, he should not able to click on it again until you
    sort of say "reset board/new game" so all are free again ?
    Im not sure what the "clicked back" means :)" - so this is totally right !
    
    How can I fix that ? :D

  • Qt Champions 2016

    Hi
    So you press mouse button and you can then move cursor around and cell will be painted when mouse enter?
    The click label can handle it for you.
    add a
    bool isPainted=false;
    to class .h

    Then in mouseMoveEvent
    if(event->buttons() == Qt::LeftButton && ! isPainted )
    isPainted=true;

    than also a new function in public in class .h

    void Reset() {
    isPainted=false;
    update();
    }

    Then in main program, loop over all clicklabels in your QList
    and call Reset() on each. ( when you want to clear board)

    I would also add a paintEvent and draw the background there and dont use stylesheet for it but
    that is just to make code easier as currently it will only color background when u click on it and
    what you seem to want is not clicking it but simply move into the cell and then its painted. (correct ?)


  • Qt Champions 2016

    Hi
    Here is example:

    #ifndef BOARDCELL_H
    #define BOARDCELL_H
    
    #include <QWidget>
    #include <QMouseEvent>
    #include <QPainter>
    #include <QApplication>
    #include <QDebug>
    
    class BoardCell : public QWidget {
      Q_OBJECT
    public:
      explicit BoardCell(QWidget* parent = nullptr) : QWidget(parent) {
        setMouseTracking(true);
      }
    
      void Reset() {
        isPainted = false;
      }
    
    signals:
      void clicked();
    
    protected:
      void mousePressEvent(QMouseEvent* event) {
        emit clicked();
      }
      void mouseMoveEvent(QMouseEvent* event) override {
        if(! isPainted  ) {
          isPainted = true;
          update();
        }
      }
    
      bool isPainted = false;
    protected:
      virtual void paintEvent(QPaintEvent* ) override {
        QPainter p(this);
        p.setBrush(Qt::white);
        if (isPainted)
          p.setBrush(Qt::red);
        p.drawRect(QRect(0, 0, width() - 1, height() - 1));
    
      }
    };
    #endif
    

    alt text

    There is one issue. When you click on first one (AND hold), it capture mouse and the other cells wont see it.

    I also checked QApplication::mouseButtons() but it wont work
    after clicking inside first cell. ( it takes the mouse so it can get Release Event)

    Not sure how to work around that and i would normally suggest just to paint the board as one widget.
    You might be able to use the draw events or eventfilter but
    it seems kinda pointless as there are better way to design it.



  • Thank you very much for your work and your time!, but I am still failing hard :(. I know there are easier ways to manage the field.. but this task is the last I need to complete. I tried to rebuild your example in my Clickable Lable Class. But it doesnt work maybe cause your example ist from QWidget and mine from QLabel ? :/

    My cpp source:

    #include <clickablelable.h>
    #include <QMouseEvent>
    
    
    ClickableLabel::ClickableLabel(QWidget* parent, Qt::WindowFlags f)
        : QLabel(parent)
    {
        pressed = false;
        setMouseTracking(true);
    }
    
    ClickableLabel::~ClickableLabel() {}
    
    void ClickableLabel::reset()
    {
        isPainted = false;
        update();
    }
    
    void ClickableLabel::mousePressEvent(QMouseEvent *event) {
    
        if (text() == " "){
            pressed = true;
            setText("  ");
            setStyleSheet(QString("QLabel { background-color:").append(color).append(QString(" }")));
        }
        else{
            pressed = false;
            setText(" ");
            setStyleSheet(QString("QLabel { background-color: white }"));
        }
    
        emit clicked();
    }
    
    void ClickableLabel::mouseMoveEvent(QMouseEvent *ev)
    {
        if(ev->buttons() == Qt::LeftButton && ! isPainted ){
            setStyleSheet(QString("QLabel { background-color:").append(color).append(QString(" }")));
            setText("  ");
            isPainted=true;
        }
    }
    
    
    
    
    

    My header file:

    /#ifndef CLICKABLELABEL_H
    #define CLICKABLELABEL_H
    
    #include <QLabel>
    #include <QWidget>
    #include <QApplication>
    #include <QString>
    #include <QMouseEvent>
    #include <QDebug>
    
    
    
    class ClickableLabel : public QLabel {
        Q_OBJECT
    
    public:
    
        explicit ClickableLabel(QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
    
        ~ClickableLabel();
    
        void reset();
    
        bool pressed;
        bool isPainted = false; // Wird implementiert für die mouseMoveEvents
        QString color = "black";
    
    
    
    signals:
        void clicked();
    
    protected:
        void mousePressEvent(QMouseEvent* event);
        void mouseMoveEvent(QMouseEvent *ev) override ;
    
    };
    
    #endif // CLICKABLELABEL_H
    
    

    I would really like to avoid to start from new with painting a gameboard. I´m just using setText to manage living cells vs dead cells and the setStyleSheet for the visual representation..


  • Qt Champions 2016

    Hi
    Well the main issue is that the others are not getting a mouseMoveEvent when you are holding
    button down. the first ClickLabel you click on, steals the mouse and until you release they others
    cannot be painted.
    I could not find a way to allow it.

    Maybe others have an idea how to do it.


  • Qt Champions 2016

    You can easy make mine to QLabel too
    just replace QWidget 2 places.



  • " steals the mouse and until you release they others
    cannot be painted. " okay now I understand the problem.

    "You can easy make mine to QLabel too
    just replace QWidget 2 places " What does this mean ?

    Just adding an Label Variabel to my Widget and making my operations on that ? So that I can still use setText and MouseMove ? or am I wrong ?

    I am so thankful for your fast answers ! German Forums are way more unfriendly


  • Qt Champions 2016

    @tianstar
    Hi
    we are super friendly here ;)

    Its easy to change type

    class BoardCell : public QWidget {
    Q_OBJECT
    public:
    explicit BoardCell(QWidget* parent = nullptr) : QWidget(parent) {
    setMouseTracking(true);
    }

    Just replace those with QLabel and it will be a QLabel instead.

    Remember to do full rebuild as not to get funky errors. :)



  • Ah okay, I guess I missunderstand your example. I got more than one class in my Projekt. One for MainWindow , one GameWidget one GameRules and one for ClickableLable . I can totally delete clickable Labels and instead use your one with creating something like setText and PaintEvent. The result is that I dont have a Grid Layout full of ClickableLabels instead I´ll have a GridLayout full of small Widgets ? :)

    Sounds good?


  • Qt Champions 2016

    @tianstar
    Well if u change QWidget with QLabel and use that instead of your clicklabel , it should just work.
    Yes you should be able to just replace ClickLabel with BoardCell without much fuss.

    Sadly there is still the issue of it stealing mouse on first click


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.