I would like to do manipulations on the image with OpenCV based on mouseClicks.
I am using QLabel to display cv::Mat images. Now my problem is with getting the mouse clicks positions with respect to the image. So, I would like (0,0) at topleft corner of the image.
Following is my mousePressEvent, but these are not correct co-ordinates.
void MainWindow::mousePressEvent( QMouseEvent* ev )
{
//This seems to work thanks to Pavel
QPoint P = ui->label->mapFrom(this, ev->pos())
//if( ui->label->underMouse() )
{
QMessageBox msgBox;
//m
sgBox.setText(QString("Click Detected X=")+QString::number(mFirstX)+QString(" Y=")+QString::number(mFirstY));
msgBox.setText("x ="+QString::number(P.x()) + " y= " + QString::number(P.y()));
msgBox.exec();
}
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
///////
*/// This seem to still give wrong position, these values do not match to those I get when I /// click
///////
const QPoint P = ui->label->mapFrom(this, mouseEvent->pos());
//statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mo
useEvent->pos().y()));
statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(P.x()).arg(P.y()));
}
return false;
}*
Please help.
You need to set QLabel's alignment to Qt::AlignTop | Qt::AlignLeft and ensure that its scaledContents property is false. You should use ui->label->mapFrom(this, ev->pos()) to convert MainWindow coordinates to label coordinates.
Related
I am trying to test a MouseEvent for a QGraphicsScene, but for some reason, the area at which the program registers the click is offset. Here is my code:
//var declaration
editorScene = new QGraphicsScene(this);
ui->spriteGraphicsEditor->setScene(editorScene);
...
//registers a click on the graphics editor
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
// Detect if the click is in the view.
QPoint remapped = ui->spriteGraphicsEditor->mapFromParent( event->pos() );
if ( ui->spriteGraphicsEditor->rect().contains( remapped ) )
{
QPointF mousePoint = ui->spriteGraphicsEditor->mapToScene( remapped );
qDebug() << mousePoint;
}
}
}
And here is a screenshot of the program(it is still a rough work-in-progress)
The blue box shows the approximate area you can click on
It appears that the gray bar on the top is causing the error, as if the area is being defined before the GraphicsScene is being put into place. Why might this happen?
I want to prevent zooming of a QChartView into negative x or y axis values. So I'm trying to intercept the mouseReleaseEvent in my subclass of QChartView and then modify the coordinates of the rubberBandRect before performing the zoom. But it seems the rubberBandRect coordinates are always zero after mouse release, so I think I'm not working with the correct rubberBandRect.
I'm trying to adapt based on the documentation for QChartView (emphasis added):
void QChartView::mouseReleaseEvent(QMouseEvent *event)
If the left mouse button is released and the rubber band is enabled, the event event is accepted and the view is zoomed into the rectangle specified by the rubber band.
When I tried looked at the actual values in the rubberBandRect in the code snippet below, the x/y and height/width values of the rectangle are always zero no matter how I zoomed with the cursor.
I also looked at the answer to this question: Qt How to connect the rubberBandChanged signal but in that case, they wanted the behavior in the main window, which is not what I want. Thanks!
class MyQChartView : public QtCharts::QChartView
{
Q_OBJECT
public:
MyQChartView(QWidget *parent = 0);
//...
protected:
void mouseReleaseEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
};
void MyQChartView::mouseReleaseEvent(QMouseEvent *event)
{
//always shows zeroes for the x and y position
if(event->button() == Qt::LeftButton){
std::cout << "x: " << this->rubberBandRect().x() << " y: " << this->rubberBandRect().y() << std::endl;
QChartView::mouseReleaseEvent(event);
}
//any other event
QChartView::mouseReleaseEvent(event);
}
After extensive experiments, I think I found the best workaround. It was necessary to make a rubberBand pointer and then findChild to get to the actual rubberband. Then after mouse release, I get the plot area coordinates as well as the rubberband coordinates. Then I made a bounding box (bounded_geometry) and limited its values if the rubberband went out of bounds. Then I zoomIn to the bounded geometry box. It won't work to just modify the rubberband because those coordinates are not floating point and rounding errors cause wrong zooming.
class MyQChartView : public QtCharts::QChartView
{
Q_OBJECT
public:
MyQChartView(QWidget *parent = 0):
QChartView(parent)
{
myChart = new QtCharts::QChart();
setRubberBand(QtCharts::QChartView::RectangleRubberBand);
rubberBandPtr = this->findChild<QRubberBand *>();
this->setChart(myChart);
//...other things
}
//...
protected:
void mouseReleaseEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
private:
QRubberBand * rubberBandPtr;
QtCharts::QChart * myChart;
};
void MyQChartView::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
//first get the plot area and save the needed values (could change each time)
QRectF plotArea = myChart->plotArea();
qreal xbound = plotArea.x(); //values < xbound are out of bounds on x axis
qreal ybound = plotArea.y() + plotArea.height(); // !!! note that values > ybound are out of bounds (i.e. negative y values)
QPointF rb_tl = rubberBandPtr->geometry().topLeft();
QPointF rb_br = rubberBandPtr->geometry().bottomRight();
QRectF bounded_geometry = rubberBandPtr->geometry();
if(rb_tl.x() < xbound){
bounded_geometry.setX(xbound);
}
if(rb_br.y() > ybound){ //note that values > ybound are out of bounds
bounded_geometry.setBottom(ybound);
}
myChart->zoomIn(bounded_geometry);
rubberBandPtr->close();
return;
}
//any other event
QChartView::mouseReleaseEvent(event);
}
I am building a major user interface but I am stuck on a problem and in order to shrink the problem I build a small working example that carries exactly the problem.
After the user creates a new .db file using the icon and saving for example to Desktop it is possible to load images(only in .png format for now) in the QGraphicsView and cliking on the checkbox to enable from Right: Drag to Right:Select it is possible to draw boxes on the image. With a right click inside the drawn box we can open a QDialog that asks to name the extracted image from the box. The image is stored in the QTableView as shown in the small working example below:
The problem:
I tried several ways to erase the box (and the related index on the QTableView of the related box) but none of them was successful.
With the use of the "Erase Square" QPushButton I am trying to erase the box. Every box can be re-activated by just left double-clicking within the interested box. See below what I am trying to achieve:
I draw the box on the QGraphicsView and right mouse click to capture the image from the box:
void MainWindow::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::RightButton)
{
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(ShowContextMenu(const QPoint &)));
}
}
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
Q_UNUSED(event);
leftScene->clearSelection(); // Selections would also render to the file
leftScene->setSceneRect(leftScene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
QImage image(leftScene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
image.fill(Qt::transparent); // Start all pixels transparent
QPainter painter(&image);
leftScene->render(&painter);
QImage subimage = image.copy(QRect(start.toPoint(), end.toPoint()));
clipSceneDialog d(this);
d.show();
d.setImage(subimage);
d.setHeaderImage(currentLeftImagePath);
d.setBoundingBox(QRect(start.toPoint(), end.toPoint()));
if(d.exec() == QDialog::Rejected) {
return;
} else {
//
}
Param result = d.getData();
Parameters* param = new Parameters(result);
mDatabaseLeftCamera->addItem(param);
mModelLeftCamera->select();
ui->tableView->show();
}
Draw as many boxes as the user wants:
void MainWindow::onRubberBandUpdate(const QRect &viewportRect,
const QPointF &fromScenePoint,
const QPointF &toScenePoint)
{
if(viewportRect.isNull() && fromScenePoint.isNull() && toScenePoint.isNull() && imageLoaded)
{
if(currentSelection >= 0)
selections[currentSelection]->setActiveState(false);
QRectF select;
select.setCoords(start.x(), start.y(), end.x(), end.y());
square = new Square(select);
square->setActiveState(true);
currentSelection = selections.size();
selections.append(square);
leftScene->addItem(square->getGraphics());
ui->graphicsView->show();
}
else
{
start = fromScenePoint;
end = toScenePoint;
}
}
After drawing several boxes the user decides to reactivate one of the boxes by double-cliking within the box:
void MainWindow::onSceneDoubleClick(QPointF point)
{
qDebug() << "Click!";
QList<QGraphicsItem*> foundItems = leftScene->items(point);
if(foundItems.size() > 0 && foundItems[0]->group() != nullptr)
{
qDebug() << "Found";
int i = 0;
for(i=0;i<selections.size();i++)
{
qDebug() << "Iterate";
if(selections[i]->getGraphics() == foundItems[0]->group())
{
qDebug() << "Target";
break;
}
}
if(currentSelection >= 0)
selections[currentSelection]->setActiveState(false);
currentSelection = i;
selections[currentSelection]->setActiveState(true);
}
}
The user decides to erase the re-activated square:
void MainWindow::on_eraseSquare_clicked()
{
clearSceneLeft();
}
void MainWindow::clearSceneLeft()
{
if (selections.size() > 0) {
qDeleteAll(selections);
selections.clear();
currentSelection = -1;
}
for(int p=0;p<shape.size();p++)
{
leftScene->removeItem(shape[p]);
delete shape[p];
}
shape.clear();
}
But this is not working and nothing happens. Additionally because the box corresponds to an SQL reference on the QTableView I dont know how that could be connected in the SQL class.
I have been trying for many days to solve this problem and am running out of ideas. Thanks for shedding light on this problem or point in the right direction.
If you would like to try the small example interface you can do it from here
I am a newbie for Qt.
The question is that: After turning an image into the QGraphicsView, I use a qrubberband to select the cropping area of the image. It is successful to select the croppping region at the moment.But I don't know how to save that cropping region into a jpg/bmp afterwards. Note that I made an ui component for the GraphicsView called CGraphicsView.
void CGraphicsView::mousePressEvent
( QMouseEvent* event)
{
mypoint = event->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);//new rectangle band
rubberBand->setGeometry(QRect(mypoint, QSize()));
rubberBand->show();
}
void CGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
if (rubberBand)
{
rubberBand->setGeometry(QRect(mypoint, event->pos()).normalized());//Area Bounding
}
}
void CGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
if (rubberBand)
{
QRect myRect(mypoint, event->pos());
rubberBand->hide();// hide on mouse Release
QImage copyImage; //<= this Qimage hold nothing
copyImage = copyImage.copy(myRect);
}
}
There is special method in Qt. It allows get screenshot of view.
QString fileName = "path";
QPixmap pixMap = QPixmap::grabWidget(graphicsView, rectRegion);
pixMap.save(fileName);
Save() method can save picture in different formats and with compression.
Also with grabWidget() method you can grab another widgets too. Moreover, this method take QRect as argument so you can create screenshot of region what you need.
You can save a part of your scene to an image like :
QPixmap pixmap=QPixmap(myRect.size());
QString filename = QFileDialog::getSaveFileName( this->parentWidget(), tr("Save As"), tr("image.png"));
if( !filename.isEmpty() )
{
QPainter painter( &pixmap );
painter.setRenderHint(QPainter::Antialiasing);
scene->render( &painter, pixmap.rect(),myRect, Qt::KeepAspectRatio );
painter.end();
pixmap.save(filename,"PNG");
}
I'm attempting to produce a widget that consists of a text display that can be resized by the user grabbing the lower right corner. So far I've been able to generate this:
I've applied a red background to the layout to make it more obvious what's going on. I've used the following code to generate this:
m_sizeGrip = new QSizeGrip( this );
m_layout = new QHBoxLayout( this );
m_label = new QLabel( this );
m_layout->setContentsMargins( QMargins() );
m_layout->setSpacing( 0 );
m_layout->addWidget( m_label );
m_layout->addWidget( m_sizeGrip, 0, Qt::AlignBottom | Qt::AlignRight );
setWindowFlags( Qt::SubWindow );
Basically, it's a horizontal layout with the label and grip added to it, which is then installed on a QWidget. My problem is that I'd like the grip to be on the lower right corner of the label, rather than the parent widget. I'd also like to make it invisible while keeping it enabled.
Or perhaps I'm going about this the wrong way. My ultimate goal is to have a textual display widget that can be resized by the user either horizontally or vertically, but doesn't have a visible grip that would obscure the text. Am I already on the right track with the code above, or is there a better way to achieve this?
You could create a custom QLabel for that. The idea would be to track mouse move events (which by default only fire when a mouse button is pressed), and resize based on how much the mouse traveled since the last event.
This allows you to control exactly how to display the "gripper" (if at all) and what shape it should have. You can constrain the resizing to vertical or horizontal (or not).
Here's a demo of how you could do that (resizes both ways). Warning: this might wreak havoc in your layout.
#include <QtGui>
class GripLabel: public QLabel
{
Q_OBJECT
public:
GripLabel(QString const& title, QWidget* parent = 0)
: QLabel(title, parent),
resizing(false),
gripSize(10, 10)
{
// Prevent the widget from disappearing altogether
// Bare minimum would be gripSize
setMinimumSize(100, 30);
}
QSize sizeHint() const
{
return minimumSize();
}
protected:
bool mouseInGrip(QPoint mousePos)
{
// "handle" is in the lower right hand corner
return ((mousePos.x() > (width() - gripSize.width()))
&& (mousePos.y() > (height() - gripSize.height())));
}
void mousePressEvent(QMouseEvent *e)
{
// Check if we hit the grip handle
if (mouseInGrip(e->pos())) {
oldPos = e->pos();
resizing = true;
} else {
resizing = false;
}
}
void mouseMoveEvent(QMouseEvent *e)
{
if (resizing) {
// adapt the widget size based on mouse movement
QPoint delta = e->pos() - oldPos;
oldPos = e->pos();
setMinimumSize(width()+delta.x(), height()+delta.y());
updateGeometry();
}
}
void paintEvent(QPaintEvent *e)
{
QLabel::paintEvent(e);
QPainter p(this);
p.setPen(Qt::red);
p.drawRect(width()-gripSize.width(), height()-gripSize.height(),
gripSize.width(), gripSize.height());
}
private:
bool resizing;
QSize gripSize;
QPoint oldPos;
};
Sample main:
#include "griplabel.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QWidget *w = new QWidget;
QPushButton *b = new QPushButton("button");
GripLabel *l = new GripLabel("Hello");
QHBoxLayout *y = new QHBoxLayout;
y->addWidget(b);
y->addWidget(l);
y->setSizeConstraint(QLayout::SetFixedSize);
w->setLayout(y);
w->show();
return app.exec();
}