QGraphicsItem scaling produces too large bounding rect - c++

Problem: When using a QGraphicsItem with the flag QGraphicsItem::ItemIgnoresTransformations, the View does not scale properly and shows unnecessary scroll bars.
To reproduce, place a QGraphicsView on a Form and use this code:
#include <QGraphicsScene>
#include <QGraphicsTextItem>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsScene* scene = new QGraphicsScene();
// Set the scene to the view. Has to be done before
// transformation in order for the problem to occur.
ui->graphicsView->setScene(scene);
// Add some text, make it transformation-invariant.
QGraphicsTextItem* txt = scene->addText("Hello World!");
txt->setFlag(QGraphicsItem::ItemIgnoresTransformations);
// Scale the scene, re-calculate the bounding rect.
ui->graphicsView->scale(10, 5);
QRectF rect = scene->itemsBoundingRect();
scene->setSceneRect(rect);
}
This also works with other Items like QGraphicsEllipseItem or QGraphicsRectItem.
Without setting the flag (simply comment out the line txt->setFlag...), the output is as expected:
However, when the flag is set, I would expect the scroll bar to disappear, because the text clearly fits into the view. But instead it looks like this:
I know that the Scene does only automatically grow, but not shrink to its content, so I am explicitly setting the Scene Rect at the end. But even this does not help.
It seems to me like this is a bug in Qt, but maybe I also simply misunderstood something. Any idea what the problem (and solution) is?
Using Qt 5.5, Ubuntu 14.04.
PS: Yes, the scene is never freed. This is of course no production code ;-)

I had a similar issue with addLine and addEllipse on top of an QImage.
The problem seems to come from the QGraphicsItem::ItemIgnoresTransformations flag that seems to enlarge the scene to hold the "transformed" object placement, but then ignores the transform for placement and size, however the damage is already done because it has incorrectly changed the scene scale.
I have a work-around that might be able to help you.
If you reset the scene scale to a known correct value after positioning the objects that ignore the transform. It will avoid your problem.
For example:
m_firstImageScene->setSceneRect(m_firstImageRect);

Related

QGraphicsScene width/height is not changing after the window was resized manually

I have an application composed from graphics view and graphics scene. The basic GUI structure is this (done in Qt Creator):
QMainWindow
QWidget (centralWidget)
QGridLayout
QVBoxLayout
QGraphicsView
This is my code for the mainwindow constructor:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
{
ui->setupUi(this);
scene = new Scene(this);
ui->graphicsView->setScene(scene);
scene->setSceneRect(-100,-100,200,200);
}
Now when I manually resize the application window, visually the scene/graphics view resizes too. It is also active in all the visible area, I can catch mouse events, add items there, although it is out of the region set by setSceneRect(). But when I call scene->width() or scene->height(), it returns 200 all the time. How can I get the size of the visible part of the scene and not the size set in setSceneRect()? By the visible part of the scene I mean the white visible area/rectangle where my items are visualized, if the item goes out of this area, it is not visible.
There's generally no need to use set the scene rectangle at all, unless the scene has very many items. Setting it is an optimization when you can calculate the bounding rectangle of the scene's contents faster than itemsBoundingRect() can, or if you know the fixed maximum size of the scene- e.g. if the scene represents some fixed-size canvas. Otherwise, you have no recourse but to leave it unset.
Anyway, binding the scene rectangle to the viewport is incorrect. The scene rectangle is used by the spatial index and must represent the contents of the scene, irrespectively of any viewports. Recall that the scene is a model usable by itself - it can have 0 or more viewports attached to it. Binding any particular viewport strongly to any of the scene's parameters is a design error.

Rotate QLabel and keep its functionality/stylesheet

