Russian Qt Forum
Июнь 01, 2024, 11:33 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Особенности работы с setMouseTracking или с отслеживанием мыши  (Прочитано 13643 раз)
Eten
Гость
« : Июнь 07, 2014, 04:21 »

Всем доброго утра, сижу дописываю программу на дипломную работу.

Потребовалось мне сделать отслеживание мыши, для того чтобы менять курсор на схеме управления, да оказалось, что разработчики Qt сделали очень интересный финт, из-за которого вчера долго разбирался, как работает отслеживание мыши, чтобы оно конкретно именно работало. И утро, вечера мудренее, да на свежую голову. Проблему, лично для своего проекта решил, а вопросы остались. Да, еще решил эту тему запостить, т.к. никто про нее не писал, а натыкаешься в других форумах и сам наткнулся на данную дилемму.



Проблему решил, а разрабов так и не понял. Нафига, делать драг и дроп в моузеэвент?! Почему нельзя было оставить отслеживание мыши для отслеживания перемещения мыши?!



И еще один момент, который надо учесть. Например, здесь указан пример, который показывает, как сделать отслеживание мыши без всякого нажатия любой кнопки мыши. Как оказалось, этот пример не всегда работает, особенно если у вас не просто окно, а еще есть центральный виджет и на нем схема управления в виде метки с изображением. А так, если без метки, то у центрального виджета тоже надо прописывать setMouseTracking(true). В моем случае заработало, когда прописал у окна, у центрального виджета, у метки с картинкой, и только после этого заработало.

Кто-нибудь, на будущее, может пояснить, а почему мне потребовалось в трех местах указать setMouseTracking(true), чтобы оно конкретно позволило работать mouseMoveEvent-у у самого окна?! Разве там не должны работать принципы наследования или иерархии классов, как это бывает с управлением стилей виджетов?!

Вот код моего окна.
заголовочный:
Код:
#ifndef STENDOSTANOV_H
#define STENDOSTANOV_H

#include <QMainWindow>
#include <QtGui>
#include <QPixmap>

#include "startmenu.h"


//Учебчный стенд остановы турбины
class stendOstanov : public QMainWindow
{
    Q_OBJECT
public:
    explicit stendOstanov(startMenu *startMenu, QWidget *parent = 0);
   
signals:

protected:
      virtual void mousePressEvent (QMouseEvent *event);
      void mouseMoveEvent (QMouseEvent *event);
   
private slots:
    void changeSelectND(int numberSelectND);
    void launchingTest();
    void interruptingTest();

private:
    //Виджеты окна
    QComboBox *selectMode;
    QPushButton *startTest;
    QPushButton *interruptTest;
    QLabel *minutes;
    QLabel *separator;
    QLabel *seconds;
    QLabel *uprTurbImage;
    QLabel *textLabel;
    QLabel *ErrorLabel;
    QLabel *reportTest;
    QScrollArea *scrollReport;



   
};

#endif // STENDOSTANOV_H

исходных кодов:
Код:
#include "stendostanov.h"

