Programmatically invoke Snap/Aero maximize - c++

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.

Related

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 C++ window title bar is blocked

I'm very new to Qt and trying to create application, that includes main window, QDockWidget and a button.
Suppose my main window has 1280 x 720 resolution. Then I want to implement QDockWidget that pop up from the left side, width of dockWidth and height of 720 without windowTitleBar. The button has size of (buttonWidth, 720). At first its hidden, and only the button is present, when we click the button dock pops up, button changes position to the right edge of dock.
Here is my code:
window::window(unsigned int h, unsigned int v, QWidget *parent) {
this->setFixedSize(h, v);
ui.setupUi(this);
createDockWindow();
}
void window::createDockWindow() {
dock = new QDockWidget(this);
dock->setTitleBarWidget(new QMainWindow());
dock->setGeometry(QRect(this->rect().topLeft(),
QSize(dockWidth, this->height())));
dock->setFloating(true);
dock->hide();
path_button = new QPushButton(">", this);
path_button->setGeometry(QRect(this->rect().topLeft(),
QSize(buttonWidth, this->height())));
connect(path_button, SIGNAL (released()), this, SLOT (showDock()));
}
void rubrick::showDock() {
if(dock->isHidden()){
dock->show();
path_button->setGeometry(QRect(dock->rect().topRight(),
QSize(buttonWidth, this->height())));
} else {
dock->hide();
path_button->setGeometry(QRect(dock->rect().topLeft(),
QSize(buttonWidth, this->height())));
}
}
So button works perfectly, at first my app looks like that screenshot:
But when the dock shows, it blocks app window title bar, like that: screenshot
I figured, this->rect().topLeft() returns top left of screen, but doesn't take into consideration window Title Bar, I tried to get menuBar height, but it return 30, and I found out that if I move left top by (0, 45) with 0 being width and 45 being height, the dock would be perfectly in place.
What am I doing wrong and how to fix that problem?
The method you're probably looking for is QWidget::frameGeometry, which returns the geometry of the window with the frame included. The rect method returns only the internal area. If you look at QWidget::rect in Qt Assistant, you'll find a link to a "Window Geometry" description that explains all of these interactions reasonably well.

Fast drawing on a qDeclarativeItem

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()

In Qt I am trying to paint over a QLabel, but QPaintEngine says to be off. Details below:

