PDA

View Full Version : Click-through window



been_1990
18th February 2010, 15:34
In a past thread there didn't seem to be a simple solution to have a click-through window. I was wondering if there was a way by disabling all mouse events, or passing them back to the OS.
I tried hiding and showing the window really fast but if the mouse button continues being pressed the window continues to grab the moue event.
Someone said that I could make a 1 pixel "hole" on the window. If anyone has already done a click-through window please share the know... :D

high_flyer
18th February 2010, 16:58
I haven't seen your other thread so I am not sure what it is you need.
But making a hole in a widget is really easy - just use setMask().
http://doc.trolltech.com/4.6/qwidget.html#setMask

been_1990
18th February 2010, 18:16
I basically need a window to stay always on top (till now ok) and be click-through window. Imagine a image always on your screen, but you can't click on it. Using setMask() is a option, but using:

void mouseMoveEvent(QMouseEvent *event)
{
setMask(region);
}
goes too slow, and if the mouse moves to quickly the user would still be able to click on the window. (slow as in not updating the mask fast enought because of mouseMoveEvent gets called "too slow" )

high_flyer
19th February 2010, 09:12
If you click through widget is not a top level window, then you can use mapToGlobal() and handle the click on the parent widget.

been_1990
19th February 2010, 17:47
What do you mean with top-level? This is how my program call the widget:

main.cpp:

#include "app.h"

App w;
w.show();

app.h:


#include "widget.h"

Widget cantClick;

app.cpp:

cantClick..setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow);
cantClick.show();

ultr
19th February 2010, 20:31
I think what you are trying to do will require platform dependency.

