QT QGraphicsView rotation - c++

Disclaimer: I am pretty much a beginner with QT.
I've been struggling for some time to rotate a QGraphicsView (no 3D rotation) but, despite what i do, it doesn't work. I have tried:
QTransform transform;
transform.rotate(45);
ui->graphicsView->setTransform(transform);
or more simply:
ui->graphicsView->rotate(45);
These seem like very straightforward ways to do it that should work, but for some reason, whenever i run it, the QGraphicsView doesn't rotate at all. If possible, i'd like some direct and easy to understand code snippets, and/or what i'm doing wrong.
EDIT: This is the code in the widget cpp file i have problems with. It should be a simple timer with an animated hourglass icon. It gets repeated every .5 seconds.
void Widget::timerEvent(QTimerEvent *event)
{
++timeFlag;
++timerFlag;
if (timerFlag < 115){
animateTimer = QString("\":/new/100/timerFrames/timerIconFrame%1.png\"").arg(timerFlag);
QPixmap pix(animateTimer);
pixmapitem.setPixmap(pix);
scene.addItem(&pixmapitem);
ui->graphicsView_2->setScene(&scene);
}
if (timerFlag >= 115 && timerFlag < 119){
//
}
if(timerFlag == 119){
ui->graphicsView_2->setStyleSheet("border-image:url(:/new/100/timerIconPix.PNG);border:0px;}");
}
if(timerFlag == 120){
timerFlag = 0;
}
if (timeFlag==2){
timeFlag = 0;
if(sec>=10){
ui->label_2->setText(QString("%1:%2").arg(min).arg(sec));
} else {
ui->label_2->setText(QString("%1:0%2").arg(min).arg(sec));
}
++sec;
if (sec == 60) {
sec = 0;
++min;
}
}
}

You're merely decorating the QGraphicsView using the style mechanism. You could have used a plain QWidget instead, since you don't use any graphics view functionality. None of the images in the stylesheet are what the view actually displays. The image must be on the scene displayed by the view.
Set the image on a QGraphicsPixmapItem, add that item to a scene, set the scene on the view, and then the transformations will work. You can then keep replacing the pixmap in the timer handler.
Finally, you must also check the timer id in the timerEvent. I assume that you're using a QBasicTimer, say called m_timer, you'd then check as follows:
void Widget::timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
... // rest of the code
}
As you can see, the code that you've not included in the original question was absolutely essential! Without it, the question was wholly off-topic.

You need to implement a QGraphicsView, a QGraphicsScene and then add something that inherits from QGraphicsItem to that scene to rotate.
Here is an example that rotates a QWidget in a QGraphicsView:
QGraphicsView* view = new QGraphicsView(parent);
QGraphicsScene* scene = new QGraphicsScene(view);
view->setScene(scene);
// Widget to rotate - important to not parent it
QWidget* widget = new QWidget();
QProxyWidget proxy_widget = scene_->addWidget(widget);
QPropertyAnimation* animation = new QPropertyAnimation(proxy_widget, "rotation");
animation->setDuration(5000);
animation->setStartValue(0);
animation->setEndValue(360);
animation->setEasingCurve(QEasingCurve::Linear);
animation->start(QAbstractAnimation::DeleteWhenStopped);

Related

Qt how to use paintEvent function to refresh two or more widgets silmultaneously

Now I am using Qt5 to draw Kline of stocks. Two classes are defined for price bar and volume bar, then I add them to a widget by a QSplitter. However, When I move mouse forward or backward and the price-bar chart updates, the volume-bar section will not repaint simultaneously until I put mouse under volume widget. I tried some ideas, but it didn't work. Many thanks.
A part of codes like this:
pvolume = new VolumeBar(this);
pvolume->setObjectName(tr("volume_bar"));
pvolume->setFocusPolicy(Qt::StrongFocus);
pkline = new PriceBar(this);
pkline->setObjectName("price_bar");
pkline->setFocusPolicy(Qt::StrongFocus);
QSplitter *splitterMain = new QSplitter(Qt::Vertical, 0);
splitterMain->insertWidget(0, pkline);
splitterMain->insertWidget(1, pvolume);
splitterMain->setStretchFactor(0, 4);
splitterMain->setStretchFactor(1, 1);
For pricebar, the paintEvent function is overrided as follows,
void PriceBar::paintEvent(QPaintEvent *event)
{
KLineGrid::paintEvent(event); // parent of VolumeBar and PriceBar
drawLine(); // draw price bar
}
For VolumeBar, it is overrided like this,
void VolumeBar::paintEvent(QPaintEvent *event)
{
KLineGrid::paintEvent(event);
drawYtick(); // y axis
drawVolume();
}

