limit qwtPlotZoomer selection to canvas boundaries - c++

The default behavior of qwtPlotZoomer seems to be that you can select a rectangle which exceeds the axis scale boundaries and canvas size, as seen in this screenshot from the realtime example:
I'd like to restrict the edges of the selection to be equal to the canvas or zoomRectboundaries. Is there any easy way to do this?

You could overload the move() method and bound the position to the contentsRect() of the canvas there ( or maybe better use the pickArea - what usually is the same ).

EDIT:
Based on Uwe's advice, I overloaded the move() function as follows, which prevents the picker's rect from being drawn off-screen.
void QZoomer::move(const QPoint& pos)
{
// bound the picker selection to the zoom base so you can't zoom off screen
QPoint boundedPos = pos;
if (boundedPos.x() > plot()->canvas()->contentsRect().right()) boundedPos.setX(plot()->canvas()->contentsRect().right());
if (boundedPos.x() < plot()->canvas()->contentsRect().left()) boundedPos.setX(plot()->canvas()->contentsRect().left());
if (boundedPos.y() < plot()->canvas()->contentsRect().top()) boundedPos.setY(plot()->canvas()->contentsRect().top());
if (boundedPos.y() > plot()->canvas()->contentsRect().bottom()) boundedPos.setY(plot()->canvas()->contentsRect().bottom());
QwtPlotPicker::move(boundedPos);
}
another way is to override the zoom function and bound the rect to the zoomBase. This limits the actual zoom to the zoom base, but still draws the picker outside the lines.
void Zoomer::zoom(const QRectF& rect)
{
// bound the zooming rect to the zoomBase dimensions
QRectF boundedRect = rect & zoomBase();
QwtPlotZoomer::zoom(boundedRect);
}

Related

How to appropriately get position of QGraphicsRectItem after drag-release?

I wanted to have an online monitoring system that could tell where the shape is currently, but am getting very weird coordinates of the item, also the dimensions of it get higher by 1 each time I create new one and drag it.
Initial position (map size is 751 by 751, checked by outputting to qDebug(), scene bound to yellow space) :
Dragging it to the left top corner.
As you can see in the beginning it was on (200;200), but after dragging it is on (-201;-196). After deleting it and creating new shape on the same position with the same properties, new shape can't be seen because it is outside of the map, which suggests that edits don't show correct data.
Here is the code of updating the edits:
void CallableGraphicsRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
QGraphicsRectItem::mouseReleaseEvent(event);
ptr->updateEdits(this);
}
Here is what I managed to cut down into updateEdits():
void MainWindow::updateEdits(QAbstractGraphicsShapeItem* item)
{
//stuff not related to scene
auto posReal = item->scenePos();
auto pos = posReal.toPoint();
//create QString from coordinates
QString coordinate;
coordinate.setNum(pos.x());
ui->leftXEdit->setText(coordinate);
coordinate.setNum(pos.y());
ui->upperYEdit->setText(coordinate);
//get width and height for rect, radius for circle
auto boundingRectReal = item->sceneBoundingRect();
auto boundingRect = boundingRectReal.toRect();
ui->widthEdit->setText(QString::number(boundingRect.width()));
//disables height edit for circles, not really relevant
if (!items[currentShapeIndex].isRect)
{
ui->heightEdit->setDisabled(true);
}
else
{
ui->heightEdit->setDisabled(false);
ui->heightEdit->setText(QString::number(boundingRect.height()));
}
}
Here is how I anchor the QGraphicsScene to the left top corner of the yellow area:
scene->setSceneRect(0, 0, mapSize.width() - 20, mapSize.height() - 20);
ui->graphicsView->setScene(scene);
How can I report the right data to the edits?
You're better off overriding the itemChange method and using the ItemPositionHasChanged notification. You have to set the ItemSendsGeometryChanges flag on the item so that it receives these notifications.
I'm not sure that your item's final position has been set when you're still in the mouseReleaseEvent method. Tracking it in itemChange will ensure that the data is valid, and this kind of thing is what it's for.
Also, note that "pos" is in the item's parent coordinates, and "boundingRect" is in the item's coordinate space. You should use "scenePos" and "sceneBoundingRect" if you want to be sure you're using scene coordinates. If the item doesn't have a parent, then "pos" and "scenePos" will return the same values, but "boundingRect" and "sceneBoundingRect" will generally differ.

How to zoom in a QgraphicsView using pushbuttons?

