[SOLVED]How to make a QLabel follow mouse movements properly.
-
I have created a number of components as part of a double image viewer idea I had.
I initialise those components on my main classm_workspaced1 = new DetailViewport; m_interactivepointer1 = new InteractivePointer; m_interactivepointer2 = new InteractivePointer; m_workspace1 = new Workspace; m_workspace2 = new Workspace;
and I add them to my mainLayout
m_mainLayout->addWidget(m_workspace1); m_mainLayout->addWidget(m_workspace2); ...
So they get displayed as I expect them to.
The whole problem is with my interactive pointers. The idea behind them is that instead of having a mouse pointer while hovering above an image, you will see a small label displaying a zoomed version (or some other data) of that image. This means that
InteractivePointer
is a subclass ofQLabel
, that contains an update function and initialises its displaym_imaged = new QImage(8, 8, QImage::Format_ARGB32); m_pixmap = QPixmap::fromImage(*m_imaged);
The update function has also a command like this
this->move(posX, posY);
for moving the label in a new position.
The thing is that if I do not add my interactive pointers to the main layout they appear on their own separate window, and if I do add them then they do not appear at all.
Question:
So basically what I want to know is a good way to make a label appear on my app and follow the mouse movement. -
UPDATE
I managed to get the labels in my scene by using Proxy Widgets.
So in a few words what I did was have two
QGraphicsProxyWidget
items, namelym_labelProxy1
, andm_labelProxy2
that I then used in order to display my interactive pointers (aka QLabels) on both workspaces.m_labelProxy1 = m_scene1->addWidget(m_interactivepointer1); m_labelProxy2 = m_scene2->addWidget(m_interactivepointer2);
This caused a few problems though. Before I do this, when running, the app would display both images centred and fully visible.
But now it just displays them transposed, with only parts of them visible. Moreover if I move the mouse (and the QLabel that moves with it) towards the edges of the GraphicsView the Image starts moving downwards as if I am scrolling. Hopefully this images gives a rough idea.
I do want the interactive pointers visible, but I also don't want them to affect my scene. Is there something I can do to achieve that.
-
UPDATE 2:
The above issue was solved by initialising the interactive pointers somewhere within the image, that is already displayed at this point. I then do not allow them to move anywhere outside the image area.
Yet another problem came to my attention. If I try to drag around the image whilst the interactive pointer is visible, it will leave various marks behind
Those marks will eventually disappear if I minimise the app, or scroll that area somewhere outside the visible viewport. It still looks bad though, and calling
repaint()
on myQGraphicsView
will not help.Only calling
update()
to the scene held by thatQGraphicsView
solves the issue. And I know this thread has started spanning too far from its title, but I would be interested to know why that happens. -
Hi,
Just to be sure I understand you correctly: is that label moving along with the mouse ?
-
Yes.
If you take a close look at the image, you will notice a greyish rectangle. Just imagine that instead of a pointer you have that, and that it is able to display various data as you move around your mouse (e.g. a zoomed version of the pixels below it). -
Shouldn't you rather have a secondary QGraphicsView that would show the same scene but zoomed in ?
On a side note:
m_imaged = new QImage(8, 8, QImage::Format_ARGB32);
In most of the cases, there's no need for a QImage allocated on the heap -
I am not sure I understand you correctly. Because then my second
QGraphicsView
should be on top of the first (the one displaying the image) and transparent.Also on your side note, I don't think I can possibly do anything else. The InteractivePointer class is rather simple, but if I change the m_image initialisation on the constructor then the pointer will display nothing but the background.
#include "interactivepointer.h" InteractivePointer::InteractivePointer() { this->setScaledContents(true); this->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); this->setFixedWidth(pointerSizeX()); this->setFixedHeight(pointerSizeY()); //this->setTextFormat(Qt::RichText); this->setMargin( 0 ); this->setContentsMargins(0, 0, 0, 0 ); this->setAlignment(Qt::AlignTop); // Initialise an image. m_imagep = new QImage(8, 8, QImage::Format_ARGB32); m_pixmap = QPixmap::fromImage(*m_imagep); // Set pixelmap to this label. this->setPixmap(m_pixmap); // Set font size. m_labelFont.setPointSize(fontSize()); // Font metrics in order to adjust pointer size QFontMetrics fm(m_labelFont); m_pixelsWideT = fm.width("000"); m_pixelsWideS = fm.width(" "); m_pixelsHighT = fm.height(); m_pixelsHighS = fm.lineSpacing(); } void InteractivePointer::update(int posX, int posY, QVector<int> val1, QVector<int> val2, int winSize, int mode) { // Also update current position. updatePosition(posX, posY); if (mode == 0) // Zoom view { setPointerSizeX(40); setPointerSizeY(40); this->setFixedWidth(pointerSizeX()); this->setFixedHeight(pointerSizeY()); zoomView(val1, winSize); } else if (mode == 1) // Difference view { setPointerSizeX(40); setPointerSizeY(40); this->setFixedWidth(pointerSizeX()); this->setFixedHeight(pointerSizeY()); diffView(val1, val2, winSize); } else // Text View { setPointerSizeX(winSize * (m_pixelsWideT + m_pixelsWideS)); // Adjust pointerX size; setPointerSizeY(winSize * (m_pixelsHighS)); // Adjust pointerY size; this->setFixedWidth(pointerSizeX()); this->setFixedHeight(pointerSizeY()); this->setLineWidth(pointerSizeY()); textView(val1, winSize); } } void InteractivePointer::updatePosition(int posX, int posY) { // Move pointer to its new position. this->move((posX - this->width() / 2), (posY - this->height() / 2)); } void InteractivePointer::setPointerSizeX(const float &pointerSizeX_) // Set pointer width. { m_pointerSizeX = pointerSizeX_; } float InteractivePointer::pointerSizeX() const // Get pointer width. { return m_pointerSizeX; } void InteractivePointer::setPointerSizeY(const float &pointerSizeY_) // Set pointer height. { m_pointerSizeY = pointerSizeY_; } float InteractivePointer::pointerSizeY() const // Get pointer height. { return m_pointerSizeY; } void InteractivePointer::setFontSize(const int &fontSize_) // Set font size. { m_fontSize = fontSize_; } int InteractivePointer::fontSize() const // Get font size. { return m_fontSize; } void InteractivePointer::zoomView(QVector<int> val1, int winSize) { // Resize image. m_imagep = new QImage(winSize, winSize, QImage::Format_ARGB32); // Update current pixels. int counter = 0; for (int i = 0; i < winSize; i++) { for (int j = 0; j < winSize; j++) { m_imagep->setPixel(i, j, qRgb(val1[counter + 0], val1[counter + 1], val1[counter + 2])); counter = counter + 3; } } // Create pixel map. m_pixmap = QPixmap::fromImage(*m_imagep); // Set pixel map to this label. this->setPixmap(m_pixmap); delete m_imagep; } void InteractivePointer::diffView(QVector<int> val1, QVector<int> val2, int winSize) { // Resize image. m_imagep = new QImage(winSize, winSize, QImage::Format_ARGB32); int diff1; int diff2; int diff3; // Update current pixels. int counter = 0; for (int i = 0; i < winSize; i++) { for (int j = 0; j < winSize; j++) { diff1 = val1[counter + 0] - val2[counter + 0]; if (diff1 < 0 || diff1 > 255) diff1 = diff1 - 256; diff2 = val1[counter + 1] - val2[counter + 1]; if (diff2 < 0 || diff2 > 255) diff2 = diff2 - 256; diff3 = val1[counter + 2] - val1[counter + 2]; if (diff3 < 0 || diff3 > 255) diff3 = diff3 - 256; m_imagep->setPixel(i, j, qRgb(diff1, diff2, diff3)); counter = counter + 3; } } // Create pixel map. m_pixmap = QPixmap::fromImage(*m_imagep); // Set pixel map to this label. this->setPixmap(m_pixmap); delete m_imagep; } void InteractivePointer::textView(QVector<int> val1, int winSize) { this->clear(); int grayColor = 0; /*double stepX = this->width() / winSize; double stepY = this->height() / winSize; int mid = winSize / 2; double curX = 0; double curY = 0;*/ int counter = 0; for (int i = 0; i < winSize; i++) { for (int j = 0; j < winSize; j++) { counter = (j * winSize + i) * 3; /*curX = curX + stepX*i; curY = curY + stepY*j; QRect rec(curX-stepX, curY-stepY, curX, curY);*/ grayColor = 0.21 * val1[counter + 0] + 0.72 * val1[counter + 1] + 0.07 * val1[counter + 2]; QString tempVal = "---"; if (grayColor / 10 < 1) tempVal = QString("00" + QString::number(grayColor)); else if (grayColor / 100 < 1) tempVal = QString("0" + QString::number(grayColor)); else tempVal = QString::number(grayColor); this->setFont(m_labelFont); this->setText(this->text() + " " + tempVal); //counter = counter + 3; // This will rotate the image by 90 deg. } if (i < winSize - 1) this->setText(this->text() + "\n"); } }
-
Why transparent since you want a zoomed view of your image ?
You have a memory leak with m_imagep. You never never delete it. And your use of it is basically:
- m_imagep = new QImage(parameters);
- modify m_image content
- m_pixmap = QPixmap::fromImage(*m_imagep);
This:
- m_image = QImage(parameters);
- modify m_image content
- m_pixmap = QPixmap::fromImage(m_image);;
will do the same without memory leak.
-
@SGaist, I think he wants this mechanism to be able to display any arbitrary data, and just mentioned a smaller version of the image as an example of something that might be displayed.
"you will see a small label displaying a zoomed version (or some other data)"
-
Just to help paint a better picture of the main concept please have a look at this.
Close to the helmet you can see a blurry rectangle. That is what I have been calling an interactive pointer and is displayed instead of the normal mouse arrow when you hover above the image. I paint it inside my scene as a proxy widget. That is why I said that I did not understand the suggestion of using another QGraphicsView.
Also SGais I am applying your suggestion, but I thought that by adding
delete m_image
I could get rid of the leak, if you notice its my very last command on zoom and diffView functions. -
I managed to miss the delete, the code scrolling isn't always working well…
I just thought of something, why not use a QGraphicsPixmapItem ?
-
PixmapItem
indeed seems to have been created for that exact purpose, whereasProxyWidget
is more useful (as the name implies) for embedding a widget inside thescene
. One drawback though, I did not knew about its existence while implementing the interactive pointer. Plus it will take me a while to implement it because I cannot directly taketextPointer
and use it as is in a PixmapItem. I definitely want to implement it later on though, probably as soon as I finish with undo/redo functionality.I am glad you did not see the delete statement because that way I learned something new. Not to mention that the code is more simple this way.