QGraphicsView->centerOn() doesn't work - qt

I can't figure out how to make centerOn() to move viewport around the item (or the item around the port, not sure which way it is).
The following code does work in main:
view.setDragMode(QGraphicsView::ScrollHandDrag);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setFrameStyle(QFrame::NoFrame);
view.showFullScreen();
QGraphicsPixmapItem *pmItem = new QGraphicsPixmapItem(pixMap);
scene.addItem(pmItem);
view.centerOn(QPointF(50,30));
view.show();
rc = qA.exec();
However, the centerOn() does nothing when I am trying to do the same from a paintEvent() of an overloaded QGraphicsView:
class MyView :: public QGraphicsView { ... }
void MyView::init(){
...
setDragMode(QGraphicsView::ScrollHandDrag);
setFrameStyle(QFrame::NoFrame);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
_scene.clear();
setScene(&_scene);
super::showNormal();
}
void MyView::paintEvent(QPaintEvent *aEvent){
_scene.clear();
QGraphicsPixmapItem *pmItem = new QGraphicsPixmapItem(pixMap);
_scene.addItem(pmItem);
centerOn(QPointF(50.0, 30.0));
super::paintEvent(aEvent);
}
What do I miss?
Thank you
Alex

You basically pull the rug under the paintEvent call.
QGraphicsView communicates with QGraphicsScene by way of signals and slots. The scene has no chance to notify the view that a new item is just added to it since the actual paintEvent is called right after the creation. There's no event loop process between the 2 steps. So the paintEvent call doesn't have it setup properly for the new item, let alone the centering change.
And once the painting is finished, the view is validated and thinks it's all painted. So the it'll never update the area of the new item.
Call centerOn outside of any painting or updating event.

I solved this problem by placing a high value on setSceneRect.
Then I centralize the scene on an object or position.
example:
this->ui->graphicsView->setSceneRect (0,0,100000000, 100000000);
this->ui->graphicsView->centerOn(500,1030);
With the setSceneRect size GraphicsView does not work right.

Related

QStackedWidget connect QActions to custom QGraphicsView of QWidget

I've made a MainWindow with a QStackedWidget and a QMenuBar.
One of my widgets contains a QGraphicsView that I promoted to my custom QGraphicsView (DrawingView).
I'd like to connect the QActions from the menuBar to my custom DrawingView.
I managed to get the QActions that were connected to the widget working, but I don't know how to access the methods from the DrawingView.
This is the code I used for the other QActions:
draw = qobject_cast<Drawing*>(ui->stackedWidget->widget(1));
connect(ui->actionOpen, &QAction::triggered, draw, &Drawing::openPhoto);
connect(ui->actionSave, &QAction::triggered, draw, &Drawing::saveFile);
connect(ui->actionExit, &QAction::triggered, draw, &Drawing::close);
I tried this for the DrawingView, but I know that it just makes a new DrawingView instead of using the one from the Draw ui.
drawView = new DrawingView();
connect(ui->actionZoom_In, &QAction::triggered, drawView, &DrawingView::zoomIn);
connect(ui->actionZoom_Out, &QAction::triggered, drawView, &DrawingView::zoomOut);
I also tried this, which gave an error on ui->stackedWidget->widget(1)->graphicsView:
drawView = qobject_cast<DrawingView*>(ui->stackedWidget->widget(1)->graphicsView); //also tried (ui->stackedWidget->widget(1)->ui->graphicsView)
connect(ui->actionZoom_In, &QAction::triggered, drawView, &DrawingView::zoomIn);
connect(ui->actionZoom_Out, &QAction::triggered, drawView, &DrawingView::zoomOut);
Any help on how to connect from the MainWindow or how to access the ui of MainWindow inside 2nd widget.
Isn’t your problem simply that graphicsView is a private member of the containing widget?
You could either make it a public member or add a wrapper function:
auto draw = qobject_cast<Drawing*>(ui->stackedWidget->widget(1));
connect(ui->actionZoom_In, &QAction::triggered, draw, &Drawing::zomIn);
and in the Drawing class:
void Drawing::zoomIn() {
ui->graphicsView.zoomIn();
}
A third option is to
emit a custom zoomIn signal from the MainWindow
make the main window accessible to Drawing (e.g. passing it to the constructor or creating a global mainWindow singleton)
and do the connect directly in Drawing

Show a QWidget after pressing a button

I want to change a QWidget in a QMainWindow dynamically. Therefore, the old widget (if it exists) will be deleted, a new one will be constructed and added to the main window.
The widget (_visualization) is a QMainWindow itself that contains a menu bar and a widget that shows an OSG scene graph.
If I don´t call show() on the new widget, I will only see the menu bar, but not the scene graph.
My goal is to call show(), when the user clicks on a button. The button is connected with the outer QMainWindow (MyQMainWindow).
Unfortunately, when I call show() on _visualization in the connected method, the scene graph will not be shown. In contrast to that, the scene graph will be shown, if I call it in the constructor method (loadVisualization(...)).
MyQMainWindow::MyQMainWindow(QWidget *parent ) :
QMainWindow(parent) {
...
loadVisualization(...);
connect(_ui->nextButton, SIGNAL(clicked()), this, SLOT(showNext()));
...
}
void MyQMainWindow::loadVisualization(QString filePath) {
if (_visualization) {
_heightWidgetLayout->removeWidget(_visualization);
delete _visualization;
}
_visualization= new HeightVisualization(0, filePath);
_visualization->setParent(_mainWindowWidget);
_heightWidgetLayout->addWidget(_visualization);
//_visualization->show(); // will work here
}
void MyQMainWindow::showNext() {
_visualization->show(); // does not work here!
}
I know that QT will call setVisible(...) on the widget. This method first tests some states in QT (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)). If I call show() in showNext(), these tests lead to a return without any change.
Is it a problem with connectors and slots? Is there a possibility to show the widget, when the user clicked on a button?
What I have learned is that it is easy to use stackedWidget.
You can then programmatically change it with this
currentPageIndex = (currentPageIndex + 1) % 3;
ui->stackedWidget->setCurrentIndex(0);
The 3 can be the total pages you have in your stack widget. You can either use currentPageIndex, or you can just create some constants with the page ids like I have done with the 0.

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()));
}

I cant paint QVideoWidget with QPainter

I am trying to draw strings while I have a video palying, like a subtitle...
I have a Phonon::QVideoWidget, in its constructor I do:
painter = new QPainter(this);
and I have overrided the paint event to this, only for test:
void MyVideoWidget::paintEvent(QPaintEvent* event)
{
painter->drawLine(0, 0, 1, 1);
//painter-> anything shows
}
So when I start my player I see nothing that QPainter did, only the normal video playing
any ideas?
It is more common to make the QPainter a local instance in the paintEvent() function.
QPainter painter(this);
This results in the begin() and end() methods being called automatically. These are necessary for the QPainter to work correctly. You could try calling them manually in the paintEvent() to see if that makes a difference.
Another thing you might try for overlaying text on the video is to create a QLabel in code and make your video widget its parent. This does not require sub classing the video widget or overriding the paint event.

Resources