Я собирал слайдер изображения для анимации изображений на основе некоторых кнопок. В качестве примера эффекта, который я искал, можно увидеть в верхней части этой страницы (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 ¤tText);
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 ¤tText)
{
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
Спасибо за это. Сработал метод, описанный выше в 2). Как вы уже упоминали, все, что мне нужно было сделать, это реализовать mousePressEvent, а mouseReleaseEvent начал получать вызов. Может быть, мне следовало бы выбрать это из документации, но даже прочитать его снова выше, я не уверен, что это очень ясно. Что касается вашего пункта 3) выше, я добавил qDebug() в imageSlider :: eventFilter, чтобы увидеть, какие события прошли, и по щелчку мыши произошло событие GraphicsSceneMousePress, но не событие выпуска, как вы предсказали выше. Большое спасибо за помощь. – stephenf555
@ stephenf555 Ну, по моему опыту, Qt предоставляет одну из самых ясных и полных документов там, поэтому внимательно читайте, что это самый полезный навык разработки с использованием Qt. Также в некоторых редких сложных случаях отладка Qt-источника может дать некоторые подсказки о том, что происходит. Всегда пожалуйста. – Predelnik