I'm loading image into QImage from data in memory, I've managed successfully paint it with QPainter onto QWidget. Then I'm trying to change some pixels of this QImage to highlight rectangle area using rubber band. My problem is that I'm getting only part of the rectangle, and sometimes nothing at all (see picture below). Code .h:
class PaintWidget : public QWidget
{
Q_OBJECT
public:
using QWidget::QWidget;
void setSourceSample(Sample src) ;
void drawRectangle(OCRRectangle rect);
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *ev) override;
void mouseMoveEvent(QMouseEvent *ev) override;
void mouseReleaseEvent(QMouseEvent *ev) override;
private:
QRectF target=QRectF(0, 0, 1000, 1000);
QImage p_source;
QPoint origin, end;
QRubberBand * rubberBand;
QPair<float, float> rsz_ratio;
void calcROI();
QPoint translateCoordinates(cv::Point point);
};
Code .cpp:
void PaintWidget::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.drawImage(target, p_source);
}
void PaintWidget::mousePressEvent(QMouseEvent *ev) {
origin=ev->pos();
rubberBand=new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void PaintWidget::mouseMoveEvent(QMouseEvent *ev) {
rubberBand->setGeometry(QRect(origin, ev->pos()).normalized());
}
void PaintWidget::mouseReleaseEvent(QMouseEvent *ev) {
end=ev->pos();
rubberBand->hide();
delete rubberBand;
calcROI();
}
void PaintWidget::calcROI() {
int p1x=min(end.x(),origin.x())*rsz_ratio.first;
int p2x=max(end.x(),origin.x())*rsz_ratio.first;
int p1y=min(end.y(), origin.y())*rsz_ratio.second;
int p2y=max(end.y(), origin.y())*rsz_ratio.second;
drawRectangle(OCRRectangle(cv::Point(p1x, p1y),cv::Point(p2x, p2y)));
}
void PaintWidget::setSourceSample(Sample src) {
QImage inter=src.toQImage();
p_source=inter.convertToFormat(QImage::Format::Format_RGB32);
rsz_ratio.first=(float)src.size().width/(float)(target.width());
rsz_ratio.second=(float)src.size().height/(float)target.height();
}
QPoint PaintWidget::translateCoordinates(cv::Point point) {
QPoint qtpoint;
qtpoint.setX(point.x);
qtpoint.setY(point.y);
return qtpoint;
}
void PaintWidget::drawRectangle(OCRRectangle rect) {
QPoint p1(translateCoordinates(rect.p1)), p2(translateCoordinates(rect.p2));
for (int i=p1.x(); i<=p2.x(); i++) {
p_source.setPixelColor(i,p1.y(),QColor(255,0,0));
p_source.setPixelColor(i, p2.y(), QColor(255,0,0));
}
for (int i=p1.y(); i<=p2.y(); i++) {
p_source.setPixelColor(p1.x(),i,QColor(255,0,0));
p_source.setPixelColor(p2.x(),i,QColor(255,0,0));
}
}
UPDATE: it seems that the problem lies within rescaling of original image. When I'm drawing filled red rectangle it goes absolutely fine, my guess is that rescaling 1px border rectangle is just erasing borders. But setting thicker borders (up to 5 pixels) doesn't provide me with solution.
Making thicker borders solved this problem.
Related
How i could create a click effect similar to push buttons on a QLabel?
QPixmap pixmap;
pixmap.load(":/files/hotkeys.png");
int w = 131;
int h = 71;
pixmap = pixmap.scaled(w, h, Qt::KeepAspectRatio);
// Label
ui.label->setGeometry(220, 220, w, h);
ui.label->setPixmap(pixmap);
// Button
QIcon icon(pixmap);
ui.toolButton->setIconSize(QSize(w, h));
ui.toolButton->setIcon(icon);
ui.toolButton->setStyleSheet("QToolButton { background-color: transparent }");
By click effect i mean like when you click on a push button containing a picture:
Ideally you'd just use a QToolButton or QPushButton, but if you must use a QLabel, you could do it by subclassing QLabel with a custom paintEvent() to give the desired effect, something like this:
class MyLabel : public QLabel
{
public:
MyLabel(const QPixmap & pm) : _isMouseDown(false) {setPixmap(pm);}
virtual void mousePressEvent( QMouseEvent * e) {_isMouseDown = true; update(); e->accept();}
virtual void mouseReleaseEvent(QMouseEvent * e) {_isMouseDown = false; update(); e->accept();}
virtual void paintEvent(QPaintEvent * e)
{
QPainter p(this);
const int offset = _isMouseDown ? 2 : 0;
p.drawPixmap(QPoint(offset, offset), *pixmap());
}
private:
bool _isMouseDown;
};
I am attempting to implement a rubberband. Here is my code.
void TOTMain::mousePressEvent(QMouseEvent * event)
{
QPoint origin = event->pos();
_selectionSquare = new QRubberBand(QRubberBand::Rectangle, this);
_selectionSquare->setGeometry(QRect(origin, QSize()));
_selectionSquare->raise();
_selectionSquare->show();
}
void TOTMain::mouseMoveEvent(QMouseEvent *event)
{
QPoint origin = event->pos();
_selectionSquare->setGeometry(QRect(origin, event->pos()).normalized());
}
void TOTMain::mouseReleaseEvent(QMouseEvent *event)
{
_selectionSquare->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
The issue is that the band is not being drawn. I confirm it is being constructed and persists through the dragging, but no rendering.
Any ideas appreciated.
Take a look at your mouseMoveEvent implementation...
void TOTMain::mouseMoveEvent(QMouseEvent *event)
{
QPoint origin = event->pos();
_selectionSquare->setGeometry(QRect(origin, event->pos()).normalized());
}
You're setting the geometry of the rubber band to QRect(origin, event->pos()), but you've previously set origin = event->pos() so the top-left and bottom-right corners of the rectangle are the same.
Make origin a member variable variable of your class (untested)...
class TOTMain: public QWidget {
public:
protected:
virtual void mousePressEvent (QMouseEvent *event) override
{
_origin = event->pos();
_selectionSquare = new QRubberBand(QRubberBand::Rectangle, this);
_selectionSquare->setGeometry(QRect(_origin, QSize()));
_selectionSquare->raise();
_selectionSquare->setStyleSheet("{ background-color : red; }");
_selectionSquare->show();
}
virtual void mouseMoveEvent (QMouseEvent *event) override
{
_selectionSquare->setGeometry(QRect(_origin, event->pos()).normalized());
}
virtual void mouseReleaseEvent (QMouseEvent *event) override
{
_selectionSquare->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
private:
QPoint _origin;
QRubberBand *_selectionSquare = nullptr;
};
There's a class inherited QQuickPaintedItem and included QwtPlot (in order to use it in QML):
class QmlQwtPlot : public QQuickPaintedItem
{
Q_OBJECT
public:
QmlQwtPlot(QQuickItem* parent = 0);
~QmlQwtPlot();
void paint(QPainter *painter) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent *event) override;
private:
QwtPlot* qwtPlot;
QwtPlotPanner* panner;
bool isDragging;
QPoint previousPosition;
QwtPlotPicker* picker;
};
For example, panning a plot was implemented by using mouse events handling and the QwtPlotPanner's signal :
void QmlQwtPlot::mouseMoveEvent(QMouseEvent* event) {
if (isDragging) { // if the mouse button has been pressed
QPoint currentPosition = event->pos();
QPoint diff = currentPosition - previousPosition;
emit panner->panned(diff.x(),diff.y());
previousPosition = event->pos();
update();
}
}
QwtPlotPicker is a Qwt library class providing selections on a plot canvas. QwtPlotPicker works well (a cursor with x,y coordinates of point and cross-line moving with cursor are appeared) with QWidget, but it doesn't work with QQuickPaintedItem because of different principles of work.
/* QWidget's child constructor (the same is in QQuickPaintedItem's one) */
PlottingWidget::PlottingWidget(QWidget *parent)
: QWidget(parent)
{
// ...
d_picker = new QwtPlotPicker(
QwtPlot::xBottom, QwtPlot::yLeft,
QwtPlotPicker::CrossRubberBand,
QwtPicker::AlwaysOn,
plot->canvas()
);
d_picker->setRubberBandPen( QColor( Qt::red ) );
d_picker->setTrackerPen( QColor( Qt::black ) );
d_picker->setStateMachine( new QwtPickerTrackerMachine() );
// ...
}
How to force QwtPlotPicker work with QQuickPaintedItem?
I have this image :
I want the border of my GraphicsViewbecome exactly like this image.
(consider the bellow image I want the top white rectangle become that shape)
I inherit a class from QGraphicsView and tried drawing this image in both drawBackground and paintEvent but none of them works.
My code :
.h file
class GraphicsTraxSuggestionView : public QGraphicsView {
Q_OBJECT
public:
GraphicsTraxSuggestionView(QWidget* widget);
protected:
// void paintEvent(QPaintEvent *event);
void drawBackground(QPainter *p, const QRectF &rect);
private:
};
.cpp file
GraphicsTraxSuggestionView::GraphicsTraxSuggestionView(QWidget* widget)
: QGraphicsView(widget)
{
//setFrameShadow(QFrame::Raised);
setFrameStyle(QFrame::NoFrame);
setStyleSheet("QGraphicsView { border-style: none; }");
}
void GraphicsTraxSuggestionView::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->drawImage(rect, QImage("suggestionBorder.png"));
}
result of my code : http://i.stack.imgur.com/r0waP.png
Any suggestion ?
1) Create a QGraphicsScene having the size of you image
2) Set it as the view's scene
3) Draw in the drawBackground as you're doing now
I tried setMask() and now it works fine .
GraphicsTraxSuggestionView::GraphicsTraxSuggestionView(QWidget* widget,QGraphicsScene* scene)
: QGraphicsView(widget),
scene_(scene)
{
setStyleSheet("background-color: transparent;");
QPixmap myPixmap = QPixmap(":/Game/Tiles//suggestionBorder.png").scaled
(scene_->sceneRect().size().width(),scene_->sceneRect().size().height());
setMask(myPixmap.mask());
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
void GraphicsTraxSuggestionView::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->drawImage(scene_->sceneRect(), QImage(":/Game/Tiles//suggestionBorder.png"));
}
result :
I tried to find some helps around internet, i had some codes to test but none works like i want.
I would draw with Qt something pixel by pixel.
I tried with a QImage within a QLabel with the protected event mousePressEvent it worked, but the pixels are too small to see them.
I tried to scale my Image, it's much better, but the position X, Y are not the same position of the pixels now or they're not synchronized with the pixels locations.
Here a screenshot of what i want do, if someone has an idea to recreate this...
If i can exactly done this via Qt i'll save a lots of time, just a basic drawing pixel by pixel, i created this example with Paint, Black and White format zoomed to 800%.
Thanks.
This is a solution hard-coded for a specific size - just implement relative coordinate mapping between pixmap and draw widget and you are set.
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent), pressed(0) {
color = Qt::black;
pixmap = new QPixmap("h:/small.png");
resize(240, 240);
}
~Widget() { if (pixmap) delete pixmap; }
protected:
void paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.drawPixmap(0, 0, pixmap->scaled(240, 240));
painter.drawPixmap(0, 0, *pixmap);
}
void mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::RightButton)
color = color == Qt::black ? Qt::white : Qt::black;
else {
pressed = 1;
draw(e);
}
}
void mouseReleaseEvent(QMouseEvent *) { pressed = 0; }
void mouseMoveEvent(QMouseEvent *e) { draw(e); }
private:
void draw(QMouseEvent *e) {
if (pressed) {
QPainter painter(pixmap);
painter.setPen(color);
int x = e->pos().x() / 12;
int y = e->pos().y() / 12;
painter.drawPoint(x, y);
repaint();
}
}
QColor color;
QPixmap *pixmap;
bool pressed;
};