Fast drawing on a qDeclarativeItem - c++

Let's say we have 2 qDeclarativeItem's laying on top of each other. Bottom item contains a background image (most of the time remaining unchanged). Top item contains a bunch of simple items (lines, arcs...) that can be directly edited by using a mouse.
Problem:
When painting on top layer, buttom layer is fully repainted too. Considering that I'm having a large image there, repainting it is very slow.
As an example of above said, here is some code.
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QmlApplicationViewer viewer;
qmlRegisterType<BottomLayer>("Layer", 1, 0, "BottomLayer");
qmlRegisterType<UpperLayer>("Layer", 1, 0, "UpperLayer");
viewer.setMainQmlFile(QLatin1String("qml/main.qml"));
viewer.setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
viewer.showExpanded();
return app.exec();
}
Background layer (painting the background image):
BottomLayer::BottomLayer(QDeclarativeItem *parent) : QDeclarativeItem(parent)
{
setFlag(QGraphicsItem::ItemHasNoContents, false);
image.load( "../img.png");
}
void BottomLayer::paint(QPainter* painter,const QStyleOptionGraphicsItem* option, QWidget* widget)
{
painter->drawImage( QRectF(0, 0, 1920, 1080), image );
}
Foreground layer (drawing lines):
UpperLayer::UpperLayer(QDeclarativeItem *parent) : QDeclarativeItem(parent)
{
setFlag(QGraphicsItem::ItemHasNoContents, false);
}
void UpperLayer::mousePosCanvasChanged(QPoint pos)
{
p2 = pos;
if(drawing)
update();
}
void UpperLayer::mouseDownCanvasChanged(QPoint pos)
{
p1 = pos;
drawing = true;
}
void UpperLayer::mouseUpCanvasChanged(QPoint pos)
{
drawing = false;
}
void UpperLayer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
QPen pen(Qt::red, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
painter->setPen(pen);
painter->drawLine(p1, p2);
}
QML code
Rectangle {
width: 1920
height: 1080
color: "transparent"
MouseArea {
anchors.fill: parent
onMousePositionChanged: upper_layer.mousePosCanvasChanged(Qt.point(mouseX,mouseY));
onPressed: upper_layer.mouseDownCanvasChanged(Qt.point(mouseX,mouseY))
onReleased: upper_layer.mouseUpCanvasChanged(Qt.point(mouseX,mouseY))
}
BottomLayer{
anchors.fill: parent
}
UpperLayer {
id: upper_layer
anchors.fill: parent
}
}
What did I try:
I tried painting everything right on the screen with viewer.setAttribute(Qt::WA_PaintOnScreen, true), so I can avoid buffering overhead. This gives me the desired frame rate, but everything becomes flickery.
I thought about using the background image as a buffer and do the painting right on it. Considering that sometimes I have to clean after myself (ex. movind the item on the screen), this approach becomes too complex and unjustified.
I tried doing it with Graphics View Framework, so I can limit the repaint area to foreground item's clip rectangle. This however does not work as desired. If f.ex. I have a line going from top-left to bottom-right corner, the clipRectangle covers the whole image (everything is slow again).
I tried calculating the clipRectangle for every foreground item and passing it to update(QRect) and update(QRegion). This gives me the same performance as GraphicsViewFramework, but, now I can divide my items in several rectangles, repaint each separately and get an even smaller repaint area. If I go further with this approach, I can update every item pixel-by-pixel and avoid background repainting at all. However, I have a feeling that I'm doing something wrong. If it is possible to do it this way, isn't there something in Qt that can do everything for me?
P.S. If you have some other ideas that I can try, I'm interested to hear (read) them.

If the image is below all items and doesn't need to be moved, you could probably draw it in QGraphicsView'::drawBackground()

Related

Make QGraphicsVideoItem Fill QWidget