For a while now I am searching for an adequate method to rotate a QLabel - and most important to keep its functionality and stylesheets.
I've found a pretty interesting approach here: Vertical QLabel, or the equivalent?
First I was satisfied because it efficiently rotated the label. Unfortunately the Stylesheet I've added (using a bigger font and other color) was completely lost and the alignment is gone as well (myLabel_->setAlignment(Qt::AlignTop); has no effect).
I've read QPainter::drawStaticText would provide more functionality but for me it doesn't work at all (I'm using the same code as in the solution before mentioned, just with the difference:
QStaticText qs = "Test1";
painter.drawStaticText(0,0,qs);
)
Using deprecated HTML ("<b>...</b>") instead of Stylesheets is also no use... Same as returning to a regular QLabel and using modern transformation stylesheets (http://snook.ca/archives/html_and_css/css-text-rotation).
I'm pretty much running out of ideas now how I am able to keep the former properties of the QLabel and still be able to rotate it...
There's no easy way to do that.
QStylePainter may help with the style:
#include<QStylePainter>
// ......
void LabelWidget::paintEvent(QPaintEvent* event) {
QStylePainter painter(this);
painter.rotate(90);
painter.drawText(0, 0, text());
}
This will draw text with properties defined by stylesheet, but this does not solve the alignment problem.
If your drawStaticText code use rotate(90) and doesn't show any thing, it is beacuse the text is rotated around the top-left point and rotate(90) will move the text out of the widget(Try rotate(45), you will find part of the text is out of the widget). A simple solution is using QPainter::translate to move to the center.
Here is my code that support alignment:
#include<QStylePainter>
// ......
void LabelWidget::paintEvent(QPaintEvent* event) {
QStylePainter painter(this);
// rotate at center
painter.translate(rect().center());
painter.rotate(90);
painter.translate(-rect().center());
painter.drawText(rect(), alignment(), text());
}
To support more features like word-wrapping:
#include<QStylePainter>
#include<QTextOption>
// ......
void LabelWidget::paintEvent(QPaintEvent* event) {
QStylePainter painter(this);
// rotate at center
painter.translate(rect().center());
painter.rotate(90);
painter.translate(-rect().center());
QTextOption textOption;
textOption.setAlignment(alignment());
if (wordWrap()) {
textOption.setWrapMode(QTextOption::WordWrap);
} else {
textOption.setWrapMode(QTextOption::NoWrap);
}
painter.drawText(rect(), text(), textOption);
}
You will have to add more features if you want other properties of the QLabel, there is no simple solution.
PS: If you want use QStaticText , make it a member variable.
The QStaticText class enables optimized drawing of text when the text
and its layout is updated rarely.
QStaticText provides a way to cache layout data for a block of text so
that it can be drawn more efficiently than by using
QPainter::drawText() in which the layout information is recalculated
with every call.
you can use QGraphicsScene
it can show a copy of anythings with customizations of size and orientation :
QLabel* myLabel_= QLabel("vertical label");
QGraphicsScene scene;
QGraphicsProxyWidget * proxy = scene.addWidget(label);
proxy->rotate(90);
QGraphicsView view(&scene);
view.show();
then you can replace view by old myLabel_ object

Change color highlight of icons in QTableView, when the cell is selected

When a cell is selected in QTableView, the icons in it are given a blue highlight, how can I control the color of this highlight or disable it?
I tried setting the QPalette::Highlight but it didn't work.
Edit:
Okay, so I do know how to change the background color and text color and color highlight, but not for an icon. If I return an icon as decoration for a cell, it is given a light blue highlight when the cell is selected. How do I remove this?
You can use style sheets to define the color of your elements. The name of the selected item in your QTableView is selection-background-color. So, changing the color of this element you will chose the background color that your prefer.
#include <QtWidgets/QApplication>
#include <QtWidgets/QTableView>
#include <QStandardItemModel>
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
QTableView *table = new QTableView();
QStandardItemModel *model = new QStandardItemModel(2,2);
table->setModel(model);
table->setStyleSheet("selection-background-color: red");
table->show();
return app.exec();
}
Look how it looks in the picture:
I discovered a way around this issue, but it has some cost associated with it.
Fundamentally, deep within Qt code it is calling onto QIcon::paint() and passing QIcon::Selected as the icon mode, so the issue is that the "selected" form of the icon's pixmap at the desired resolution is the one auto-generated by Qt.
I worked around this by setting the Selected form of the icon to be the same as the Normal mode:
// Make the "Selected" version of the icon look the same as "Normal".
for (const auto& size : icon.availableSizes())
{
icon.addPixmap(icon.pixmap(size, QIcon::Normal, QIcon::Off),
QIcon::Selected, QIcon::Off);
icon.addPixmap(icon.pixmap(size, QIcon::Normal, QIcon::On),
QIcon::Selected, QIcon::On);
}
The downside is extra time spent doing this, possibly extra memory to store it, and wasted time generating the selected icons that we're throwing away.
In my case I'm using a QStyledItemDelegate and unfortunately that doesn't give you the ability to more closely influencing how the icon is rendered without completely reimplementing how QStyle::CE_ItemViewItem is rendered in your style.
Come to think of it, if you use a proxy style it wouldn't be too hard to override how CE_ItemViewItem is rendered to not use a selected icon, so that would be an option too.
It's utterly impossible to change this behavior with the standard style in Qt. You need to implement your own specific style in order to work around this.

