Using QtimerEvent in QGraphicsScene - c++

The timerEvent, which is a member of a QGLWidget class shall be triggered when the mousemove-function is called. I thought I could do it like this:
void GLWidget::timerEvent(QTimerEvent *e)
{
if (e->timerId()==1 && refresh==true)
{
refresh = !refresh;
swapBuffers();
update();
}
}
It looks like this:
void OpenGLScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
int mousex = event->scenePos().x();
int mousey = event->scenePos().y();
if ((test->modus==2) && (test->move1 != -1))
{
p_list[test->move1].x=mousex-(1220);
p_list[test->move1].y=mousey-( 610);
test->refresh = !(test->refresh);
test->timerEvent(???);
update();
}
}
But somehow I dont know what to put into where the questions marks are. I have tried several things. It is not working. I want to set timerId()=1.
Thanks for your help...

why don't you call your own event like :
void OpenGLScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
// ...
if ((test->modus==2) && (test->move1 != -1))
{
// ...
test->refresh = !(test->refresh);
//test->timerEvent(???); replaced by :
test->manuelUpdate(); // your own function
//...
}
}
and in your GLWidget :
void GLWidget::manuelUpdate()
{
if (refresh==true)
{
refresh = !refresh;
swapBuffers();
update();
}
}

Related

I Can't delete a member variable of wxApp class

I have a CTaskBarIcon *m_pTaskbar member variable in myApp class.
That variable will take an instance of CTaskBarIcon object.
When deleting the m_pTaskbar from within onExit method a runtime error occurs when quitting the program, and cause of the problem is to delete m_pTaskbar variable.
app.h
#include "taskBarIcon.h"
class myApp: public wxApp{
public:
// ....
private:
CTaskBarIcon *m_pTaskbar; // = NULL
};
app.cpp
int myApp::OnExit() {
if (m_pTaskbar != NULL) {
delete m_pTaskbar; // <-- The problem here
m_pTaskbar = NULL;
}
return 0;
}
int myApp::OnRun() {
mainFrm *_mainFrm = mainFrm::getInstance(); // The main window
_mainFrm->Show(false);
m_pTaskbar = new CTaskBarIcon(_mainFrm);
m_pTaskbar->SetIcon(wxIcon("appIcon"), _mainFrm->GetTitle());
return wxApp::OnRun();
}
CTaskBarIcon.cpp
CTaskBarIcon::CTaskBarIcon(mainFrm *handler) : m_pHandler(handler), m_pMenu(NULL) {
this->Bind(wxEVT_TASKBAR_CLICK, &CTaskBarIcon::rightButton_Click, this);
}
CTaskBarIcon::~CTaskBarIcon() {
if (m_pMenu != NULL) {
delete m_pMenu;
m_pMenu = NULL;
}
}
void CTaskBarIcon::rightButton_Click(wxTaskBarIconEvent & event) {
PopupMenu(CreatePopupMenu());
}
void CTaskBarIcon::popupMenu_showWindow(wxCommandEvent & event) {
m_pHandler->Iconize(false); // 'm_pHandler' is the main window
m_pHandler->Show();
m_pHandler->Raise();
this->Destroy();
}
void CTaskBarIcon::popupMenu_showAbout(wxCommandEvent & event) {
aboutFrm aboutWindow(m_pHandler, "About");
aboutWindow.ShowModal();
aboutWindow.Raise();
}
void CTaskBarIcon::popupMenu_exit(wxCommandEvent & event) {
m_pHandler->Close(true);
this->Destroy();
}
wxMenu* CTaskBarIcon::CreatePopupMenu() {
this->m_pMenu = new wxMenu();
m_pMenu->Append(wxID_SHOW, "&Show");
m_pMenu->Append(wxID_ABOUT, "&About");
m_pMenu->Append(wxID_EXIT, "E&xit");
m_pMenu->Bind(wxEVT_MENU, &CTaskBarIcon::popupMenu_showWindow, this, wxID_SHOW);
m_pMenu->Bind(wxEVT_MENU, &CTaskBarIcon::popupMenu_showAbout, this, wxID_ABOUT);
m_pMenu->Bind(wxEVT_MENU, &CTaskBarIcon::popupMenu_exit, this, wxID_EXIT);
return this->m_pMenu;
}
Is there a problem with my code?
There might be a problem with deleting a wxTaskBarIcon from its own event handler. Can you try if using CallAfter([this] { Destroy(); }) instead of just calling Destroy() directly fixes the problem?

How to refresh QGraphicsView to show changes in the QGraphicsScene's background

