calling Qt's QGraphicsView::setViewport with a custom QGLWidget - qt

I've derived from QGLWidget before, like so:
class MyGLWidget : public QGLWidget
{
public:
// stuff...
virtual void initializeGL() { /* my custom OpenGL initialization routine */ }
// more stuff...
};
However, I find that if I try to initialize a QGraphicsView with my custom QGLWidget as the viewport, initializeGL doesn't get called (setting a breakpoint within the Qt library, neither does QGLWidget::initializeGL() when created plain).
// initializeGL, resizeGL, paintGL not called
ui.graphicsView->setViewport(new MyGLWidget(QGLFormat(QGL::DoubleBuffer)));
// initializeGL, resizeGL, paintGL *still* not called
ui.graphicsView->setViewport(new QGLWidget(QGLFormat(QGL::DoubleBuffer)));
Where is the correct location to place the code that currently resides in MyGLWidget::initializeGL()?

The setupViewport slot of a custom QGraphicsView could be used to call updateGL() on the QGLWidget, which will cause initializeGL() to be called.
class MyGraphicsView : public QGraphicsView
{
//... The usual stuff
protected slots:
virtual void setupViewport(QWidget *viewport)
{
QGLWidget *glWidget = qobject_cast<QGLWidget*>(viewport);
if (glWidget)
glWidget->updateGL();
}
};

So what I've found is QGraphicsView installs a custom eventFilter on your QGLWidget viewport so it never sees the initialize/resize/repaint events. This probably was done to make it work properly with drawBackground() etc.
My current best resolution is to catch the desired event either in QGraphicsView::resizeEvent()/etc, or install a custom eventFilter on your QGLWidget derived class to catch the resize/paint/etc events before QGraphicsView's custom eventFilter swallows them.

The pain, the pain, ... integrating widgets derived from QGlWidgets into QGraphicsView is no fun, of the parts of Qt that I know this is definitely one of the messier areas. I ended up using a part of kgllib (out of kde) called widgetproxy that is a very decent wrapper around a QGlWidget. I modified it to fit my needs but works reasonably well for most general cases where you want to use an exisiting class derived from QGlWidget inside a QGraphicsView and draw other things on top of it.

initializeGL() won't get called until the first call to either paintGL() or resizeGL() and not when the widget is constructed. This may happen as late as when the widget is first made visible.

I'm going to go ahead and answer my own question. This isn't optimal, but this is how I've gotten around the problem.
Instead of
ui.graphicsView->setViewport(new MyGLWidget(QGLFormat(QGL::DoubleBuffer)));
I've got this instead:
ui.graphicsView->setViewport(new QGLWidget(new CustomContext(QGLFormat(QGL::SampleBuffers))));
CustomContext is a class that derives from QGLContext. I've overridden the create member, like so:
virtual bool create(const QGLContext *shareContext = 0)
{
if(QGLContext::create(shareContext))
{
makeCurrent();
/* do my initialization here */
doneCurrent();
return true;
}
return false;
}
I don't think this is the optimal way to do this, but it's better than the alternative of not having a specific initialization step at all. I'd still be happy to have someone leave a better answer!

Related

How to handle QComboBox signals in inner classes

I am using Qt5 and I have created a class "outer" with an "inner" class. The "inner" class has two QComboBox "cb1" and "cb2" objects as private variables.
In the principle, the displayed text of the second QComboBox, "cb2", depends of the current text of the first QComboBox "cb1". In fact, it is easy to implement a connection between these two, using signals and slots by writing the appropriate slot.
The problem is that Qt does not support writing slots inside an inner class. That makes me confused.
How can I handle a connection between these two QComboBoxes in the "inner" class?
For some code,
class Outer : public QDialog
{
Q_OBJECT
// private variables;
class Inner : public QWidget
{
QComboBox *cb1, *cb2;
// Other variables;
public:
// Public methods
public slots:
void setIndex(int i);
};
// Other things;
};
Inner Implementation
Outer::Inner::Inner()
{
// Useless things;
connect(cb1, SIGNAL(currentIndexChanged(int)), this, SLOT(setIndex(int)));
}
Outer::Inner::setIndex(int i)
{
// Some stuff to retrieve the correct index in cb2;
}
In Qt 5, any method can be connected to a signal, whether marked as a slot or not, so what you are describing is a non problem.
You need to use the modern connect syntax, and it will work fine:
connect(cb1, &QComboBox::currentIndexChanged, this, &Outer::Inner::setIndex);
Of course, nothing else will work correctly: the qobject_cast mechanism won't work, metadata will be wrong, etc.
An inner class cannot be a QObject. I completely fail to see the point of it. Make it a local class in the .cpp file instead of an inner class.
From your code I don't get a reason why you need an inner class at all.
If you just want to avoid the possibility to use the Class somewhere else, I would recommend to put it in a anonymous namespace.
namespace {
class YourAnonymousClass{
...
}
}
class TheOtherClass {
...
}

Qt QGraphicsScene and QGraphicsItem events