How to resize QWidget in a layout while aligning it to the center and maintaining aspect ratio

Ok, so I'd like to display an image with qt where the image resizes with the browser and maintains the aspect ratio while also remaining centered in the window. I can get the resizing with aspect ratio to work correctly, but when I align it with Qt::AlignCenter, the qwidget no longer resizes (remains a fixed size). So basically, I can get either option to work but not together.
A good example of what I'm trying to do would be the imshow() function in matlab. This resizes the image while maintaining the aspect ratio and also centering the image in the window. The code I have is soemthing like this:
void roilayout::resizeEvent(QResizeEvent *event)
{
QSize p(roiview->refimage->size());
p.scale(roiview->view->size(), Qt::KeepAspectRatio);
roiview->view->resize(p);
}
and in the constructor:
roiview = new roiwindow;
roiview->view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
roiview->view->setCursor(Qt::CrossCursor);
roiview->view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
roiview->view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QHBoxLayout *layoutContainer = new QHBoxLayout;
layoutContainer->addWidget(roiview->view);
setLayout(layoutContainer);
I searched google and couldnt find anything. Also asked a similar question a little while back but from the answers it appears I didn't ask the question clearly enough. Thanks.
A couple of things: First it would be helpful to know what type of control "view" is.
Also, I don't think you should need to resize the child control "view" (whatever type it is) within the parent's resizeEvent() callback.
A better solution might be to set the sizeHint policy on the child widget to automatically expand.

Viewing entire QGraphicsScene

I'm trying to write a map editor in Qt, using QGraphicsView and QGraphicsScene for both the map and tile sheets.
The problem I'm having right now is with making a good widget for importing tiles. For this, I'm using a QTabWidget (for different tile sheets), and TileWidget as the widget for each tab, which contains the QGraphicsScene and QGraphicsView.
It's working to a rough degree, but not all the tiles (or TileObjects, which are implementations of QGraphicsItem) are visible. I'm even calling view->ensureVisible(scene->sceneRect()), but still not all of the QGraphicsScene is not visible, even with scroll bars.
I understand this is due to limiting the maximum size of my QTabWidget, but that is necessary.
This happens mainly when I import a larger tile sheet.
I have a TileWidget as the QWidget for the QTabWidget, which has both the QGraphicsScene and the QGraphicsView.
TileWidget::TileWidget(QWidget *parent)
: QWidget(parent)
{
scene = new QGraphicsScene;
view = new TileView(scene, this);
connect(view, SIGNAL(newBrushSelected(TileObject *b)), this, SLOT(selectNewBrush(TileObject *b)));
}
TileView is simply a QGraphicsView re-implemented to handle mouse release events.
To add tiles, I simply call scene->addItem().
I have no other code for TileView. When I use
void TileWidget::showEvent(QShowEvent *event)
{
view->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio);
}
I get something like this.
It's okay for smaller tile sheets, but not for larger ones. What should I add to keep the size of the tiles normal, and navigate TileView using scroll bars?
Nevermind, figured it out. Just me being stupid.
You need something like:
p_myGraphicsView->fitInView(
myGraphicsView->scene()->itemsBoundingRect(),
Qt::KeepAspectRatio);