I have a custom QGraphicsView and QGraphicsScene. Inside QGraphicsScene I have overriden void drawBackground(QPainter *painter, const QRectF &rect) and based on a boolean flag I want to toggle a grid on and off. I tried calling clear() or calling the painter's eraseRect(sceneRect()) inside my function but it didn't work. So after doing some reading I guess it wasn't supposed to work since after changing the scene you need to refresh the view. That's why I'm emitting a signal called signalUpdateViewport()
void Scene::drawBackground(QPainter *painter, const QRectF &rect) {
if(this->gridEnabled) {
// Draw grid
}
else {
// Erase using the painter
painter->eraseRect(sceneRect());
// or by calling
//clear();
}
// Trigger refresh of view
emit signalUpdateViewport();
QGraphicsScene::drawBackground(painter, rect);
}
which is then captured by my view:
void View::slotUpdateViewport() {
this->viewport()->update();
}
Needless to say this didn't work. With doesn't work I mean that the results (be it a refresh from inside the scene or inside the view) are made visible only when changing the widget for example triggering a resize event.
How do I properly refresh the view to my scene to display the changed that I have made in the scene's background?
The code:
scene.h
#ifndef SCENE_HPP
#define SCENE_HPP
#include <QGraphicsScene>
class View;
class Scene : public QGraphicsScene
{
Q_OBJECT
Q_ENUMS(Mode)
Q_ENUMS(ItemType)
public:
enum Mode { Default, Insert, Select };
enum ItemType { None, ConnectionCurve, ConnectionLine, Node };
Scene(QObject* parent = Q_NULLPTR);
~Scene();
void setMode(Mode mode, ItemType itemType);
signals:
void signalCursorCoords(int x, int y);
void signalSceneModeChanged(Scene::Mode sceneMode);
void signalSceneItemTypeChanged(Scene::ItemType sceneItemType);
void signalGridDisplayChanged(bool gridEnabled);
void signalUpdateViewport();
public slots:
void slotSetSceneMode(Scene::Mode sceneMode);
void slotSetSceneItemType(Scene::ItemType sceneItemType);
void slotSetGridStep(int gridStep);
void slotToggleGrid(bool flag);
private:
Mode sceneMode;
ItemType sceneItemType;
bool gridEnabled;
int gridStep;
void makeItemsControllable(bool areControllable);
double round(double val);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
void drawBackground(QPainter *painter, const QRectF &rect);
};
Q_DECLARE_METATYPE(Scene::Mode)
Q_DECLARE_METATYPE(Scene::ItemType)
#endif // SCENE_HPP
scene.cpp
#include <QGraphicsItem>
#include <QGraphicsView>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QRectF>
#include <QKeyEvent>
#include <QtDebug>
#include "scene.h"
Scene::Scene(QObject* parent)
: QGraphicsScene (parent),
sceneMode(Default),
sceneItemType(None),
gridEnabled(true),
gridStep(30)
{
}
Scene::~Scene()
{
}
void Scene::setMode(Mode mode, ItemType itemType)
{
this->sceneMode = mode;
this->sceneItemType = itemType;
QGraphicsView::DragMode vMode = QGraphicsView::NoDrag;
switch(mode) {
case Insert:
{
makeItemsControllable(false);
vMode = QGraphicsView::NoDrag;
break;
}
case Select:
{
makeItemsControllable(true);
vMode = QGraphicsView::RubberBandDrag;
break;
}
case Default:
{
makeItemsControllable(false);
vMode = QGraphicsView::NoDrag;
break;
}
}
QGraphicsView* mView = views().at(0);
if(mView) {
mView->setDragMode(vMode);
}
}
void Scene::slotSetSceneMode(Scene::Mode sceneMode)
{
this->sceneMode = sceneMode;
qDebug() << "SM" << (int)this->sceneMode;
emit signalSceneModeChanged(this->sceneMode);
}
void Scene::slotSetSceneItemType(Scene::ItemType sceneItemType)
{
this->sceneItemType = sceneItemType;
qDebug() << "SIT:" << (int)this->sceneItemType;
emit signalSceneItemTypeChanged(this->sceneItemType);
}
void Scene::slotSetGridStep(int gridStep)
{
this->gridStep = gridStep;
}
void Scene::slotToggleGrid(bool flag)
{
this->gridEnabled = flag;
invalidate(sceneRect(), BackgroundLayer);
qDebug() << "Grid " << (this->gridEnabled ? "enabled" : "disabled");
update(sceneRect());
emit signalGridDisplayChanged(this->gridEnabled);
}
void Scene::makeItemsControllable(bool areControllable)
{
foreach(QGraphicsItem* item, items()) {
if(/*item->type() == QCVN_Node_Top::Type
||*/ item->type() == QGraphicsLineItem::Type
|| item->type() == QGraphicsPathItem::Type) {
item->setFlag(QGraphicsItem::ItemIsMovable, areControllable);
item->setFlag(QGraphicsItem::ItemIsSelectable, areControllable);
}
}
}
double Scene::round(double val)
{
int tmp = int(val) + this->gridStep/2;
tmp -= tmp % this->gridStep;
return double(tmp);
}
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsScene::mousePressEvent(event);
}
void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
emit signalCursorCoords(int(event->scenePos().x()), int(event->scenePos().y()));
QGraphicsScene::mouseMoveEvent(event);
}
void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsScene::mouseReleaseEvent(event);
}
void Scene::keyPressEvent(QKeyEvent* event)
{
if(event->key() == Qt::Key_M) {
slotSetSceneMode(static_cast<Mode>((int(this->sceneMode) + 1) % 3));
}
else if(event->key() == Qt::Key_G) {
slotToggleGrid(!this->gridEnabled);
}
QGraphicsScene::keyPressEvent(event);
}
void Scene::drawBackground(QPainter *painter, const QRectF &rect)
{
// FIXME Clearing and drawing the grid happens only when scene size or something else changes
if(this->gridEnabled) {
painter->setPen(QPen(QColor(200, 200, 255, 125)));
// draw horizontal grid
double start = round(rect.top());
if (start > rect.top()) {
start -= this->gridStep;
}
for (double y = start - this->gridStep; y < rect.bottom(); ) {
y += this->gridStep;
painter->drawLine(int(rect.left()), int(y), int(rect.right()), int(y));
}
// now draw vertical grid
start = round(rect.left());
if (start > rect.left()) {
start -= this->gridStep;
}
for (double x = start - this->gridStep; x < rect.right(); x += this->gridStep) {
painter->drawLine(int(x), int(rect.top()), int(x), int(rect.bottom()));
}
}
else {
// Erase whole scene's background
painter->eraseRect(sceneRect());
}
slotToggleGrid(this->gridEnabled);
QGraphicsScene::drawBackground(painter, rect);
}
The View currently doesn't contain anything - all is default. The setting of my Scene and View instance inside my QMainWindow is as follows:
void App::initViewer()
{
this->scene = new Scene(this);
this->view = new View(this->scene, this);
this->viewport = new QGLWidget(QGLFormat(QGL::SampleBuffers), this->view);
this->view->setRenderHints(QPainter::Antialiasing);
this->view->setViewport(this->viewport);
this->view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
this->view->setCacheMode(QGraphicsView::CacheBackground);
setCentralWidget(this->view);
}
EDIT: I tried calling invalidate(sceneRect(), BackgroundLayer) before the update(), clear() or whatever I tried to trigger a refresh.
I also tried QGraphicsScene::update() from within the scene but it didn't work Passing both no argument to the function call and then passing sceneRect() didn't result in anything different then what I've described above.
Found the issue - I forgot to set the size of the scene's rectangle:
this->scene->setSceneRect(QRectF(QPointF(-1000, 1000), QSizeF(2000, 2000)));
I actually found the problem by deciding to print the size of the QRectF returned by the sceneRect() call and when I looked at the output I saw 0, 0 so basically I was indeed triggering the update but on a scene with the area of 0 which (obviously) would result in nothing.
Another thing that I tested and worked was to remove the background caching of my view.
When you change your grid settings, whether it's on or off (or color, etc.), you need to call QGraphicsScene::update. That's also a slot, so you can connect a signal to it if you want. You can also specify the exposed area; if you don't, then it uses a default of everything. For a grid, that's probably what you want.
You don't need to clear the grid. The update call ensures that the updated area gets cleared, and then you either paint on it if you want the grid, or don't paint on it if the grid shouldn't be there.
Sometimes when the view/scene refuse to run update() directly, processing the app events before the update() fixes it, like so:
qApp->processEvents();
update();