My goal is to create a simple video player QWidget that allows for some overlayed graphics (think subtitles or similar).
I started with the naive approach, which was to use QVideoWidget with another set of QWidget on top (my overlays). Unfortunately this does not work because Qt does not allow widgets with transparent background to render on top of video. The background shows as black instead of the actual video.
Next idea is to use QGraphicsScene et al. which is supposed to allow this kind of compositing, so I create a dead simple setup like this:
// The widget we will use as viewport
auto viewport= new QWidget();
//Set an easily recognizable bg color for debugging
palette.setColor(QPalette::Window, Qt::green);
viewport->setPalette(palette);
viewport->setAutoFillBackground(true);
// Our scene
auto mScene=new QGraphicsScene(this);
// The video
auto mVideoItem = new QGraphicsVideoItem();
mVideoItem->setPos(0,0);
myVideoSource.setVideoOutput(mVideoItem); // ... not shown: setting up of the video source
mScene->addItem(mVideoItem);
// Yellow line starting at 0,0 for debugging
auto line=new QGraphicsLineItem (0,0,100,100);
line->setPos(0,0);
line->setPen(QPen(Qt::yellow, 2));
mScene->addItem(line);
// A Text string
auto text=new QGraphicsTextItem("Its Wednesday my dudes", mVideoItem);
text->setPos(10, 10);
// Our view
mView=new QGraphicsView;
mView->setScene(mScene);
mView->setViewport(viewport);
viewport->show()
Now this looks promising because I can see compositing works; the line and text render flawlessly on top of the video. However the video is positioned in a seemingly random place in the widget. (see screensdhot)
At this point I have tried every conceivable and inconceivable combination of
mVideoItem->setSize();
mVideoItem->setOffset();
mScene->setSceneRect();
mView->fitInView();
mView->ensureVisible();
mView->centerOn()
Trying to fill the viewport widget with the video item but nothing seems logical at all. Instead of centering the content, it seems to fly around the screen in logic defying ways and I have given up. I put my code in the viewport widget's resizeEvent and use the viewport widget's size() as the base.
So my question is; How can I fill viewport widget with video item on resize?
I dont think QGraphicsVideoItem is good for this task.
You can implement QAbstractVideoSurface that receives QVideoFrames and feeds them to QWidget that converts them to QImage, scales and draws them in paintEvent. Since you control paintEvent you can draw anything over your video, and get "fill viewport" feature for free.
Gist:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Surface surface;
Widget widget(surface);
widget.show();
QMediaPlayer player;
player.setVideoOutput(&surface);
player.setMedia(QMediaContent(QUrl("path/to/media")));
player.play();
return a.exec();
}
bool Surface::present(const QVideoFrame &frame)
{
mFrame = frame;
emit frameReceived();
return true;
}
Widget::Widget(Surface &surface, QWidget *parent)
: QWidget{parent}, mSurface(surface)
{
connect(&mSurface,SIGNAL(frameReceived()),this,SLOT(update()));
}
void Widget::paintEvent(QPaintEvent *event)
{
QVideoFrame frame = mSurface.frame();
if (frame.map(QAbstractVideoBuffer::ReadOnly)) {
QPainter painter(this);
int imageWidth = mSurface.imageSize().width();
int imageHeight = mSurface.imageSize().height();
auto image = QImage(frame.bits(),
imageWidth,
imageHeight,
mSurface.imageFormat());
double scale1 = (double) width() / imageWidth;
double scale2 = (double) height() / imageHeight;
double scale = std::min(scale1, scale2);
QTransform transform;
transform.translate(width() / 2.0, height() / 2.0);
transform.scale(scale, scale);
transform.translate(-imageWidth / 2.0, -imageHeight / 2.0);
painter.setTransform(transform);
painter.drawImage(QPoint(0,0), image);
painter.setTransform(QTransform());
painter.setFont(QFont("Arial", 20));
int fontHeight = painter.fontMetrics().height();
int ypos = height() - (height() - imageHeight * scale) / 2 - fontHeight;
QRectF textRect(QPoint(0, ypos), QSize(width(), fontHeight));
QTextOption opt(Qt::AlignCenter);
painter.setPen(Qt::blue);
painter.drawText(textRect, "Subtitles sample", opt);
frame.unmap();
}
}
Full source: draw-over-video
Based on customvideosurface example from Qt.

Translate screen drawing to another part of the screen