stendOstanov::stendOstanov(startMenu *startMenu, QWidget *parent) :
    QMainWindow(parent)
{
    //Устанавливаем параметры окна стенда останова турбины


    //-------------

    //Прописываем параметры окна стенда останова турбины

    setWindowTitle(QObject::trUtf8("Учебчный стенд останова турбины"));
    setWindowFlags(Qt::Window | Qt::WindowSystemMenuHint);
    setMinimumSize(800, 420);
    setMaximumSize(800, 420);
    setMouseTracking(true);

    //-------------

    //Элементы компановки виджетов в окне

    //Основной элемент компановки окна
    QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom);


    //Элемент компановки верхней части элементов окна
    QHBoxLayout *topLayout = new QHBoxLayout();

    //Элемент компановки таймера в верхней части элементов окна
    QHBoxLayout *timerkLayout = new QHBoxLayout();

    //Элемент компановки нижней части элементов окна
    QHBoxLayout *bottomLayout = new QHBoxLayout();


    //Элемент компановки правой части панели обучения/тестирования
    QVBoxLayout *rightPanelLayout = new QVBoxLayout();

    //-------------


    //Виджеты окна тестирования по НД

    //Виджет отчета после проведения теста
    reportTest = new QLabel("");

    reportTest->setScaledContents(true);
    reportTest->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    reportTest->setMinimumSize(730, 330);
    scrollReport = new QScrollArea();
    scrollReport->setWidget(reportTest);
    scrollReport->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    scrollReport->setMinimumSize(750, 350);
    scrollReport->hide();


    //Выбор теста из списка доступных
    selectMode = new QComboBox();
    connect(selectMode, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSelectND(int)));
    selectMode->addItem(QObject::trUtf8("Режим обучения"));
    selectMode->addItem(QObject::trUtf8("Режим тестирования"));


    //Кнопка старта теста
    startTest = new QPushButton(QObject::trUtf8("Начать тест"));
    connect(startTest, SIGNAL(clicked()), this, SLOT(launchingTest()));

    //Кнопка прерывания теста
    interruptTest = new QPushButton(QObject::trUtf8("Прервать тест"));
    connect(interruptTest, SIGNAL(clicked()), this, SLOT(interruptingTest()));
    interruptTest->setEnabled(false);


    //Виджеты таймера: минуты, ":", секунды
    minutes = new QLabel();
    separator = new QLabel(":");
    seconds = new QLabel();

    //Схема управления паровой турбиной
    uprTurbImage = new QLabel();
    QPixmap px;
    px.load("parturbina.png");
    uprTurbImage->setPixmap(px);
    uprTurbImage->setMinimumSize(570,370);
    uprTurbImage->setMaximumSize(570,370);
    uprTurbImage->setScaledContents(true);
    QPalette pal = uprTurbImage->palette();
    pal.setBrush(QPalette::Base, QBrush(Qt::black));
    uprTurbImage->setPalette(pal);
    uprTurbImage->setFrameStyle(QFrame::Plain | QFrame::StyledPanel);
    uprTurbImage->setMouseTracking(true);


    //Текстовая метка об кол-ве ошибок и/или допущенной ошибке
    ErrorLabel = new QLabel();
    ErrorLabel->setMinimumSize(200, 70);
    ErrorLabel->setMaximumSize(200, 70);
    pal = ErrorLabel->palette();
    pal.setBrush(QPalette::Base, QBrush(Qt::black));
    ErrorLabel->setPalette(pal);
    ErrorLabel->setFrameStyle(QFrame::Plain | QFrame::StyledPanel);
    ErrorLabel->setAlignment(Qt::AlignCenter);
    ErrorLabel->setScaledContents(true);
    ErrorLabel->setWordWrap(true);


    //Текст текущей операции в учебном стенде
    textLabel = new QLabel();

    textLabel->setMinimumSize(200, 270);
    textLabel->setMaximumSize(200, 270);
    pal = textLabel->palette();
    pal.setBrush(QPalette::Base, QBrush(Qt::black));
    textLabel->setPalette(pal);
    textLabel->setFrameStyle(QFrame::Plain | QFrame::StyledPanel);
    textLabel->setAlignment(Qt::AlignCenter);
    textLabel->setScaledContents(true);
    textLabel->setWordWrap(true);



    //-------------


    //Компановка виджетов окна

    //Компановка виджетов таймера
    timerkLayout->addWidget(minutes);
    timerkLayout->addWidget(separator);
    timerkLayout->addWidget(seconds);

    //Компановка виджетов верхней части окна
    topLayout->addWidget(selectMode);
    topLayout->addSpacing(15);
    topLayout->addWidget(startTest);
    topLayout->addSpacing(15);
    topLayout->addWidget(interruptTest);
    topLayout->addSpacing(25);
    topLayout->addLayout(timerkLayout);
    topLayout->addStretch();

    //Компановка правой части панели обучения/тестирования учебного стенда
    rightPanelLayout->addWidget(textLabel);
    rightPanelLayout->addSpacing(5);
    rightPanelLayout->addWidget(ErrorLabel);

    //Компановка виджетов нижней части окна
    bottomLayout->addWidget(uprTurbImage);
    bottomLayout->addLayout(rightPanelLayout);


    //Компановка всех элементов компановки окна
    mainLayout->addLayout(topLayout);
    mainLayout->addStretch();
    mainLayout->addWidget(scrollReport);
    mainLayout->addLayout(bottomLayout);
    QWidget *centralWidget = new QWidget();
    centralWidget->setMouseTracking(true);
    centralWidget->setLayout(mainLayout);
    this->setCentralWidget(centralWidget);


    //    reportTest->setWordWrap(true);// Перенос слов
}