Under X11 you can try reimplementing QWidget::x11Event ( XEvent * event ) and, for every pointer event i.e. ButtonPress, ButtonRelease, MotionNotify (http://tronche.com/gui/x/xlib/events/types.html), search the X11 window stack for the next window laying under the mouse and send the message to it.
Returning false in the above function will allow you to handle the event within your window in a standard Qt's way.
X11 window stack is ordered by widnows' z-order, so you just need to find the first window which contains the mouse pointer position and is not your own window (QWidget::winId()).

I don't know how to achieve this on other platforms.

Hope this helps.

been_1990
19th February 2010, 23:22
Well, my target platform is Windows. I know that this is doable when programming using the WinAPI(don't know how but I know it can be...). Is there a way to "wrap" my program with the WinAPI? Or even code just this specific "window" with WinAPI and still be able to comunicate with it in a signal/slot manner?

been_1990
20th February 2010, 21:35
http://www.codeproject.com/KB/wtl/transparent.aspx
The class Igor made (in the above link) seems to do just that, but it's made for the WinAPI. How can I use his class to modify the window QT is creating?
While doing research I came across this explanation as to how a app implemented "Click-through":
http://bytes.com/topic/visual-basic-net/answers/363382-make-window-click-through

The reason you can click through with ClocX is because it's drawn directly
to the main screen (if you set it to click through).

How could that be done, then?

Drawing directly onto the desktop's hDC wouldn't show on screen, if
there's a window on top of it, would it?

Try this out. This will get you started.

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tic
Dim dc As IntPtr = Me.GetDC(IntPtr.Zero
Dim grfx As Graphics = Graphics.FromHdc(dc
Dim ft As New Font(Me.Font.Name, 25, GraphicsUnit.Point
grfx.DrawString("Writing on the desktop! Isn't this cool?", ft, Brushes.Black,125, 150
ft.Dispose(
grfx.Dispose(
End Su

Well, when it comes to Windows programming I know absolutly nothing to be able to understand exactly what's going on and how to achieve something similar in QT. Anyone?

high_flyer
21st February 2010, 19:13
I think that your problem is that you want "conflicting" things.
One one hand, you want to draw directly on the desktop.
On the other, you want what ever it is you want to draw, behave as a window.
So that is a conceptual paradox.

On top of that, you want to do that with Qt, which is meant to be crossplatform, and this is very much native.

Still this is possible, but I am not sure that doing it with Qt would be the way to go, since you will have to go as low level as Qt lets you to get close to the native API.

Here is an article that explains how to draw dirrectlz on the the desktop.
http://www.codeguru.com/cpp/g-m/multimedia/desktopeffects/article.php/c4719/
That on its own is not difficult.
You could even (probably) use Qt do draw a dialog (by drawing it on a QPixmap, and giving its data as a CBitmap to the code in the article).
Then you will have to "listen" for the clicks on the desktop, and see if they fall in your painted dialog region, and handle them accordingly.

been_1990
21st February 2010, 20:54
One one hand, you want to draw directly on the desktop.
On the other, you want what ever it is you want to draw, behave as a window.
So that is a conceptual paradox.`

I want both, but not at the same time. Or the user will choose Click-through window or normal movable window.
So if Click-through is choosen, the window(QGraphicsView) gets hidden and passes it's data(?) to somewhere that will draw it to screen.
Can QT draw directly to screen?


Then you will have to "listen" for the clicks on the desktop, and see if they fall in your painted dialog region, and handle them accordingly.

Well, when it's set to click-trough I don't need to know when the mouse clicks the painted area.

And the link you posted is a Visual Basic project, is this only doable in VB? Or can WinAPI/C++ handle that too?

high_flyer
22nd February 2010, 09:04
Well, when it's set to click-trough I don't need to know when the mouse clicks the painted area.
Then the link I gave you will solve your problem - if you don't need to catch the mouse clicks, all you have to do is draw on the desktop, and that article shows how to do it.



And the link you posted is a Visual Basic project, is this only doable in VB? Or can WinAPI/C++ handle that too?
I have no idea what you are talking about.
The code there is C++ with windows API which is C.
Even if it was VB, the WINAPI is always C, even in VB, and you would use it the same in VB or C++.

been_1990
22nd February 2010, 14:30
Sorry, I confused a VS project file with it being in VB. The example shows how to draw to the screen, but will they be click-through? My goal is not only drawing to the screen directly. The goal here is to make a app click-through, no matter how.

been_1990
23rd February 2010, 16:05
Is there a way to send Windows (trough WinAPI) a mouse down event? If that is possible:
user clicks on window -> make a hole on window with setMask() -> send a mouse-down event trough WinAPI - -> remove mask -> be happy!

been_1990
24th February 2010, 17:55
Bump!... So? Anyone?

high_flyer
25th February 2010, 10:06
look, you described well what you want.
You know how to catch a mouse event in Qt, you know how to set a mask.
Now use google and MSDN to figure out how you can send events using WinAPI.
You are not the first person to want to synthesize mouse event win WinAPI, so google should be pretty helpful.
CodeGuru is a good place to ask C++ and windows programming questions.
You can't expect us to feed you with everything, or do the searching for you, specially if its off topic. (not Qt)
If you have specific questions post them.

been_1990
26th February 2010, 02:36
You're rigth high_flyer, I made opened a thread on dreamincode.net, and they helped me find out how to call mouse clicks trough the WinAPI. And after some time messing with mouse events I finnaly created what I needed.
I'm rigth now writing and clicking away with this big, grey, 1400x900 px, semi-transparent app on top of the web browser. Here is what I came up with:

stdafx.h:


#ifndef STDAFX_H
#define STDAFX_H
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define _WIN32_WINNT 0x0500 // so the code would compile
#include <windows.h>
#endif // STDAFX_H

Link: http://www.daniweb.com/forums/thread6727.html
main.cpp:

#include <QtGui/QApplication>
#include "widget.h"


int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}


widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();

protected:
void changeEvent(QEvent *e);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void dragEnterEvent ( QDragEnterEvent * event );
void dragMoveEvent ( QDragMoveEvent * event );
void dropEvent ( QDropEvent * event ) ;
bool eventFilter(QObject *obj, QEvent *event);

private:
Ui::Widget *ui;
int mouseX;
int mouseY;
};

#endif // WIDGET_H

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include "stdafx.h"
#include <QtGui>
#include <QDebug>

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowFlags(Qt::WindowStaysOnTopHint);
setWindowOpacity(0.4);
installEventFilter(this);
setMouseTracking(true);
setAttribute(Qt::WA_MouseNoMask);
setAcceptDrops(true);

}

Widget::~Widget()
{
delete ui;
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
mouseX = event->x(); // ____ Always update mouse position
mouseY = event->y(); //
QRegion maskedRegion(0, 0,width(), height(), QRegion::Rectangle); // mask the entire app
QRegion maskedRegion3(mouseX-1, mouseY-1,2,2, QRegion::Rectangle);// mask a 2 px square around the mouse
setMask(maskedRegion.subtract(maskedRegion3)); // ...
}

void Widget::mousePressEvent(QMouseEvent *event)
{
mouseX = event->x(); // ...
mouseY = event->y(); // ...
QRegion maskedRegion(0, 0,width(), height(), QRegion::Rectangle);// ...
QRegion maskedRegion3(mouseX-1, mouseY-1,2,2, QRegion::Rectangle);// ...
setMask(maskedRegion.subtract(maskedRegion3));// ...

// INPUT windows class from stdafx.h
INPUT *buffer = new INPUT[1]; //allocate a buffer

buffer->type = INPUT_MOUSE;
buffer->mi.dx = 100;
buffer->mi.dy = 100;
buffer->mi.mouseData = 0;
buffer->mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
buffer->mi.time = 0;
buffer->mi.dwExtraInfo = 0;

SendInput(1,buffer,sizeof(INPUT));
delete (buffer); //clean up our messes.
}


bool Widget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)
{
QRegion maskedRegion(0, 0,width(), height(), QRegion::Rectangle);
QRegion maskedRegion3(mouseX-1, mouseY-1,2,2, QRegion::Rectangle);
setMask(maskedRegion.subtract(maskedRegion3));

INPUT *buffer = new INPUT[1]; //allocate a buffer

buffer->type = INPUT_MOUSE;
buffer->mi.dx = 100;
buffer->mi.dy = 100;
buffer->mi.mouseData = 0;
buffer->mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
buffer->mi.time = 0;
buffer->mi.dwExtraInfo = 0;
SendInput(1,buffer,sizeof(INPUT));
delete (buffer); //clean up our messes.*/
}else
return false;//QWidget::eventFilter(obj, event);
}
//
//
// masking the app at the mouse's pos when dragging |
// V
//
void Widget::dragEnterEvent ( QDragEnterEvent * event )
{
mouseX = event->pos().x(); // ...
mouseY = event->pos().y(); // ...
QRegion maskedRegion(0, 0,width(), height(), QRegion::Rectangle); // ...
QRegion maskedRegion3(mouseX-1, mouseY-1,2,2, QRegion::Rectangle);// ...
setMask(maskedRegion.subtract(maskedRegion3));// ...
event->accept(); // accept event so that dragMoveEvent and dropEvent get called also
}

void Widget::dragMoveEvent ( QDragMoveEvent * event )
{

mouseX = event->pos().x();
mouseY = event->pos().y();
QRegion maskedRegion(0, 0,width(), height(), QRegion::Rectangle);
QRegion maskedRegion3(mouseX-1, mouseY-1,2,2, QRegion::Rectangle);
setMask(maskedRegion.subtract(maskedRegion3));
event->accept();
}

void Widget::dropEvent ( QDropEvent * event )
{
mouseX = event->pos().x();
mouseY = event->pos().y();
QRegion maskedRegion(0, 0,width(), height(), QRegion::Rectangle);
QRegion maskedRegion3(mouseX-1, mouseY-1,2,2, QRegion::Rectangle);
setMask(maskedRegion.subtract(maskedRegion3));
event->accept();
}


// generated by QtCreator
void Widget::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}


I'm so happy, I would be jumping up and down if I wasn't so tired. :D
Maybe one of you super coders wants to tweak this and make a proper tutorial for newbies like myself? I would like to know if the code used can be better, if I did some mistakes please point them out! :p

ultr
25th October 2010, 18:09
If anyone needs the same effect on X11, here is the code:


#include <QX11Info>
#include <X11/Xlib.h>
#include <X11/extensions/shape.h>

MyWidget::MyWidget() : QWidget()
{
QRegion region;
XShapeCombineRegion( QX11Info::display(), winId(), ShapeInput, 0, 0, region.handle(), ShapeSet );
}
You need link X11 extensions library. You may add this to your .pro file:
LIBS += -lXext

Note that window's decoration will still be clickable, so you will probably have to get rid of it.

mattie
3rd August 2016, 21:25
window masks are different from the OP's question, see http://doc.qt.io/qt-5/qwidget.html#setMask-1
when you click within the region, the event is not passed on to the underlying window