How QTableView or QListView is scrolling with hand drag?

in QGraphicview,
if we set it with : ui->graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
this code make graphicsview can scroll items with mouse pressed and drag.
How can we make QListView or QTableView as the QGraphicsView?
You will need to subclass these widgets and reimplement QWidget::mousePressEvent, QWidget::mousMoveEvent and QWidget::mouseReleaseEvent. However you will have to be careful because you may be interfering with actions that are mapped to these by default implementations (e.g. selecting) so it would need to be tweaked a bit. For example (assumed subclass of QListView):
void MyListView::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton) //lets map scrolling to right button
m_ScrollStart = event->pos(); //QPoint member, indicates the start of the scroll
else
QListView::mousePressEvent(event);
}
and then
void MyListView::mouseMoveEvent(QMouseEvent *event)
{
if(!m_ScrollStart.isNull()) //if the scroll was started
{
bool direction = (m_ScrollStart.y() < event->pos().y()); //determine direction, true is up (start is below current), false is down (start is above current)
int singleStep = (direction ? 10 : -10); //fill in the desired value
verticalScrollBar()->setValue(verticalScrollBar()->value() + singleStep);
//scroll by the certain amount in determined direction,
//you decide how much will be a single step... test and see what you like
}
QListView::mouseMoveEvent(event);
}
and finally
void MyListView::mouseReleaseEvent(QMouseEvent *event)
{
m_ScrollStart = QPoint(); //resets the scroll drag
QListView::mouseReleaseEvent(event);
}
like Resurrection did mention
You will need to subclass these widgets and reimplement QWidget::mousePressEvent, QWidget::mousMoveEvent and QWidget::mouseReleaseEvent
but below code is more preferred by us:
class MyListView : public QListView
{
typedef QListView super;
public:
explicit MyListView(QWidget *parent = 0);
protected:
// QWidget interface
void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE;
private:
enum DragState {
DragStopped,
DragStarted,
Dragged
};
quint8 m_dragState;
int m_dragStartPos;
};
MyListView::MyListView(QWidget *parent)
: super(parent)
, m_dragState(DragStopped)
, m_dragStartPos(-1)
{
}
void MyListView::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton) {
m_dragState = DragStarted;
m_dragStartPos = event->pos().y();
} else
super::mousePressEvent(event);
}
void MyListView::mouseReleaseEvent(QMouseEvent *event)
{
if(m_dragState) {
m_dragState = DragStopped;
m_dragStartPos = -1;
return;
}
super::mouseReleaseEvent(event);
}
void MyListView::mouseMoveEvent(QMouseEvent *event)
{
if(m_dragState != DragStopped) {
const int itemSize = sizeHintForRow(0) / 2;
const int distance = qAbs(m_dragStartPos - event->pos().y());
if(distance > 10)
m_dragState = Dragged;
if(distance > itemSize) {
QScrollBar *scrollBar = this->verticalScrollBar();
int stepCount = (distance/itemSize);
if(m_dragStartPos < event->pos().y())
stepCount = -stepCount; //scrolling up
scrollBar->setValue(scrollBar->value() + (stepCount * scrollBar->singleStep()));
m_dragStartPos = event->y();
}
return;
}
super::mouseMoveEvent(event);
}