void stendOstanov::mousePressEvent(QMouseEvent *event)
{
    int x = event->x();
    int y = event->y();
    qDebug()<<x + "|" + y << x << y;

    textLabel->setText("");

    //ПВД 7
    if (x >= 67 && x <= 116 && y >= 305 && y <= 402)
        textLabel->setText("PVD 7");

    //ПВД 6
    if (x >= 130 && x <= 184 && y >= 305 && y <= 402)
        textLabel->setText("PVD 6");

    //ПВД 5
    if (x >= 200 && x <= 234 && y >= 305 && y <= 402)
        textLabel->setText("PVD 5");

    //Деаэратор
    if (x >= 263 && x <= 334 && y >= 265 && y <= 317)
        textLabel->setText("dearator");

    //ПНД 3
    if (x >= 373 && x <= 418 && y >= 305 && y <= 402)
        textLabel->setText("PND 3");

    //ПНД 2
    if (x >= 428 && x <= 475 && y >= 305 && y <= 402)
        textLabel->setText("PND 2");

    //ПНД 1
    if (x >= 487 && x <= 537 && y >= 305 && y <= 402)
        textLabel->setText("PND 1");

    //Конденсатор
    if (x >= 414 && x <= 476 && y >= 223 && y <= 280)
        textLabel->setText("Kondensator");

    //ЦВД
    if (x >= 194 && x <= 252 && y >= 90 && y <= 193)
        textLabel->setText("CVD");

    //ЦСД
    if (x >= 335 && x <= 383 && y >= 90 && y <= 193)
        textLabel->setText("CSD");

    //ЦНД
    if (x >= 419 && x <= 458 && y >= 90 && y <= 193)
        textLabel->setText("CND");

    //Генератор
    if (x >= 489 && x <= 540 && y >= 119 && y <= 160)
        textLabel->setText("GENERATOR");

    //Основной паропровод
    if (x >= 41 && x <= 85 && ((y >= 58 && y <= 82) || (y >= 198 && y <= 222)))
        textLabel->setText("OP");

}

void stendOstanov::mouseMoveEvent(QMouseEvent *event)
{
   setWindowTitle("mouse event grabbed!");

    int x = event->x();
    int y = event->y();

    textLabel->setText("");

    //ПВД 7
    if (x >= 67 && x <= 116 && y >= 305 && y <= 402)
        textLabel->setText("PVD 7");

    //ПВД 6
    if (x >= 130 && x <= 184 && y >= 305 && y <= 402)
        textLabel->setText("PVD 6");

    //ПВД 5
    if (x >= 200 && x <= 234 && y >= 305 && y <= 402)
        textLabel->setText("PVD 5");

    //Деаэратор
    if (x >= 263 && x <= 334 && y >= 265 && y <= 317)
        textLabel->setText("dearator");

    //ПНД 3
    if (x >= 373 && x <= 418 && y >= 305 && y <= 402)
        textLabel->setText("PND 3");

    //ПНД 2
    if (x >= 428 && x <= 475 && y >= 305 && y <= 402)
        textLabel->setText("PND 2");

    //ПНД 1
    if (x >= 487 && x <= 537 && y >= 305 && y <= 402)
        textLabel->setText("PND 1");

    //Конденсатор
    if (x >= 414 && x <= 476 && y >= 223 && y <= 280)
        textLabel->setText("Kondensator");

    //ЦВД
    if (x >= 194 && x <= 252 && y >= 90 && y <= 193)
        textLabel->setText("CVD");

    //ЦСД
    if (x >= 335 && x <= 383 && y >= 90 && y <= 193)
        textLabel->setText("CSD");

    //ЦНД
    if (x >= 419 && x <= 458 && y >= 90 && y <= 193)
        textLabel->setText("CND");

    //Генератор
    if (x >= 489 && x <= 540 && y >= 119 && y <= 160)
        textLabel->setText("GENERATOR");

    //Основной паропровод
    if (x >= 41 && x <= 85 && ((y >= 58 && y <= 82) || (y >= 198 && y <= 222)))
        textLabel->setText("OP");
}

//Прерывание теста
void stendOstanov::interruptingTest()
{
}

void stendOstanov::changeSelectND(int numberSelectND)
{
  //  numbertest = numberSelectND;
}

//Запуск тестирования
void stendOstanov::launchingTest()
{
}


Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Июнь 07, 2014, 10:32 »

Проблему решил, а разрабов так и не понял. Нафига, делать драг и дроп в моузеэвент?! Почему нельзя было оставить отслеживание мыши для отслеживания перемещения мыши?!
Не понял Вашего негодования. О чем Вы?

Код:
    //ПВД 7
    if (x >= 67 && x <= 116 && y >= 305 && y <= 402)
        textLabel->setText("PVD 7");

И так еще 10 раз. Это не только длинно, но и плохо. Напр мыша выскочила туда где нет пр-ка, а текст в textLabel остался. Сделайте пр-ки виджетами и ловите события MouseEnter/MouseLeave
Записан
Bepec
Гость
« Ответ #2 : Июнь 07, 2014, 12:24 »

Всё логично у них сделано.
Каждый виджет может ловить перемещения мыши над собой.
Виджет на виджете-родителе получает только свои перемещения, в то время как родитель получает свои и своего ребенка.

А чтобы изменять надписи вполне достаточно mouseTracking на главном виджете. Или же фильтр событий.

to Igors: так не будет, как вы описали. Перед присваиванием текста он обнуляет Label. Так что это вполне рабочее решение, пусть и не особо изящное.

Я бы к примеру написал со словарём <QRect, QString>. так было бы проще во всём Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Июнь 07, 2014, 12:44 »

