2014-02-06 5 views
2

Я собирал слайдер изображения для анимации изображений на основе некоторых кнопок. В качестве примера эффекта, который я искал, можно увидеть в верхней части этой страницы (http://www.menucool.com/slider/javascript-image-slider-demo4).Обнаружение щелчков на QGraphicsItemGroup

Я выполнил эту часть процесса, выполнив подкласс QObject/QGraphicsItemGroup (закодированный в imageSlider.h и imageSlider.cpp) и добавив к нему анимацию. Код может быть полезен для других, пытающихся схожих.

После этого я хотел добавить возможность щелкнуть по изображениюSlider, в результате которого будет вызываться сигнал(), посредством которого можно было бы запросить выбранное изображение и выполнить некоторую задачу на основе этого выбора. Звучит просто.

Попытка 1:

Мой первый, возможно, наиболее очевидная попытка была переопределять обработчик mouseReleaseEvent в imageSlider поймать щелчков мыши. Однако эта функция никогда не вызывается.

Попытка 2:

imageSlider добавляют к QGraphicsScene, который затем рассматривается через QGraphicsView. Имело смысл, что, возможно, QGraphicsView обрабатывал щелчки мыши, и поэтому они вообще не попадали в imageSlider. Поэтому я подклассифицировал QGraphicsView, чтобы создать класс с именем clickableQGraphicsView, у которого есть переопределенный mouseReleaseEvent. Это действительно работает. Хотя я могу использовать этот метод и двигаться дальше, для элегантности я хотел бы инкапсулировать весь код, необходимый для взаимодействия с imageSlider в самом коде imageSlider. Если бы я использовал этот метод, он требовал двух классов.

Попытка 5: (попытки 3 & 4 не получилось)

Я думал, что это может быть возможным, чтобы передать события, обнаруженные регулярным QGraphicsView до обработчика событий в imageSlider. Это означало бы, что я мог бы использовать стандартный класс QGraphicsView и иметь возможность в imageSlider обнаруживать свои собственные щелчки мыши. Я установил фильтр событий. Этот обработчик событий в imageSlider вызывается при любом перемещении мыши в QGraphicsView, поэтому фильтр работает. Странно, однако, события щелчков мыши не обнаружены.

Итак, вопрос:

Я уверен, что вы задаетесь вопросом, почему я не просто заткнуться и однако использование попытка 2. Мне любопытно, и мне было бы интересно для моего собственное образование относительно причин, по которым попытки 1 и 5 не работают.

Самый простой способ проверить это было бы загрузить код проекта от: https://www.dropbox.com/sh/zxlo3n014v7g2n7/riMq3FCB4i Про файл находится в папке TestGUI.

Различные попытки могут быть скомпилированы путем изменения #define в верхней части imageSlider.h. Если все определения закомментированы, то обнаружение кликов не будет включено. Каждая из попыток может быть проверена путем раскомментирования соответствующей строки #define.

Если предпочтительный, приведенный ниже код будет вставлен ниже.


imageSlider.h код также показано ниже:

class imageSlider: public QObject, public QGraphicsItemGroup 
{ 
    Q_OBJECT 
    Q_PROPERTY(QPointF pos READ pos WRITE setPos) /// these property access functions don't have to be reimplemented as they are implemented in QObject. Just declare them. 

public: 
    imageSlider(QGraphicsItem *parent = 0); 
    ~imageSlider(); 

    virtual void addToGroup(QGraphicsItem *item); // /virtual function is used so that the function can be reimplemented but using the same signature (name, arguments, etc) 
    virtual void removeFromGroup(QGraphicsItem *item); 
    void moveImage(int numImages); 
    void setTransitionDuration(uint ms); 
    void setEasingCurve(uint curveNum); //see the help file for QEasingCurve to see what the integers stand for 

private: 
    QList <QGraphicsItem*> items; /// holds the images themselves 
    uint transitionDuration; /// The duration of the transition in ms 
    int currentImageIndex; /// The index of the currently displayed image 
    QMutex animationMutexLock; /// So that an animation cannot be called while the previous one is ongoing 
    QEasingCurve::Type easingCurveType; /// the type of easing curve for the animation. 

    int getXPosChange(int numImages); /// Get the amount the position has to change by to slide by numImages images 
    void animate(int xPosChange); /// Move the images along by the number of pixels in xPosChange 

public slots: 
    void unlockMutex(); 

signals: 
    void clicked(); /// Parent process can be connected to this. When clicked the getCurrentImageIndex() function can be called and a response carried out (like running a process, opening a link, etc). 

    //******************* 
    // TEST CODE 
    //******************* 
#ifdef MOUSECLICKDETECTATTEMPT1 
public slots: 
    void mouseReleaseEvent(QGraphicsSceneMouseEvent * event); 
#endif 
#ifdef MOUSECLICKDETECTATTEMPT5 
protected: 
    bool eventFilter(QObject *target, QEvent *event); 
#endif 

}; 

И imageSlider.cpp является:

#include "imageSlider.h" 

/** 
    * Constructor for image slider. 
    */ 
imageSlider::imageSlider(QGraphicsItem *parent) : 
    QObject(0), 
    QGraphicsItemGroup(parent), 
    transitionDuration(500), 
    currentImageIndex(0), 
    easingCurveType(QEasingCurve::Linear) 
{ 
} 

/** 
    * Deconstructor for image slider. 
    */ 
imageSlider::~imageSlider() 
{ 
    if(~items.isEmpty()) 
     items.clear(); 
} 

/** 
    * Add QGraphicsItems (images from QPixmaps) to the image slider. 
    */ 
void imageSlider::addToGroup(QGraphicsItem *item){ 
    //change the xpos of the item before adding so that the images line up one after the other 
    int xpos = 0; 
    for(int i=0;i<items.count();i++) 
     xpos += items.at(i)->boundingRect().width(); 
    item->setX(xpos); 
    //add to the items and group 
    items << item; 
    QGraphicsItemGroup::addToGroup(item); 
} 

/** 
    * Remove items from the imageSlider. 
    */ 
void imageSlider::removeFromGroup(QGraphicsItem *item) 
{ 
    items.removeAll(item); 
    QGraphicsItemGroup::removeFromGroup(item); 
} 

/** 
    * Move the imageSlider along by numImages number of images. 
    * numImages can be +ve (move images left) or -ve (move images right). 
    */ 
void imageSlider::moveImage(int numImages) 
{ 
    if(animationMutexLock.tryLock()) //the mutex will be unlocked on receiving the finished() signal from the animation object 
    { 
     int xPosChange = getXPosChange(numImages); 
     if(xPosChange==0) //not going to move, so unlock the mutex here as otherwise you have to wait for a zero move animation to finish before getting the next animation. Not a bug, but not ideal for user fluidity. 
      animationMutexLock.unlock(); 
     else 
      //Do the animation 
      imageSlider::animate(xPosChange); 
    } 
} 

/** 
    * Calculate the distance the QGraphicsItemGroup must slide to show the rqeuired image. 
    * A positive numImages move moves the current image to the left. 
    * A negative numImages move moves the current image to the right. 
    */ 
int imageSlider::getXPosChange(int numImages) 
{ 
    //create an incrementer that increments up or down to the requested image 
    int incrementer = 1; 
    if(numImages<0) 
     incrementer = -1; 
    int imageToGoTo = currentImageIndex + numImages; 
    //check that the requested image is within the bounds of the number of images we have 
    if(imageToGoTo<0) 
     imageToGoTo = 0; 
    if(imageToGoTo>items.count()-1) 
     imageToGoTo = items.count()-1; 
    //calculate the positional change 
    int posChange = 0; 
    int i=0; 
    for(i=currentImageIndex;i!=imageToGoTo;i+=incrementer) 
     posChange += items.at(i)->boundingRect().width(); //add the width of each image to skip over 
    //update the current image index to the image that will be shown 
    currentImageIndex = imageToGoTo; 
    //if we want to move to the right, make the positional change negative 
    if(incrementer==1) 
     posChange = -posChange; 
    return posChange; 
} 

/** 
    * Carry out the animation from one image to another. 
    */ 
void imageSlider::animate(int xPosChange) 
{ 
    QPointF currPos = this->pos(); 
    QPointF endPos = currPos; 
    endPos.setX(currPos.x()+xPosChange); 

    QPropertyAnimation *animation = new QPropertyAnimation(this,"pos"); 
    connect(animation,SIGNAL(finished()),SLOT(unlockMutex())); 
    animation->setStartValue(currPos); 
    animation->setEndValue(endPos); 
    animation->setDuration(this->transitionDuration); 
    animation->setEasingCurve(this->easingCurveType); 
    animation->start(QAbstractAnimation::DeleteWhenStopped); 
} 

/** 
    * A slot which is called when the animation is completed to unlock the mutex. 
    * This mutex stops two animations from occurring at the same time. 
    */ 
void imageSlider::unlockMutex() 
{ 
    this->animationMutexLock.unlock(); 
} 

/** 
    * set function for the animation transition duration. 
    */ 
void imageSlider::setTransitionDuration(uint ms) 
{ 
    this->transitionDuration = ms; 
} 

/** 
    * set functionfor the easing curve enum. 
    */ 
void imageSlider::setEasingCurve(uint curveNum) 
{ 
    this->easingCurveType = (QEasingCurve::Type)curveNum; 
} 

//******************* 
// TEST CODE 
//******************* 
#ifdef MOUSECLICKDETECTATTEMPT1 
/** 
    * Try reimplementing the mouseReleaseEvent for the imageSlider to catch mouse clicks. 
    */ 
void imageSlider::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 
{ 
    qDebug() << "imageSlider mouse release event detected"; 
    emit clicked(); 
} 
#endif 

#ifdef MOUSECLICKDETECTATTEMPT5 
/** 
    * Try capturing clicks on the images within the slider using a QObject event filter (imageSlider inherits from QObject and QGraphicsItemGroup. 
    */ 
bool imageSlider::eventFilter(QObject *target, QEvent *event) 
{ 
    if(event->type()==QEvent::MouseButtonRelease) 
    { 
     qDebug() << "imageSlider mouse release event detected through the eventFilter"; 
     emit clicked(); 
     return true; 
    } 
    return false; 
} 
#endif 

Основной код формы mainwindow.h:

// 
#ifndef MAINWINDOW_H 
#define MAINWINDOW_H 

#include <QMainWindow> 
#include <QGraphicsScene> 
#include <QGraphicsItem> 
#include <QPropertyAnimation> 
#include "../imageSlider/imageSlider.h" 
#include <QGraphicsView> 

namespace Ui { 
class MainWindow; 
} 

#ifdef MOUSECLICKDETECTATTEMPT2 
class clickableQGraphicsView : public QGraphicsView 
{ 
    Q_OBJECT 

public: 
    clickableQGraphicsView(QWidget *parent=0); 
    ~clickableQGraphicsView(); 
public slots: 
    virtual void mouseReleaseEvent(QMouseEvent *event); 
}; 
#endif 

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

public: 
    explicit MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 

private slots: 
    void on_pushButtonRight_clicked(); 
    void on_pushButtonLeft_clicked(); 
    void on_listWidgetAnimType_currentTextChanged(const QString &currentText); 
    void on_spinBoxMSAnim_valueChanged(int arg1); 
    void imageGroupClicked(); 

private: 
    Ui::MainWindow *ui; 
    imageSlider *imageGroup; 
    QGraphicsScene *GScene; 
    void moveImageSlider(int numImages); 

    //**************** 
    // TEST CODE 
    //**************** 
#ifdef MOUSECLICKDETECTATTEMPT2 
    clickableQGraphicsView *clickView; 
#endif 

}; 