Algorithm for drawing a straight line with Shift button pressing

I have line painter widget in my project. So, I have to develope algorithm, which allows to correct line to straight, when user presser Shift key. i have seen similar in Photoshop and Paint.
But my own method doesn't work. I'm working with Qt libs.
Code here. drawedPoints_ is QList of QPointF
void GesturePaintSurface::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
isDrawingFinished_ = false;
drawedPoints_.clear();
firstPoint_ = event->pos();
drawedPoints_.append(firstPoint_);
}
else
{
event->ignore();
isDrawingFinished_ = true;
}
}
void GesturePaintSurface::mouseMoveEvent(QMouseEvent *event)
{
if (!isDrawingFinished_)
{
if (isShiftPressed_)
{
QPoint newPoint;
if (abs(drawedPoints_.last().rx() - event->pos().rx()) < 5)
{
newPoint.rx() = firstPoint_.x();
newPoint.ry() = event->pos().y();
}
else
{
newPoint.rx() = event->pos().x();
newPoint.ry() = firstPoint_.y();
}
drawedPoints_.append(newPoint);
}
else
drawedPoints_.append(event->pos());
repaint();
}
}
Sorry for my language. thanks in advance!
First problem is that you are appending point every time mouse moves! Second problem is that this condition to make vertical or horizontal line is wrong.
It should be like that:
void GesturePaintSurface::mouseMoveEvent(QMouseEvent *event)
{
if (!isDrawingFinished_)
{
QPoint newPoint;
if (event->modifiers().test(Qt::ShiftModifier)) {
newPoint = drawedPoints_.last();
if (qAbs(newPoint.x()-event->x())<qAbs(newPoint.y()-event->y())) {
newPoint.setY(event->y());
} else {
newPoint.setX(event->X());
}
} else {
newPoint = event->pos();
}
if (drawedPoints_.count()>1) {
drawedPoints_.last() = newPoint;
} else {
drawedPoints_.append(newPoint);
}
update(); // update is better then repaint!
}
}