I'm building a very simple image editor on Qt creator.I have my image displayed on a QGraphicsView and i want to give the user the ability to zoom in and out by a pushbutton.
I've searched a lot and found how to zoom in and out through the mouse wheel.As i am very new to Qt i can't adjust it to the pushbutton because i don't understand everything clearly.
I' ve tried this(without understanding completely what i'm doing)but the result isn't the wanted.It zooms in only once and quite abruptly.I want a smoother zoom and as many times as i want.
void MainWindow::on_pushButton_clicked(){
QMatrix matrix;
ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
matrix.scale(1.0,1.0);
ui->graphicsView->setMatrix(matrix);
ui->graphicsView->scale(1,-1);
}
I would be very grateful if you guys can help
Below is how I implemented zooming in my subclass of QGraphicsView. Note that you'd need to pass in different values of "zoom" to get different magnifications as the zoom factor is an absolute value, not a relative one.
(The optMousePos argument can be set to point to a QPoint indicating the spot that should be the central-point of the zoom transformation, or it can be left NULL if you don't care about that. I use it because I zoom in and out based on the user turning the wheel in his mouse, and when doing that, the user usually wants to zoom in towards the point where his mouse point is currently positioned, rather than in towards the center of the graphics area)
qreal _zoom = 0.0;
[...]
void MyQGraphWidgetSubclass :: SetZoomFactor(qreal zoom, const QPoint * optMousePos)
{
if ((zoom != _zoom)&&(zoom >= 0.02f)&&(zoom <= 1000000.0f))
{
QPointF oldPos;
if (optMousePos) oldPos = mapToScene(*optMousePos);
// Remember what point we were centered on before...
_zoom = zoom;
QMatrix m;
m.scale(_zoom, _zoom);
setMatrix(m);
if (optMousePos)
{
const QPointF newPos = mapFromScene(oldPos);
const QPointF move = newPos-*optMousePos;
horizontalScrollBar()->setValue(move.x() + horizontalScrollBar()->value());
verticalScrollBar()->setValue(move.y() + verticalScrollBar()->value());
}
}
}
void MyQGraphWidgetSubclass :: wheelEvent(QWheelEvent* event)
{
QPoint pos = event->pos();
SetZoomFactor(_zoom*pow(1.2, event->delta() / 240.0), &pos);
event->accept();
}

Getting center of specific screen in virtual desktop qt

Hey guys i need to get resolution of specific screen in virtual desktop under qt and move window around given screen. I tried
QRect screenSize = desktopWidget.availableGeometry(desktopWidget.screen(ui.monitorNumberComboBox->currentIndex()));
and now when i execute
void MyWindow::setCoordinates(int x, int y)
{
this->move((x-(this->width())/2),(y-(this->height()/2)));
//sets center of window on given coordinates
}
window->setCoordinates(screenSize.width()/2, screenSize.height()/2);
it works great but only for primary screen. Is there a possibility to use it for different screen, selected by index?
I think you need to call:
[..]
QPoint center = screenSize.center(); // Get the center of the screen rect.
window->setCoordinates(center.x(), center.y());

What is the fastest way to get QWidget pixel color under mouse?

I need to get the color of pixel under mouse, inside mouseMoveEvent of a QWidget (Breadboard). Currently I have this code->
void Breadboard::mouseMoveEvent(QMouseEvent *e)
{
QPixmap pixmap = QPixmap::grabWindow(winId());
QRgb color = pixmap.toImage().pixel(e->x(), e->y());
if (QColor(color) == terminalColor)
QMessageBox::information(this, "Ter", "minal");
}
Take a look at (scaled down) screenshot below-
When user moves his mouse on breadboard, the hole should get highlighted with some different color (like in red circle). And when the mouse exits, the previous color (grey) should be restored. So I need to do following steps-
Get color under mouse
According to color, floodfill the hole. (Different holes are distinguished using color)
On mouse out, restore the color. There would be wires going over holes, so I can't update the small rectangle (hole) only.
What is the fastest way of doing this? My attempt to extract color is not working i.e the Message box in my above code never displays. Moreover I doubt if my existing code is fast enough for my purpose. Remember, how fast you will be moving your mouse on breadboard.
Note - I was able to do this using wxWidgets framework. But due to some issues that project got stalled. And I am rewriting it using Qt now.
You are invited to look at code https://github.com/vinayak-garg/dic-sim
The "idiomatic" way of doing this in Qt is completely different from what you're describing. You'd use the Graphics View Framework for this type of thing.
Graphics View provides a surface for managing and interacting with a large number of custom-made 2D graphical items, and a view widget for visualizing the items, with support for zooming and rotation.
You'd define your own QGraphicsItem type for the "cells" in the breadboard that would react to hover enter/leave events by changing their color. The connections between the cells (wires, resistors, whatever) would also have their own graphics item types with the features you need for those.
Here's a quick and dirty example for you. It produces a 50x50 grid of green cells that become red when the mouse is over them.
#include <QtGui>
class MyRect: public QGraphicsRectItem
{
public:
MyRect(qreal x, qreal y, qreal w, qreal h)
: QGraphicsRectItem(x,y,w,h) {
setAcceptHoverEvents(true);
setBrush(Qt::green);
}
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *) {
setBrush(Qt::red);
update();
}
void hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
setBrush(Qt::green);
update();
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QGraphicsScene scene;
for (int i=0; i<50; i++)
for (int j=0; j<50; j++)
scene.addItem(new MyRect(10*i, 10*j, 8, 8));
QGraphicsView view(&scene);
view.show();
return app.exec();
}
You could modify the hover event handlers to talk to your "main window" or "controller" indicating what's currently under the mouse so you can update your caption, legend box or tool palette.
For best speed, render only the portion of the widget you're interested in into a QPaintDevice (like a QPixmap). Try something like this:
void Breadboard::mouseMoveEvent(QMouseEvent *e)
{
// Just 1 pixel.
QPixmap pixmap(1, 1);
// Target coordinates inside the pixmap where drawing should start.
QPoint targetPos(0, 0);
// Source area inside the widget that should be rendered.
QRegion sourceArea( /* use appropriate coordinates from the mouse event */ );
// Render it.
this->render(&pixmap, targetPos, sourceArea, /* look into what flags you need */);
// Do whatever else you need to extract the color from the 1 pixel pixmap.
}
Mat's answer is better if you're willing to refactor your application to use the graphics view API.