Is it possible to make it so that all drawing to an area "A" is translated to an area "B"?
For example drawing to the area(0,0)(100,100) and have it appear in area(200,200)(300,300).
The question is actually tagged with windows and graphics. This might have been targeted to Win32 and GDI (where I've unfortunately nearly no experience). So, the following might be seen as proof of concept:
I couldn't resist to implement the idea / concept using QWindow and QPixmap.
The concept is:
open a window fullscreen (i.e. without decoration)
make a snapshot and store it internally (in my case a )
display the internal image in window (the user cannot notice the difference)
perform a loop where pixmap is modified and re-displayed periodically (depending or not depending on user input).
And this is how I did it in Qt:
I opened a QWindow and made it fullscreen. (Maximum size may make the window full screen as well but it still will have decoration (titlebar with system menu etc.) which is unintended.)
Before painting anything, a snapshot of this window is done. That's really easy in Qt using QScreen::grabWindow(). The grabbed contents is returned as QPixmap and stored as member of my derived Window class.
The visual output just paints the stored member QPixmap.
I used a QTimer to force periodical changes of the QPixmap. To keep the sample code as short as possible, I didn't make the effort of shuffling tiles. Instead, I simply scrolled the pixmap copying a small part, moving the rest upwards, and inserting the small stripe at bottom again.
The sample code qWindowRoll.cc:
#include <QtWidgets>
class Window: public QWindow {
private:
// the Qt backing store for window
QBackingStore _qBackStore;
// background pixmap
QPixmap _qPixmap;
public:
// constructor.
Window():
QWindow(),
_qBackStore(this)
{
showFullScreen();
}
// destructor.
virtual ~Window() = default;
// disabled:
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
// do something with pixmap
void changePixmap()
{
enum { n = 4 };
if (_qPixmap.height() < n) return; // not yet initialized
const QPixmap qPixmapTmp = _qPixmap.copy(0, 0, _qPixmap.width(), n);
//_qPixmap.scroll(0, -n, 0, n, _qPixmap.width(), _qPixmap.height() - n);
{ QPainter qPainter(&_qPixmap);
qPainter.drawPixmap(
QRect(0, 0, _qPixmap.width(), _qPixmap.height() - n),
_qPixmap,
QRect(0, n, _qPixmap.width(), _qPixmap.height() - n));
qPainter.drawPixmap(0, _qPixmap.height() - n, qPixmapTmp);
}
requestUpdate();
}
protected: // overloaded events
virtual bool event(QEvent *pQEvent) override
{
if (pQEvent->type() == QEvent::UpdateRequest) {
paint();
return true;
}
return QWindow::event(pQEvent);
}
virtual void resizeEvent(QResizeEvent *pQEvent)
{
_qBackStore.resize(pQEvent->size());
paint();
}
virtual void exposeEvent(QExposeEvent*) override
{
paint();
}
// shoot screen
// inspired by http://doc.qt.io/qt-5/qtwidgets-desktop-screenshot-screenshot-cpp.html
void makeScreenShot()
{
if (QScreen *pQScr = screen()) {
_qPixmap = pQScr->grabWindow(winId());
}
}
private: // internal stuff
// paint
void paint()
{
if (!isExposed()) return;
QRect qRect(0, 0, width(), height());
if (_qPixmap.width() != width() || _qPixmap.height() != height()) {
makeScreenShot();
}
_qBackStore.beginPaint(qRect);
QPaintDevice *pQPaintDevice = _qBackStore.paintDevice();
QPainter qPainter(pQPaintDevice);
qPainter.drawPixmap(0, 0, _qPixmap);
_qBackStore.endPaint();
_qBackStore.flush(qRect);
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// setup GUI
Window win;
win.setVisible(true);
// setup timer
QTimer qTimer;
qTimer.setInterval(50); // 50 ms -> 20 Hz (round about)
QObject::connect(&qTimer, &QTimer::timeout,
&win, &Window::changePixmap);
qTimer.start();
// run application
return app.exec();
}
I compiled and tested with Qt 5.9.2 on Windows 10. And this is how it looks:
Note: On my desktop, the scrolling is smooth. I manually made 4 snapshots and composed a GIF in GIMP – hence the image appears a bit stuttering.

QT 5.7 QPainter line aligment

I am working with QT 5.7 and C++.
At the moment I try to get used to draw my own widgets with the QPainter class.
But I noticed a problem I couldn't solve.
I try to draw a border line extactly at the widget border but if I do so:
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter;
painter.begin(this);
painter.setBrush(Qt::cyan);
QBrush brush(Qt::black);
QPen pen(brush, 2);
painter.setPen(pen);
painter.drawRect(0, 0, size().width() - 1, size().height() - 1);
painter.end();
}
The Line is at the bottom and right site bigger than the others:
And before someone is telling me I have to remove the two -1 expressions,
you should know if I do this and also set the pen width to 1 there is no line anymore at the bottom and right side.
I think this artifact is caused by the "line aligment".
QT tries to tint the the pixels near the logical lines defined by the rectangle but actually because finally all have to be in pixels it has to decide.
If I am right, why there is no method to set the line aligment of the pen like in GDI+?
And how I can solve this?
Everything depends on whether you want the entire pen's width to be visible or not. By drawing the rectangle starting at 0,0, you're only showing half of the pen's width, and that makes things unnecessarily complicated - never mind that the line appears too thin. In Qt, the non-cosmetic pen is always drawn aligned to the middle of the line. Qt doesn't let you change it: you can change the drawn geometry instead.
To get it right for odd line sizes, you must give rectangle's coordinates as floating point values, and they must be fall in the middle of the line. So, e.g. if the pen is 3.0 units wide, the rectangle's geometry will be (1.5, 1.5, width()-3.0, width()-3.0).
Here's a complete example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/widget-pen-wide-38019846
#include <QtWidgets>
class Widget : public QWidget {
Q_OBJECT
Q_PROPERTY(qreal penWidth READ penWidth WRITE setPenWidth)
qreal m_penWidth = 1.0;
protected:
void paintEvent(QPaintEvent *) override {
QPainter p{this};
p.setPen({Qt::black, m_penWidth, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin});
p.setBrush(Qt::cyan);
qreal d = m_penWidth/2.0;
p.drawRect(QRectF{d, d, width()-m_penWidth, height()-m_penWidth});
}
public:
explicit Widget(QWidget * parent = 0) : QWidget{parent} { }
qreal penWidth() const { return m_penWidth; }
void setPenWidth(qreal width) {
if (width == m_penWidth) return;
m_penWidth = width;
update();
}
QSize sizeHint() const override { return {100, 100}; }
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget top;
QVBoxLayout layout{&top};
Widget widget;
QSlider slider{Qt::Horizontal};
layout.addWidget(&widget);
layout.addWidget(&slider);
slider.setMinimum(100);
slider.setMaximum(1000);
QObject::connect(&slider, &QSlider::valueChanged, [&](int val){
widget.setPenWidth(val/100.0);
});
top.show();
return app.exec();
}
#include "main.moc"

Programmatically invoke Snap/Aero maximize

Is there a way to programmatically invoke the Aera maximize effect using C or C++ for a specific window/window ID?
For example:
or
(source: thebuzzmedia.com)
I am using a border-less Qt window and Qt has an API for getting the window ID. I want to programmatically trigger the windows effects without the known triggers.
I don't want to talk about every single detail involved in achieving this effect, not only there's a lot that goes on but you also mentioned you understand the logic to place the windows at their specific locations. In this answer I'll address what I believe are the 2 main challenges:
How to receive and handle a maximize event?
How to create an approximation of the aero snap effect?
In order to answer the first question, we must analyze which event handlers are triggered when the window is maximized:
void resizeEvent(QResizeEvent* evt); // Invoked first,
void paintEvent(QPaintEvent* event); // then second,
void changeEvent(QEvent* evt); // and at last.
A Qt application is first notified of a resizeEvent(), which is followed by a paintEvent() to draw the window (or widget), and only after everything has been displayed, changeEvent() is invoked to let you know the widget was maximized (maybe it's a little bit late to receive such notification, I don't know).
Of all these, the only one we care about is resizeEvent(). This event handler informs the new window/widget size that can be used for comparison with the desktop size, thus allowing us to know if the event was actually a maximize request. Once we identify a maximize request, we can figure out whether the application should be maximized (and anchored) to right, left or to the center of the screen.
This would be the time to create the aero snap widget and place it on the screen as a visual clue to the user.
To answer the second question, I don't think is possible to call the native Windows API and ask it politely to perform this effect on your window. The only other logical choice is to write a code that approximates this effect ourselves.
The visual appearance can be replicated by drawing a transparent window with a shadow-ish border. The approach demonstrated in the source code below, creates and customizes a QWidget to make it behave and look like a aero snap window:
It's not the most beautiful thing in the world, I know. This demo creates a regular window for the user to interact with, and once it's maximized, it places itself to the left of the screen. To the right size of the screen it displays something that resembles an aero snap window (shown above).
The idea behind the aero snap widget is very simple: a QWidget with transparent background and a custom painting procedure. In other words, it's a transparent window which draws a rounded rectangle with a shadow and that's it.
To make it a bit more realistic, you should add some animation to resize the widget little by little. A for loop might do the trick, but if you need something fancy you'll end up using timers. If you take a look here, you can see the quickest & dirtiest method to perform animation with Qt in action, and better ways to deal with animation. However, for simple tasks like this, stick with frame-based animation.
main.cpp:
#include "window.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
window.h:
#pragma once
#include "snapwindow.h"
#include <QMainWindow>
#include <QEvent>
class Window : public QMainWindow
{
public:
Window();
void resizeEvent(QResizeEvent* evt);
//void paintEvent(QPaintEvent* event);
void changeEvent(QEvent* evt);
private:
SnapWindow* _sw;
};
window.cpp:
#include "window.h"
#include "snapwindow.h"
#include <QDebug>
#include <QWindowStateChangeEvent>
#include <QApplication>
#include <QDesktopWidget>
Window::Window()
{
setWindowTitle("AeroSnap");
resize(300, 300);
_sw = new SnapWindow(this);
_sw->hide();
}
void Window::changeEvent(QEvent* evt)
{
if (evt->type() == QEvent::WindowStateChange)
{
QWindowStateChangeEvent* event = static_cast<QWindowStateChangeEvent*>(evt);
if (event->oldState() == Qt::WindowNoState &&
windowState() == Qt::WindowMaximized)
{
qDebug() << "changeEvent: window is now maximized!";
}
}
}
// resizeEvent is triggered before window_maximized event
void Window::resizeEvent(QResizeEvent* evt)
{
qDebug() << "resizeEvent: request to resize window to: " << evt->size();
QSize desktop_sz = QApplication::desktop()->size();
//qDebug() << "resizeEvent: desktop sz " << desktop_sz.width() << "x" << desktop_sz.height();
// Apparently, the maximum size a window can have in my system (1920x1080)
// is actually 1920x990. I suspect this happens because the taskbar has 90px of height:
desktop_sz.setHeight(desktop_sz.height() - 90);
// If this not a request to maximize the window, don't do anything crazy.
if (desktop_sz.width() != evt->size().width() ||
desktop_sz.height() != evt->size().height())
return;
// Alright, now we known it's a maximize request:
qDebug() << "resizeEvent: maximize this window to the left";
// so we update the window geometry (i.e. size and position)
// to what we think it's appropriate: half width to the left
int new_width = evt->size().width();
int new_height = evt->size().height();
int x_offset = 10;
setGeometry(x_offset, 45, new_width/2, new_height-45); // y 45 and height -45 are due to the 90px problem
/* Draw aero snap widget */
_sw->setGeometry(new_width/2-x_offset, 0, new_width/2, new_height);
_sw->show();
// paintEvent() will be called automatically after this method ends,
// and will draw this window with the appropriate geometry.
}
snapwindow.h:
#pragma once
#include <QWidget>
class SnapWindow : public QWidget
{
public:
SnapWindow(QWidget* parent = 0);
void paintEvent(QPaintEvent *event);
};
snapwindow.cpp:
#include "snapwindow.h"
#include <QPainter>
#include <QGraphicsDropShadowEffect>
SnapWindow::SnapWindow(QWidget* parent)
: QWidget(parent)
{
// Set this widget as top-level (i.e. owned by user)
setParent(0);
/* Behold: the magic of creating transparent windows */
setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
setStyleSheet("background:transparent;");
setAttribute(Qt::WA_NoSystemBackground, true); // speed up drawing by removing unnecessary background initialization
setAttribute(Qt::WA_TranslucentBackground);
//setAutoFillBackground(true);
/* Use Qt tricks to paint stuff with shadows */
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
effect->setBlurRadius(12);
effect->setOffset(0);
effect->setColor(QColor(0, 0, 0, 255));
setGraphicsEffect(effect);
}
void SnapWindow::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
/* Lazy way of painting a shadow */
QPainter painter(this);
QPen pen(QColor(180, 180, 180, 200));
pen.setWidth(3);
painter.setPen(pen);
// Offset 6 and 9 pixels so the shadow shows up properly
painter.drawRoundedRect(QRect(6, 6, (width()-1)-9, (height()-1)-9), 18, 18);
}
This is just a quick demo to point you to the right direction. It is by no means a complete implementation of the effect you are looking for.
Maybe it is not what you need, but this effect is just resizing and moving window then try use Qt methods to do this.
bool left = false;
QSize size = QApplication::desktop()->size();//resolution of current screen
if(left)
{//left side
this->setGeometry(0, 0, size.width()/2, size.height());//(maybe need do some changes)
}
else
{//right side
this->setGeometry(size.width()/2, 0, size.width()/2, size.height());
}
With QApplication::desktop() it will work properly on screen with different resolutions.
In web I found something similar in winapi, but it didn't work properly:
HWND act = GetForegroundWindow();
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
The best way
Combine this approaches. For example:
HWND act = GetForegroundWindow();
bool left = false;
QSize size = QApplication::desktop()->size();
if(left)
{
this->move(0,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
else
{
this->move(size.width()/2,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
Why? Because move() regulate left and right sides, but PostMessage (winapi) set window's height properly on every screen (window will not locate lower then taskbar, as in your example)
EDIT
I changed code a little and now it is better. Yes, it is resizing again, but now it hasn't winapi code (PostMessage etc), so Photoshop doesn't catch it, there is one interesting method in Qt which called availableGeometry. It return normal height of screen which we need, with this method borderless windows perfectly simulates Aero Snap effects in different directions. It is works, maybe don't so good, but as I can see, there isn't API for Aero effects. Maybe this approach will be normal for yoo.
There is Aero Peek in Qt : http://qt-project.org/doc/qt-5/qtwinextras-overview.html , but it is can't solve this problem too.
Code:
bool left = true;
bool upper = true;
if(upper)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
this->setGeometry(rect);
}
else if(left)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
rect.setWidth(rect.width()/2);
this->setGeometry(rect);
}
else
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
int half = rect.width()/2;
rect.setX(half);
rect.setWidth(half);
this->setGeometry(rect);
}
Try it with frameless window! You should choose one direction or let user choose it.

Qt drawPixmap isn't drawing what I expect

I'm trying to make a Paint application in C++ with Qt. Everytime I click or click & drag the mouse, the program will draw something on a pixmap. After that, it updates the window calling paintEvent(), which will draw the pixmap onto the window.
void QPaintArea::mousePressEvent(QMouseEvent *event){
startpoint = event->pos();
drawPoint(startpoint);
is_pressed = true;
}
void QPaintArea::mouseReleaseEvent(QMouseEvent *event){
is_pressed = false;
}
void QPaintArea::mouseMoveEvent(QMouseEvent *event){
if(is_pressed == true){
endpoint = event->pos();
drawLine(startpoint, endpoint);
startpoint = endpoint;
}
else{
return;
}
}
void QPaintArea::paintEvent(QPaintEvent *event){
QDesktopWidget *desktop = QApplication::desktop();
int x = (desktop->width() - 800) / 2;
int y = (desktop->height() - 600) / 2;
QPainter painter(this);
QRect target(QPoint(x, y - 35), QSize(800, 600));
QRect dirtyrect(QPoint(0,0), QSize(800, 600));
painter.drawPixmap(target, *pixmap, dirtyrect);
}
The problem is that, the program is not printing the pixmap onto the window as expected. For example, I press the mouse at x: 17, y: 82 trying to draw something. The program will print what I drew but at an offset location, like x + 20, y.
Maybe I don't fully understand how QRect or drawPixmap works, but the pixmap is 800x600. "dirtyrect" is supposed to save the entire pixmap (starting a x: 0, y: 0, and the size 800x600).
drawPixmap(target, pixmap, source) paints on target rect of painter area (QPaintArea in this case) source part of pixmap. So you paint whole pixmap (0,0,800,600) at some (x,y-35,800,600) rect of QPaintArea. If you want to paint whole pixmap on whole QPaintArea just use drawPixmap(QPoint(0,0), *pixmap).
// EDIT
But if you expected, that pixmap will be painted with some offset from QPaintArea top left corner, then your calculations are wrong, and if you wont explain what did you want to achieve we won't be able to help you. Explain us your calculations of x,y (and magic -35 for y), and maybe we will be able to figure something out
// EDIT
You don't have to use window offsets like -35 if you're painting on widget. 0,0 of the widget is not top left corner of window frame, but of widget contents. How do you expect it to behave on other platforms?
If you want to paint it in the middle of your window simply use:
void QPaintArea::paintEvent(QPaintEvent *event){
QPoint middle = geometry.center();
int x = middle.x() - 800/2; // probably best would be pixmap->width()/2
int y = middle.y() - 600/2; // probably best would be pixmap->height()/2
QPainter painter(this);
painter.drawPixmap(QPoint(x,y), *pixmap);
}