I can currently see the dialog window, the only thing I cannot see is the label where the picture is displayed and the rectangle drawn. Now I believe is because the widget is covering the label, but I cannot think on a way to walk around it. The application output says:
QRect(10,10 0x0), 0 0 0 final 0, QWidget::paintEngine: Should no longer be called, QPainter::begin: Paint device returned engine == 0, type: 1, QWidget::paintEngine: Should no longer be called, QPainter::begin: Paint device returned engine == 0, type: 1, QPainter::setPen: Painter not active, QPainter::drawRects: Painter not active, qDebug PAINTEVENT
my code is as follows:
This is my_qlabel.cpp
/Uses mouse events/
#include "my_qlabel.h"
#include <QMouseEvent>
#include <QPaintEvent>
my_qlabel::my_qlabel(QWidget *parent) :
QLabel (parent)
{
x = NULL;
y = NULL;
}//constructor
void my_qlabel::mouseMoveEvent (QMouseEvent *e)
{
this->x = e->x();
this->y = e->y();
emit mouse_pos();
}//mouseMoveEvent
void my_qlabel::mouseReleaseEvent (QMouseEvent *e)
{
this-> x = e->x ();
this-> y = e->y ();
emit mouse_release();
}//mouseReleaseEvent
void my_qlabel::mousePressEvent(QMouseEvent *)
{
emit mouse_pressed ();
}//mousePressEvent
void my_qlabel::paintEvent(QPaintEvent *pa)
{
this->parentWidget ();
this->lower ();
emit mouse_rectangle ();
pa->accept (); //accepts rect in pic
}//paintevent
void my_qlabel::leaveEvent(QEvent *)
{
emit mouse_left();
}//leaveEvent
below is my dialog.cpp:
/*Makes connections between my_qlabel and dialog.
*/
#include "dialog.h"
#include "ui_dialog.h"
#include "my_qlabel.h"
#include <QPainter>
#include <QEvent>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
InitialX = 0;
InitialY = 0;
Height = 0;
Width = 0;
ui->setupUi(this);
connect(ui-> lblMouse, SIGNAL(mouse_pos()), this, SLOT(mouse_current_pos()));
connect(ui-> lblMouse, SIGNAL(mouse_pressed()), this, SLOT(mouse_pressed()));
// connect(ui-> lblMouse, SIGNAL(mouse_pressed()), this, SLOT(paintEvent(QPaintEvent*)));
connect(ui-> lblMouse, SIGNAL(mouse_release()), this, SLOT(mouse_release()));
connect(ui-> lblMouse, SIGNAL(mouse_left()), this, SLOT(mouse_left()));
connect (ui->lblMouse,SIGNAL(mouse_rectangle()), this,SLOT(mouse_rectangle()));
//MUST: create function for clear rectangle
//connect(ui-> ClearSelection, SIGNAL(rectangle_clear()), this, SLOT(mouse_left()));
/*delete rectangle points and update paint*/
}
Dialog::~Dialog() { delete ui;} //deconstruct
/* Generate mouse position on real time in the label of X and Y*/
void Dialog::mouse_current_pos ()
{
ui->lblMouse_Current_Pos->setText(QString
(" X = %1 , Y = %2")
.arg(ui->lblMouse->x)/*%1*/
.arg(ui->lblMouse->y));/*%2*/
qDebug()<<"qDebug MOUSE_CURRENT_POS \n";
}//mouse_current_pos()
/* Uses mouse event to START rectangle paint event*/
void Dialog::mouse_pressed()
{
ui->lblMouse_Current_Event->setText(QString
("Mouse pressed at location %1 and %2!!!")
.arg(ui->lblMouse->x) //%1
.arg(ui->lblMouse->y)); //%2
/*Sets location of X and Y when is pressed*/
InitialX = ui->lblMouse->x;
InitialY = ui->lblMouse->y;
qDebug()<<"UPDATE OF MOUSE_PRESSED \n";
update();
} //mouse_pressed()
/*Uses release mouse event to END rectangle paint event*/
void Dialog::mouse_release()
{
ui->lblMouse_Current_Event->setText(QString
("Mouse released at location %1 and %2!!!")
.arg(ui->lblMouse->x) /*%1*/
.arg(ui->lblMouse->y));/*%2*/
/*Sets location of width and height when is released*/
Width= ui->lblMouse->x - InitialX;
Height= ui->lblMouse->y - InitialY;
qDebug()<<Width<<" final "<<Height;
qDebug()<<"qDebug MOUSE_RELEASE \n";
update();
}//mouse_release()
/*Mouse finds the cursor out of mouse area*/
void Dialog::mouse_left()
{
ui->lblMouse_Current_Event->setText("Mouse Left :( !!");
qDebug()<<"qDebug MOUSE_LEFT \n";
}//mouse_left()
void Dialog::mouse_rectangle()
/*PaintEvent paint rectangle*/
//!!!!!Runs good: Paints a rectangle and is adjusted!!!!
//void Dialog::paintEvent(QPaintEvent *pa)
{
// this->raise ();
QRect rectangle
(InitialX,InitialY, //Initial point of rectangle at press event
Width, Height); //Final point of rectangle at release event
rectangle.adjust (10,10,10,10);
qDebug()<<rectangle;
qDebug()<<InitialX<<InitialY<<Width<<" final "<<Height;
QPainter painter(this);
painter.begin (this);
painter.setPen(QPen(Qt::red, //Propierties of rectangle
2.0,
Qt::DotLine,
Qt::SquareCap,
Qt::MiterJoin));
painter.drawRect(rectangle);
qDebug()<<"qDebug PAINTEVENT \n";
}//paintEvent
Hopefully I have made my self clear, I want to draw on the qlabel, but I can't. If you can tell me how to fix the code in an explicit manner would be great.
The problem I see is, that you call a drawing function of a different class to draw in that different class by an emit in my_qlabels paintEvent.
If a widget receives the paintEvent only that widget can be drawn. An only in that class works QPainter p(this).
In your case, my_qlabel has the paintEvent but you "call" mouse_rectangle of the Dialog class to draw on the Dialog. But Dialog is not ready for painting. Hence the Paint device returned engine == 0 issue.
If your label shows a picture your label needs to do the drawing on itself to mark an area on the pic. and the label would be placed in the dialog. Within that paintEvent, at the beginning, you should call QLabel::paintEvent(event) to let the label do the picture painting. Your drawing will than be on top of that picture.
It is important that you draw in the paintEvent of the label on the label itself.
You may still use another function but you must pass along the pointer of my_qlabel and create the QPainter with that pointer (e.g. QPainter p(label) instead of QPainter p(this)).
So you could pass this on my_qlabel along with the emit, like emit mounse_rectangle(this) if you alter the signal and slot the accept QWidget * as argument. Though, I'd prefer calling any function directly from a paintEvent instead of using the signal and slot mechanism which is may introduce a considerable overhead in terms of performance and reduces code clarity.