Cocos2d-x Subclassing Issue CCMenuItemImage

I'm trying to create a button that is derived from the base class CCMenuItemImage. I want this button to be able to call it's function when it's first touched instead of after the touch ends. However, trying to subclass, I get an error saying it's an invald conversion.
button.ccp:
#include "button.h"
void Button::selected(){
CCLOG("SELECTED");
}
void Button::unselected(){
CCLOG("UNSELECTED");
}
Button* Button::create(const char *normalImage, const char *selectedImage, const char *disabledImage, CCObject* target, SEL_MenuHandler selector) {
Button *button = new Button();
if (button && button->initWithNormalImage(normalImage, selectedImage, disabledImage, NULL, NULL))
{
button->autorelease();
return button;
}
CC_SAFE_DELETE(button);
return NULL;
}
button.h:
#ifndef BUTTON_H
#define BUTTON_H
#include "cocos2d.h"
class Button : public cocos2d::CCMenuItemImage{
public:
virtual void selected();
virtual void unselected();
};
#endif
SinglePlayer.ccp piece:
Button *left1 = Button::create("turncircle.png","turncircle.png", this, menu_selector(SinglePlayer::turning));
MenuItem select() is triggered on touch finished by default.
You need to subclass CCSprite with Touch registered with dispatcher and overwrite the ccTouchBegan
What I can understand is that you are trying to do manual control with the touches of your CCMenuItemImage. Actually all the touches are being handled in CCMenu not in MenuItem so you have to inherit CCMenu rather CCMenuItemImage to override the touches.
In my game I had this problem with CCTableView and CCMenuItem where MenuItem was a prioritised in taking gestures. So I tweaked it with inheriting CCMenu.
It also contain some extra code but just to make everything gets intact I am pasting everything.
ScrollMenu.h Class
class ScrollMenu:public CCMenu
{
public:
ScrollMenu();
virtual ~ScrollMenu(){};
bool isMovedGesture_;
bool istabBar_;
CCMenuItem * previousSelectedItem_;
static ScrollMenu* create(CCMenuItem* item,...);
virtual void registerWithTouchDispatcher();
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
virtual void ccTouchEnded(CCTouch *touch, CCEvent* event);
CREATE_FUNC(ScrollMenu);
};
class ScrollMenuLoader : public cocos2d::extension::CCNodeLoader
{
public:
CCB_STATIC_NEW_AUTORELEASE_OBJECT_METHOD(ScrollMenuLoader, loader);
protected:
CCB_VIRTUAL_NEW_AUTORELEASE_CREATECCNODE_METHOD(ScrollMenu);
};
ScrollMenu.cpp Class
#include "ScrollMenu.h"
ScrollMenu* ScrollMenu::create(CCMenuItem* item, ...)
{
va_list args;
va_start(args, item);
ScrollMenu *pRet = new ScrollMenu();
if (pRet && pRet->initWithItems(item,args))
{
pRet->autorelease();
va_end(args);
return pRet;
}
va_end(args);
CC_SAFE_DELETE(pRet);
return NULL;
}
ScrollMenu::ScrollMenu()
{
isMovedGesture_ = false;
}
void ScrollMenu::registerWithTouchDispatcher()
{
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}
bool ScrollMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
return CCMenu::ccTouchBegan(touch, event);
}
void ScrollMenu::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
CC_UNUSED_PARAM(event);
CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchMoved] -- invalid state");
isMovedGesture_ = true;
}
void ScrollMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
{
CC_UNUSED_PARAM(touch);
CC_UNUSED_PARAM(event);
CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchEnded] -- invalid state");
CCMenuItem * currentItem = this->itemForTouch(touch);
if(!currentItem && isMovedGesture_ && m_pSelectedItem)
{
if(!istabBar_ || (previousSelectedItem_ && previousSelectedItem_ != m_pSelectedItem))
{
m_pSelectedItem->unselected();
}
}
else if(currentItem)
{
if(currentItem == m_pSelectedItem)
{
if(!isMovedGesture_)
{
m_pSelectedItem->activate();
previousSelectedItem_ = m_pSelectedItem;
}
else{
if(previousSelectedItem_ != m_pSelectedItem)
{
m_pSelectedItem->unselected();
}
}
}
else
{
if(isMovedGesture_)
{
m_pSelectedItem->unselected();
m_pSelectedItem = currentItem;
m_pSelectedItem->activate();
previousSelectedItem_ = m_pSelectedItem;
}
}
if (!istabBar_) {
currentItem->unselected();
}
}
m_eState = kCCMenuStateWaiting;
isMovedGesture_ = false;
}