#endif // MAINWINDOW_H 

И, наконец, mainwindow.cpp является:

#include "mainwindow.h" 
#include "ui_mainwindow.h" 
#include "qmessagebox.h" 

MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow) 
{ 
    ui->setupUi(this); 

    //******************* 
    // Set up options list 
    // for animation types 
    //******************* 
    for(int i=0;i<41;i++) 
     ui->listWidgetAnimType->addItem(QString::number(i)); 

    //**************** 
    // Graphics Scene 
    //**************** 
    QPixmap pixmap1(":imageSwipe/images/1.png"); 
    QGraphicsItem *Image1 = new QGraphicsPixmapItem(pixmap1); 
    QPixmap pixmap2(":imageSwipe/images/2.png"); 
    QGraphicsItem *Image2 = new QGraphicsPixmapItem(pixmap2); 
    QPixmap pixmap3(":imageSwipe/images/3.png"); 
    QGraphicsItem *Image3 = new QGraphicsPixmapItem(pixmap3); 
    QPixmap pixmap4(":imageSwipe/images/4.png"); 
    QGraphicsItem *Image4 = new QGraphicsPixmapItem(pixmap4); 

    GScene = new QGraphicsScene(); 
    GScene->setSceneRect(QRectF(0,0,Image1->boundingRect().width(),Image1->boundingRect().height())); 

    imageGroup = new imageSlider; 
    imageGroup->addToGroup(Image1); 
    imageGroup->addToGroup(Image2); 
    imageGroup->addToGroup(Image3); 
    imageGroup->addToGroup(Image4); 

    GScene->addItem(imageGroup); 

    ui->graphicsViewGUIInterface->setScene(GScene); 
    ui->graphicsViewGUIInterface->setGeometry(0,0,Image1->boundingRect().width(),Image1->boundingRect().height()); 
    //******************* 
    // TEST CODE 
    //******************* 
    connect(imageGroup,SIGNAL(clicked()),this,SLOT(imageGroupClicked())); 

#ifdef MOUSECLICKDETECTATTEMPT2 
    clickView = new clickableQGraphicsView(this); 
    clickView->setScene(GScene); 
    clickView->setGeometry(20,20,Image1->boundingRect().width(),Image1->boundingRect().height()); 
#endif 

#ifdef MOUSECLICKDETECTATTEMPT5 
    ui->graphicsViewGUIInterface->installEventFilter(imageGroup); 
#endif 
} 