I've got some QGraphicsObjects which are dependent of the size of the scene they are on. So my graphics object needs to know when two events occur:
When it is added to the scene
When it's scene is resized
The way I do it now is - create 2 signals in scene's parent: obj_create, scene_resize. And connect them to the slots of the gr.object. It seems to be not the best way. I can't find any event like addedToScene or sceneResized in the QGraphcisItem...
Thanks much.
I would suggest one of two possible ways: Either subclass QObject in your GraphicsItem and simply use Signal/Slots or define your own interface, lets say IResizableEvent with a resize method. In your GraphicsItem you implement the method with your resize code. When you detect a scene resize in your Scene class, just iterate over all items, cast them to the interface type and call the resize method.
I just needed to read the doc little bit accurately... Hope it helps someone...
QVariant itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemSceneHasChanged)
{
this->performSomeUpdates();
QObject::connect(this->scene(),SIGNAL(sceneRectChanged(QRectF)),this,SLOT(sceneRectChanged(QRectF)));
}
return QGraphicsItem::itemChange(change, value);
}

Drag and drop widget outside source application widgets

I have a Qt Desktop aplication which has several top-level widgets. Subwidgets of top-level widgets can be moved between top-level widgets by using drag-and-drop mechanism.
The problem i have now is to drop a sub-widget outside any of existing top-level widgets and create a new top-level widget to contain this one. Lets call this separation.
Can this be done using drag-and-drop? I could not find a way where my dropEvent goes?
Can i want to handle the drop event in my application even if the drop place is not allowed? Maybe a mouse release or something?
I cannot change everything now but also a question for the future. Is docking/undocking a better way to do this?
Regards
Mihai
I found a way to do this. When drag moves outside of the application widgets QDrag object emits a targetChanged signal with 0 parameter.
So i inherited from QDrag and then emit a custom signal in destructor if the target() is null.
The only problem is that the cursor looks like interdiction of drop and this i could not fix because QDrag can only set cursor pixmap for valid actions like Move or Copy or Link
Update:
Here is the inherited class.
class TabDrag: public QDrag
{
Q_OBJECT
public:
explicit TabDrag(QWidget *dragSource);
~TabDrag();
signals:
void tearOff(); /// emit tearOff signal if the QDrag object is destroyed and target was null
};
TabDrag::TabDrag(QWidget *dragSource):QDrag(dragSource)
{
}
TabDrag::~TabDrag()
{
// check if we need to detach this tab
if(!target())
{
emit tearOff();
}
}
The tearOff signal should be connected to whatever you want to happen. In my case i pull out the widget from the tab and change parent to a new window.
Example of usage
void MyTabBar::mouseMoveEvent(QMouseEvent* event)
{
..................
TabDrag * drag = new TabDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(*m_tabPixmap.data());
drag->setHotSpot(QPoint(m_dragStartPos.x() - tabAtRect.x(), m_dragStartPos.y() - tabAtRect.y()));
drag->exec();
connect(drag, SIGNAL(tearOff()), this, SLOT(onTearOff()));
}

Changing QWidget with QGlWidget in Qt 4.8.0

I am working on an application that currently uses QWidgets for rendering moving images, and I am trying to move from using QWidgets to QGlWidgets, but my application keeps crashing.
The only modifications I've done are:
from this:
class MyClass : public QWidget
{
MyClass(QWidget *parent):QWidget(parent)
{
...
}
}
to this:
class MyClass : public QGlWidget
{
MyClass(QWidget *parent):QGlWidget(QGLFormat(QGL::SampleBuffers),parent)
{
...
}
}
Do I have to modify my code more, if so what other modifications are needed?
I am currently using Visual studio 2010, and qt 4.8.0
MyClass(QWidget *parent):QGlWidget(QGLFormat(QGL::SampleBuffers),parent)
{
...
}
It looks like you're creating a temporary QGLFormat object, which is being passed by reference to QGLWidget, and when that temporary object goes out of scope, you'll have an invalid reference.
I would use a different form of the constructor.
I found what the problem was.
In my QWidget class I had a member object QPainter, that I would reuse in the paint event. I kept searching and found that this is a bad practice, so now I declared a QPainter object at the beginning of the paint event and my problem is solved.

How to install and handle the event filter to Qt graphicsview

I have a graphicsview and a graphicsscen, but I don't know how to install and handle the event filter for getting the keyboard events. Can anyone help me with that?
Thanks in advance.
If you have created custom QGraphicsScene class you can just override QWidget's "QWidget::keyPressEvent()" and "QWidget::keyReleaseEvent()" methods.
class MyGraphicsScene : QGraphicsScene
{
void keyPressEvent(QKeyEvent *event);
}
//in cpp
void MyGraphicsScene::keyPressEvent(QKeyEvent *event)
{
// do sth with event
}
If you are just using an istance of QGraphicsScene, you can use parent's keyPressEvent. Whether or not you must give more details
You have two options to do that:
1) Create your own class based on QGraphicsView and override keyPressEvent(). That only has sense if you going to change a lot of other things.
2) Install event filter, using installEventFilter(..) method and pass there filter object which will receive everything you might need

Resources