Identify which QPixmapItem has been selected

I am adding QGraphicsPixmapItems to my scene, and I can see that when I pick on the item, it gets the white dashed selection rectangle, but I'm struggling to get any data from this selection. Here is how I'm adding it to the scene.
void MainWindow::drawImage(curTarget *newTarget){
QGraphicsPixmapItem *tgt = new QGraphicsPixmapItem;//new pixmap
tgt = scene->addPixmap(newTarget->myIcon);//assign pixmap image
tgt->setFlag(QGraphicsItem::ItemIsSelectable, true);
scene->addItem(tgt);
}
Each PixmapItem that I add to the scene has struct data associated with it, and I need to be able to retrieve that data when I select on the QGraphicsPixmapItem inside of the QGraphicsScene. If the selection rectangle is showing up when the pixmapitem is selected, isn't there some easy way to return information to me based on that fact? A Pointer to what is selected perhaps?
I do have a mousePressEvent method implemented but I'm struggling getting anything relevant with that.
void MainWindow::mousePressEvent(QMouseEvent *event){
qDebug() << "Clicked" << endl;
}
When I run the app, I see Clicked everywhere in my scene except when I click on my pixmapitems.
I've tried every version of the mousePressEvents available and the ones that actually do something, only do something as long as I don't press on my pixmapitems.
Thank to the help I received in my comments, I will post up what ultimately worked for me.
void MainWindow::drawImage(curTarget *newTarget)
{
QGraphicsPixmapItem *tgt = new QGraphicsPixmapItem
tgt = scene->addPixmap(newTarget->myIcon);
tgt->setFlag(QGraphicsItem::ItemIsSelectable, true);
scene->addItem(tgt);
}
With the new function added...
void MainWindow::whatIsSelected(){
QDebug() <<scene->selectedItems() << endl;}
And then I made the connection of the scene to the window elsewhere...
QObject::connect(scene, SIGNAL(selectionChanged()), this, SLOT(whatIsSelected);

QT Graphic scene/view - moving around with mouse

I created my own classes (view and scene) to display image and objects I added to it, even got zoom in/out function implemented to my view, but now I have to add new functionality and I don't even know how to start looking for it.
Whenever I press the scroll button of my mouse and hold it - I wish to move around the scene, to see different parts of it - just like I would with sliders. It is supposed to be similar to any other program allowing to zoom in/out to image and move around zoomed picture to see different parts of it.
Unfortunately - I don't even know how to look for some basic, because "moving" and similar refer to dragging objects around.
EDIT 1
void CustomGraphicView::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() == Qt::MidButton)
{
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
translate(event->x(),event->y());
}
}
Tried this - but it is working in reverse.
I suppose you know how to handle events using Qt.
So, to translate (move) your view use the QGraphicsView::translate() method.
EDIT
How to use it:
void CustomGraphicsView::mousePressEvent(QMouseEvent* event)
{
if (e->button() == Qt::MiddleButton)
{
// Store original position.
m_originX = event->x();
m_originY = event->y();
}
}
void CustomGraphicsView::mouseMoveEvent(QMouseEvent* event)
{
if (e->buttons() & Qt::MidButton)
{
QPointF oldp = mapToScene(m_originX, m_originY);
QPointF newP = mapToScene(event->pos());
QPointF translation = newp - oldp;
translate(translation.x(), translation.y());
m_originX = event->x();
m_originY = event->y();
}
}

Lock app orientation to landscape in Qt