MainWindow::~MainWindow() 
{ 
    if(imageGroup) 
    { 
     disconnect(imageGroup); 
     delete imageGroup; 
    } 
    if(GScene) 
     delete GScene; 
    delete ui; 
} 

void MainWindow::on_pushButtonRight_clicked() 
{ 
    moveImageSlider(-1); 
} 

void MainWindow::on_pushButtonLeft_clicked() 
{ 
    moveImageSlider(1); 
} 

void MainWindow::moveImageSlider(int numImages) 
{ 
    imageGroup->moveImage(numImages); 
} 

void MainWindow::on_listWidgetAnimType_currentTextChanged(const QString &currentText) 
{ 
    imageGroup->setEasingCurve(currentText.toUInt()); 
} 

void MainWindow::on_spinBoxMSAnim_valueChanged(int arg1) 
{ 
    imageGroup->setTransitionDuration((uint)arg1); 
} 

void MainWindow::imageGroupClicked() 
{ 
    QMessageBox msgBox; 
    msgBox.setText(QString("Received index = 1")); 
    msgBox.exec(); 
} 

//*************** 
// TEST CODE 
//*************** 
#ifdef MOUSECLICKDETECTATTEMPT2 
/** 
    * The below functions are an attempt to subclass the QGraphicsView 
    * to provide a click response. 
    */ 