to Igors: так не будет, как вы описали. Перед присваиванием текста он обнуляет Label. Так что это вполне рабочее решение, пусть и не особо изящное.
Ну как минимум если мышь ушла за пределы приложения - текст застрял. По поводу "рабочести" - для диплома годится, но в принципе неверно. Нас интересует не текущее положение мыши, а лишь факт что ее входа/выхода в заданные области - ну так надо и делать соответственно.

Если же по каким-то причинам не хочется делать пр-ки виджетами, то обычно запускают таймер и там отлавливают. Хотя судя по вбитым в код константам - пустой разговор, "не в коня корм"  Плачущий
Записан
Bepec
Гость
« Ответ #4 : Июнь 07, 2014, 13:07 »

Я тоже сначала хотел написать - проще виджеты задать, но там если посмотреть код - простая картинка. Аналог как отмечать лица в соц сетях - рамку натянул, тултип поменялся на ФИО.

Как я вижу по координатам - картинка имеет буферную зону со всех сторон. Т.е. указатель вылететь за пределы мгновенно не может, за исключением стороннего вмешательства, но тут уже другой вопрос Улыбающийся

Нормальная программа. Видно, что только в первый раз написана Веселый Пару раз переписать и норм будя ^.^
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #5 : Июнь 07, 2014, 13:09 »

Если же по каким-то причинам не хочется делать пр-ки виджетами, то обычно запускают таймер и там отлавливают.
Вот это настоящее пионерство.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Июнь 07, 2014, 13:28 »

Вот это настоящее пионерство.
Так предложите "комсомольство"  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #7 : Июнь 07, 2014, 13:44 »

Так предложите "комсомольство"  Улыбающийся
Отслеживать каждое перемещение мыши.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Июнь 07, 2014, 14:26 »

Виджет на виджете-родителе получает только свои перемещения, в то время как родитель получает свои и своего ребенка.
Никогда о таком не слышал - и на всякий случай проверил (аттач).  Каждый получает "свои" mouseMoveEvent - и все.

[/off]"ребенка" - ну не звучит этот термин в переводе  Улыбающийся

Отслеживать каждое перемещение мыши.
Тот же аттач. Вполне вероятно не захочется возиться со сплиттером или каким-то еще разделителем - просто при наезде мыши на вертикальную границу (+/- 2 пыкселя) курсор меняет форму (мол, двигайте). Как это сделать  отслеживая перемещение мыши?

Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #9 : Июнь 07, 2014, 14:39 »

Вполне вероятно не захочется возиться
Когда оно не хочется, то никто не заставит. Улыбающийся
А если очень хочется сачкануть, то eventFilter + mouseTracking позволят это сделать.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Июнь 07, 2014, 18:35 »

Когда оно не хочется, то никто не заставит. Улыбающийся
А если очень хочется сачкануть,
Почему сачкануть? Что, очень хорошо там лепить виджет? А какой, как его рисовать? И его никто не спрашивал, надо еще объяснять что это  Улыбающийся

то eventFilter + mouseTracking позволят это сделать.
Ну это решение октябренка (если Вы им были  Улыбающийся)
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #11 : Июнь 07, 2014, 19:36 »

Ну если отслеживать перемещение мыши в обработчике события перемещения мыши "октябризм", то пользуйтесь для этого таймером. Улыбающийся
Записан
Bepec
Гость
« Ответ #12 : Июнь 07, 2014, 21:16 »

Если по таймеру - это очень похоже на процедурное программирование, без использования классов в полной мере.

А вот использовать обработчик перемещений мыши у класса окна - как раз таки ООП.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #13 : Июнь 07, 2014, 22:12 »

Если по таймеру - это очень похоже на процедурное программирование, без использования классов в полной мере.
Не знаю насколько это похоже на процедурное программирование... скорее это как читать файл с диска через QSerialPort. Улыбающийся
Как бы никто не запрещает, но нельзя сказать что "обычно делают так". Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Июнь 08, 2014, 11:23 »

Как бы никто не запрещает, но нельзя сказать что "обычно делают так". Улыбающийся
Техника "по таймеру" существует с незапамятных времен. Не скажу что она идеальна - но возможна. Многие классы Qt ее тоже используют, напр хоть QAbstractItemView для autoScroll (недавно там лазил)

Перекрывать mouseMoveEvent (на том основании что "это ООП - и значит хорошо") в моем примере явно глупо - придется наследоваться для решения частной/мелкой задачи. Предлагаете фильтр - но он здесь не универсален. Для левого айтема надо отследить "мышь на правом крае", для правого - наоборот. А отследив - что делать? Послать сигнал тому это может двигать (а его еще надо знать). В итоге все равно окно двигает - так не лучше ли сделать это одним вызовом таймера?
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.048 секунд. Запросов: 19.