I would like to lock my app developed with Qt to landscape orientation, even if the screen display is portrait. I have tried adding to my code the resizeEvent method found here: http://qt-project.org/doc/qt-4.8/widgets-orientation.html, but my app still does not display correctly. Here is my code for resizeEvent:
void MainWindow::resizeEvent(QResizeEvent *event)
{
QSize size = event->size();
qDebug() << size;
bool isLandscape = size.width() > size.height();
if (isLandscape == false){
size.transpose();
}
this->setFixedSize(size);
}
Does anyone know how to do this in Qt 4.8.5? I am trying to display an app for a 320x240 display.
Thanks
You can't really follow that example. It shows two different widgets depending on the orientation. Furthermore the doc warns about modifying size properties inside resizeEvent.
One solution would be to set a fix aspect ratio similar to 320x240 by overloading QWidget::heightForWidth. You wouldn't need to overload resizeEvent.
It will look like
int MainWindow::heightForWidth( int w ) {
return (w * 240 )/320;
}
heightForWidth is discussed in detail in https://stackoverflow.com/a/1160476/1122645.
edit:
sizeHint is used by the layout of the parent to compute the size of children widgets. In general, it make sense to implement it as
QSize MainWindow::sizeHint() const
{
int w = //some width you seem fit
return QSize( w, heightForWidth(w) );
}
but if MainWindow is top level then it will not change anything. You can also alter heightForWidth flag of your current size policy
QSizePolicy currPolicy = this->sizePolicy();
currPolicy->setHeightForWidth(true);
this->SetSizePolicy(currPolicy);
But again, if it is a top level widget I doesnt change much.
Anyway you don't need to call updateGeometry.

Qt: How to force a hidden widget to calculate its layout?

What I am trying to do is render a qwidget onto a different window (manually using a QPainter)
I have a QWidget (w) with a layout and a bunch of child controls. w is hidden. Until w is shown, there is no layout calculations happening, which is expected.
When I call w->render(painter, w->mapToGlobal(QPoint(0,0)), I get a bunch of controls all overlapping each other.
w->layout()->activate();w->layout()->update() doesn't seem to do anything.
Is there a way to force the layout to happen without showing w?
Forcing a layout calculation on a widget without showing it on the screen:
widget->setAttribute(Qt::WA_DontShowOnScreen);
widget->show();
The show() call will force the layout calculation, and Qt::WA_DontShowOnScreen ensures that the widget is not explicitly shown.
The layout calculation of a widget can be forced by calling invalidate() followed by activate() on its layout, even if the widget is hidden. This also causes the widget's size() and sizeHint() functions to return correct and updated values, even if show() has not yet been called on that widget.
It is however necessary to care about all child widgets and layouts recursively, as a layout recalculation request doesn't automatically propagate to the childs.
The following code shows how to do this.
/**
* Forces the given widget to update, even if it's hidden.
*/
void forceUpdate(QWidget *widget) {
// Update all child widgets.
for (int i = 0; i < widget->children().size(); i++) {
QObject *child = widget->children()[i];
if (child->isWidgetType()) {
forceUpdate((QWidget *)child);
}
}
// Invalidate the layout of the widget.
if (widget->layout()) {
invalidateLayout(widget->layout());
}
}
/**
* Helper function for forceUpdate(). Not self-sufficient!
*/
void invalidateLayout(QLayout *layout) {
// Recompute the given layout and all its child layouts.
for (int i = 0; i < layout->count(); i++) {
QLayoutItem *item = layout->itemAt(i);
if (item->layout()) {
invalidateLayout(item->layout());
} else {
item->invalidate();
}
}
layout->invalidate();
layout->activate();
}
Try with the QWidget::sizeHint() method, which is supposed to return the size of the widget once laid out.
I had some succes in a similar problem by first calling w->layout()->update() before w->layout()->activate(). That seems to force the activate() to actually do something rather than think it is fine because the window isn't being shown anyway.
When going through QWidget::grab() I noticed this part:
if (r.width() < 0 || r.height() < 0) {
// For grabbing widgets that haven't been shown yet,
// we trigger the layouting mechanism to determine the widget's size.
r = d->prepareToRender(QRegion(), renderFlags).boundingRect();
r.setTopLeft(rectangle.topLeft());
}
QWidget::grab() has been introduced in Qt 5.0, and this test function with a QDialog containing a layout seems to work on Qt 5.5.1
int layoutTest_2(QApplication& a)
{
CLayoutTestDlg dlg; // initial dlg size can also be set in the constructor
// https://stackoverflow.com/questions/21635427
QPixmap pixmap = dlg.grab(); // must be called with default/negative-size QRect
bool savedOK = pixmap.save("E:/temp/dlg_img.png");
// saving is not necessary, but by now the layout should be done
dlg.show();
return a.exec();
}
This worked for me when using the sizeHint() plus translating the painter, however I do this inside the paint() method.
void ParentWidget::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
painter->save();
painter->translate(option.rect.topLeft());
w->render(painter);
painter->restore();
}
In this case, option.rect.topLeft() gives me the correct placement. You should try a more sensible coordinate instead of w->mapToGlobal(QPoint(0,0).