clickableQGraphicsView::clickableQGraphicsView(QWidget *parent) : 
    QGraphicsView(parent) 
{ 
} 

clickableQGraphicsView::~clickableQGraphicsView() 
{ 
} 

void clickableQGraphicsView::mouseReleaseEvent(QMouseEvent *event) 
{ 
    if(event->type() == QEvent::MouseButtonRelease) 
     qDebug() << "QGraphicsView event dectected"; 

} 
#endif 

Спасибо за любую помощь. Надеюсь, это может быть полезно и для других. Stephen

ответ

1

Хорошо, что я не очень опытен с объектами, о которых идет речь, чтобы быть точным, но, возможно, просто внимательно прочитайте руководство и немного отладки будет достаточно. Давайте посмотрим:

1) Первое, что я хотел бы отметить, что использование двоичного кода not на значениях bool немного противоречит интуиции и не является хорошей практикой, мой VS даже предупредил меня об этом. Я имею в виду эту строку, если быть точным: if(~items.isEmpty()).

2) Когда вы получаете в Qt инструкции по QGraphicsItem::mouseReleaseEvent вы найдете следующие строки:

Пожалуйста, обратите внимание, что mousePressEvent() определяет, какой графический элемент является то, что получает события мыши. Подробнее см. Описание mousePressEvent().

Так что мы собираемся QGraphicsItem::mousePressEvent то, что мы находим:

Событие нажатия кнопок мыши определяет какой элемент должен стать граббер мыши (см QGraphicsScene :: mouseGrabberItem()). Если вы не переопределите эту функцию, событие пресса будет распространяться на любой самый верхний элемент под этим элементом, и никакие другие события мыши не будут доставлены в этот элемент.

Так в основном, чтобы решить проблему с первой попытки мы просто нужно переопределить mousePressEvent с функцией ничего не делать {} и события придут в mouseReleaseEvent из imageSlider

3) О методе 5, что было немного сложно фигуры вне. В основном это не работает из-за особого характера QGraphicsView - он всегда переводит событие типа QEvent::MouseButtonRelease на его особый тип QEvent::GraphicsSceneMouseRelease и отправляет его его захватчикам мыши (упоминалось ранее), но даже если их нет у этих захватчиков устанавливает GraphicsSceneMouseRelease событие принятое и таким образом MouseButtonRelease также принимается, поэтому в конце он никогда не отправляется дальше eventFilter s. Кстати, MousePressEvent не едят, как это, к примеру.

Фактически, используя setEventFilter, это слишком общий способ делать что-то большую часть времени, поэтому в любом случае, я думаю, вы должны придерживаться упомянутого выше пути.

+0

Спасибо за это. Сработал метод, описанный выше в 2). Как вы уже упоминали, все, что мне нужно было сделать, это реализовать mousePressEvent, а mouseReleaseEvent начал получать вызов. Может быть, мне следовало бы выбрать это из документации, но даже прочитать его снова выше, я не уверен, что это очень ясно. Что касается вашего пункта 3) выше, я добавил qDebug() в imageSlider :: eventFilter, чтобы увидеть, какие события прошли, и по щелчку мыши произошло событие GraphicsSceneMousePress, но не событие выпуска, как вы предсказали выше. Большое спасибо за помощь. – stephenf555

+0

@ stephenf555 Ну, по моему опыту, Qt предоставляет одну из самых ясных и полных документов там, поэтому внимательно читайте, что это самый полезный навык разработки с использованием Qt. Также в некоторых редких сложных случаях отладка Qt-источника может дать некоторые подсказки о том, что происходит. Всегда пожалуйста. – Predelnik

Смежные вопросы