QGraphicsView ensureVisible() and centerOn()

I am going to do pan/scale stuff on QGraphicsView.
So I read the documentation of QGraphicsView and see some utility functions like ensureVisible() and centerOn().
I think I understand what the documentation says but I can' t manage to write a working example.
Could you please write/suggest me an example code to understand the issue.
Ton pan the view by a certain amount (for example in your view's mouseMoveEvent()), assuming MyView is a subclass of QGraphicsView (all the following code was ported from Python, I didn't test it):
void MyView::moveBy(QPoint &delta)
{
QScrollBar *horiz_scroll = horizontalScrollBar();
QScrollBar *vert_scroll = verticalScrollBar();
horiz_scroll->setValue(horiz_scroll.value() - delta.x());
vert_scroll->setValue(vert_scroll.value() - delta.y());
}
To fit a rectangle specified in scene coordinates by zooming and panning:
void MyView::fit(QRectF &rect)
{
setSceneRect(rect);
fitInView(rect, Qt::KeepAspectRatio);
}
Note that if your scene contains non transformable items (with the QGraphicsItem::ItemIgnoresTransformations flag set), you'll have to take extra steps to compute their correct bounding box:
/**
* Compute the bounding box of an item in scene space, handling non
* transformable items.
*/
QRectF sceneBbox(QGraphicsItem *item, QGraphicsItemView *view=NULL)
{
QRectF bbox = item->boundingRect();
QTransform vp_trans, item_to_vp_trans;
if (!(item->flags() & QGraphicsItem::ItemIgnoresTransformations)) {
// Normal item, simply map its bounding box to scene space
bbox = item->mapRectToScene(bbox);
} else {
// Item with the ItemIgnoresTransformations flag, need to compute its
// bounding box with deviceTransform()
if (view) {
vp_trans = view->viewportTransform();
} else {
vp_trans = QTransform();
}
item_to_vp_trans = item->deviceTransform(vp_trans);
// Map bbox to viewport space
bbox = item_to_vp_trans.mapRect(bbox);
// Map bbox back to scene space
bbox = vp_trans.inverted().mapRect(bbox);
}
return bbox;
}
In that case the bounding rect of your objects becomes dependent on the view's zoom level, meaning that sometimes MyView::fit() won't fit exactly your objects (for example when fitting a selection of objects from a largely zoomed out view). A quick and dirty solution is to call MyView::fit() repeatedly until the bounding rect naturally "stabilizes" itself.