How can I add a button to the corner of a widget in QT?

I am trying to display a square image and have an X in the top right corner (half in the image half outside) to close the image. I dont know of a layout manager that will allow me to do that. How do I implement this?
+---------O <- close button
| |
| |
+---------+
There will be a lot to implement here. I've accomplished this in the following way:
Step 1. Subclass QLabel to make it possible to capture mouse clicks. In the header declare signals Clicked and Pressed, and override the proper mouse events.
LabelButton::LabelButton(QWidget *parent) : QLabel(parent)
{
}
void LabelButton::mouseReleaseEvent(QMouseEvent *event){
emit Clicked();
event->accept();
}
void LabelButton::mousePressEvent(QMouseEvent *event){
emit Pressed();
event->accept();
}
Step 2. Add a LabelButton called xbutton containing an circular 'x' image to your widget at the location that you desire.
Example (This would be in your setupUi function):
...
xbutton = new LabelButton(MainWidget);
xbutton->setObjectName(QString::fromUtf8("xbutton"));
xbutton->setGeometry(QRect(0, 0, 31, 31));
xbutton->setPixmap(QPixmap(QString::fromUtf8(":/xbutton.gif")));
xbutton->setAlignment(Qt::AlignCenter);
...
Step 3. Create your widget. Set its background to transparent, and make sure its size includes room for the 'x' close button. Note: Setting your background to transparent means your widget will have to contain some child widget that accepts input from the user.
Example:
mywidget::mywidget(QWidget *parent): QWidget(parent){
setupUi(this);
moving=false; // notice that you must declare this bool for Step 4.
offset=QPoint(0,0); // Also a QPoint for Step 4
#if defined(Q_WS_MAC) //These values worked for me with the Mac OS 10.5 SDK
this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::Window);
QPalette pal = this->palette();
pal.setColor(this->backgroundRole(), Qt::transparent);
this->setPalette(pal);
#elif defined(Q_WS_WIN)//These values worked for me on Windows XP/Vista/7
this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |Qt::FramelessWindowHint | Qt::Window);
setStyleSheet("background:transparent;");
setAttribute(Qt::WA_TranslucentBackground);
#endif
connect(xbutton,SIGNAL(Clicked()),this,SLOT(hide()));
}
Now you have the functionality that you originally desired. When you click the xbutton, the window will close. But you will not have normal move functionality until you implement that.
Step 4. Implement move functionality to your widget.
/*
FUNCTION:mousePressEvent
used to help move the widget since there is no title bar, sets the initial offset of the mouse
*/
void mywidget::mousePressEvent(QMouseEvent *event){
if((event->button() == Qt::LeftButton)) {
moving = true;
offset = event->globalPos() - this->pos();
}
}
/*
FUNCTION:mouseReleaseEvent
used to help move the widget since there is no title bar, releases the "moving" attribute
*/
void mywidget::mouseReleaseEvent(QMouseEvent *event){
if(event->button() == Qt::LeftButton) {
moving = false;
}
}
/*
FUNCTION:mouseMoveEvent
used to help move the widget since there is no title bar
*/
void mywidget::mouseMoveEvent(QMouseEvent *event){
if(moving){
QPoint global = event->globalPos();
this->setGeometry(global.x()-offset.x(),global.y()-offset.y(),this->width(),this->height());
}
}
I found this way to be of most use to me, because I needed lots of functionality out of my customized slick-looking widget.
I really enjoy creative user interfaces and I hope that yours looks really sleek when you get it finished!