PDA

View Full Version : native event of child window



TonyInSoMD
20th December 2016, 15:43
I have a sub-classed QFrame and have a third party windows type window as a child. Is there a virtual function, something like virtual bool nativeEvent(...) or some other way to get the native events of that child window?

d_stranz
20th December 2016, 18:10
If you can get a window handle for your native window, you can embed it in a QWindow using QWindow::fromWinId(), and can further map it to a QWidget using QWidget::createWindowContainer(). This should give you access to the events using the normal Qt event loop and event handlers.

anda_skoa
21st December 2016, 11:06
That is likely very platform specific, e.g. if the system sends events not just to the program but also to the host.

Even if that is possible, it could be automatic or require some form of registration.

If we assume for a moment that this is the case for the platform you are working on, then native events would be available to a native event filter, see QCoreApplication::setNativeEventFilter().

Cheers,
_

TonyInSoMD
21st December 2016, 11:13
I don't have a WId to use QWindow::fromWinId() with, all I have is the windows handle data type HWND and don't know how to convert it to a WId. It's not a Qt window, so I can't use ->winId() to get it and the way it gets generated all I get returned is the HWND so that's all I have to work with as an identifier. What I'm working with is not a QObject or a Qanything. It's embedded in (a child of) a customized QFrame.

anda_skoa
21st December 2016, 13:49
I think a WId on Windows is a HWND handle.

Anyway, you've got the embedding part already done, no?

Cheers,
_

TonyInSoMD
21st December 2016, 16:13
my HWND is m_hRectPe. Its parent is ui.m_frame_RectGraph which is a sub-class of QFrame. I'm trying to get events from m_hRectPe. The data type of m_hRectPe is proprietary a ProEssentials graph. It does operate off of windows events. I subclassed QWindow into CPeWindow and re-implemented the keyPressEvent and put a break in there just to see if it gets called. So I've got this



Rat.h
CPeWindow *m_pRectWindow;

Rat.cpp
m_pRectWindow = (CPeWindow*)QWindow::fromWinId((WId)m_hRectPe);

I would think this would give me a QWindow pointer to m_hRectPe that I could then use to get Qt events from.

in CPeWindow I have



CPeWindow.cpp
void CPeWindow::keyPressEvent(QKeyEvent *ev)
{
if(ev->key() == Qt::Key_Alt) // Break right here so that if a key press event occurs, it stops the program here
{
int x = 1;
}
}


when m_hRectPe has focus and I press a key, nothing happens. From previous suggestions, this route seemed to make the most sense, or am I out in left field.

I'm sorry if I sound ignorant, its just been hard trying to create a hybrid of Qt and ProEssentials

P.S.
The ProEssentials guys only know how to work in mfc.

Tony

TonyInSoMD
21st December 2016, 19:31
I did some more checking, I'm 99.999% sure the WinID of m_pRectWindow is the same as m_hRectPe, that they're both pointing to the same window

d_stranz
21st December 2016, 19:45
If you reimplement CPeWindow:: event() do you see any events at all from the MFC window?

TonyInSoMD
21st December 2016, 20:06
I put in the code


bool CPeWindow::event(QEvent *ev)
{
int x = 1; // set break here to see if it enters the function, should catch ANY event
return true;
}

But it never enters the function no matter what I do

d_stranz
21st December 2016, 22:46
I see this in the ProEssentials MFC DLL documentation:


PEcreate is similar to the Windows CreateWindow function. However, it always creates a child window and places this child into the provided parent. The PEcreate function returns the Windows handle for the newly created control and this handle must be used to set properties and eventually destroy the control. As with most child windows, the ProEssentials controls will communicate with its parent through WM_COMMAND notification messages.

So it might be that the HWND you are getting is actually the parent window and not that of the control itself. The parent window could be eating all of the events.

If I had the time I'd download the demo and see what I could do, but time is not something I have a lot of at the moment. One thing you may find when mixing MFC and Qt is lots of apparent memory leaks upon program exit when you watch your program in the debugger. These aren't real and apparently result from MFC and Qt DLLs and static objects being unloaded / freed in a different order from how they were loaded / created. It's a nuisance that makes it hard to find real memory leaks.

And the "are you sure it's plugged in" question: You've correctly derived CPEWindow from QWindow, added the Q_OBJECT macro, etc., etc., right?

TonyInSoMD
22nd December 2016, 12:24
Before I forget to say it, thanks for everyone's time and help. The Q_OBJECT macro is in there, I'm not sure what else would need to be added other than the standard c++ stuff when sub-classing. In this particular case, the "provided parent" is the sub-classed QFrame I mentioned in the first post. The QFrame receives the WM_COMMAND messages which I get using nativeEvent. When the message->message is WM_COMMAND in the QFrame, the HIWORD(message->wParam) tells me what the communication is. They have one for keypress but not keyrelease. I'm trying to find a way to capture the keyrelease event so I can switch between vertical and horizontal scroll depending on whether the Alt key is depressed. When I asked the folks at ProEssentials if there was a way to do it, they said to add this to their sample code:



DLL, C++,

In our VC demo, PEViews file

Header file

protected:
//{{AFX_MSG(CPEView)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);

// new func
virtual BOOL PreTranslateMessage(MSG* pMsg);

CPP file

// new implementation
BOOL CPEView::PreTranslateMessage(MSG* pMsg) {
if ( pMsg->message == WM_KEYDOWN )
{
if (pMsg->wParam = VK_MENU)
{
PEnset(m_hPE, PEP_nMOUSEWHEELFUNCTION, PEMWF_VERT_SCROLL);
}
return TRUE;
}
else if (pMsg->message == WM_KEYUP )
{
if (pMsg->wParam = VK_MENU)
{
PEnset(m_hPE, PEP_nMOUSEWHEELFUNCTION, PEMWF_HORZ_SCROLL);
}
return TRUE;
}
else
return CView::PreTranslateMessage(pMsg);



I have no idea how to translate this from MFC to Qt. I don't know jack about MFC. Maybe I should have said all this up front, if so my sincerest apologies. When I said I wasn't working in MFC, it was Qt, ProEssentials offered to customize the code for a measly $1000 USD. $1000 to add a keyrelease wparam to WM_COMMAND which in my opinion I can't believe doesn't already exist considering they have one for keypress. I'll get off my soap box now.

edit after post
I'm starting to think that the ProEssentials window is not a "Windows" window in that it doesn't generate events with MSG's. The only MSG's it generates are the self-made self-emitted WM_COMMAND MSG's. There aren't any Windows (or Qt) events to catch because they're not generated, only the ProEssentials events which ProEssentials generates. If so, I've been spinning my and your wheels all this time and you have my apologies. What do you think, does it sound like I might be right?

anda_skoa
22nd December 2016, 13:41
Rat.h
CPeWindow *m_pRectWindow;

Rat.cpp
m_pRectWindow = (CPeWindow*)QWindow::fromWinId((WId)m_hRectPe);

I would think this would give me a QWindow pointer to m_hRectPe that I could then use to get Qt events from.

No, this wouldn't give you an object of your class, just make the standard QWindow object be interpreted as your class.

I.e. fromWind() wraps the native window in QWindow of a subclass thereof, your cast just tells the compiler to reinterpret the object as being of your type.
It doesn't become your type.

What you could try is to install an event filter, (as in QObject::eventFilter(), on the returned window, but I doubt it will see any of the events sent to the wrapped window from outside Qt.



The ProEssentials guys only know how to work in mfc.


I haven't used this myself but I was made aware that it is possible to run MFC and Qt in the same process.
It looks like the library to do that is https://github.com/qtproject/qt-solutions/tree/master/qtwinmigrate


existing custom controls developed with MFC or Win32 can be integrated into Qt widgets


Cheers,
_

TonyInSoMD
22nd December 2016, 14:20
Yeah, I tried eventFilter, event, nativeEvent, keyPressEvent keyReleaseEvent but none of them do anything. I haven't written any of my code in MFC so there's nothing to translate.
The code they told me to add gets added during creation of the parent window. It intercepts the events/messages before they get sent to the ProEssentials child window. At least, that's the way I understand it, I could be wrong. In order to add something similar to my code, I would think it would have to happen in the constructor of CPeFrame (the QFrame that's the parent window). Qt doesn't have anything like that that I know of. Is there something in qApp maybe that sees all events regardless of who the child is? Maybe? I'm grasping at straws, but I'm starting to think I need to give up the ghost and accept that there's no way to do it using Qt. Which would suck.

anda_skoa
22nd December 2016, 18:41
I haven't written any of my code in MFC so there's nothing to translate.


While the framework is intended to serve for migrations from MFC to Qt, its main feature for doing that is to allow a "mixed mode" applications.
I.e. Qt's widgets can be used in a predominantly MFC based applications or vice versa, basically allowing a step by step migration.

My line of thinking is that if you have code that would work in an MFC application, then maybe it would also work if the target control was part of the Qt application via this hybrid mechanism.

How do you currently embed the target component?
Is it running in a separate process and you are emedding that process' window using fromWinId(), is it a COM component and you are using QAxWidget or something else?

Cheers,
_

TonyInSoMD
22nd December 2016, 19:38
Just in case it makes a difference, I'm working in VisualStudio 2013
The following is the code to create a graph window


m_hRectWnd = (HWND)ui.m_frame_RectGraph->windId(); // ui.m_frame_RectGraph is a sub-classed QFrame so that I can access nativeEvents, etc. is to be parent window of graph window
RECT rect;
GetClientRect(m_hRectWnd, &rect);
m_hRectPE = PECreate(PECONTROL_SGRAPH, WS_VISIBLE, &rect, m_hRectWnd, 1001)
// m_hRectPE is HWND of the ProEssentials graph window
// PECONTROL_SGRAPH determines what type of graph is being created, in this case a rectangular scientific graph
// WS_VISIBLE is the window style
// &rect is location and size of object passed to the Windows CreateWindow function
// m_hRectWnd is the parent window to place control passed to the Windows CreateWindow function
// 1001 is random, it can be any number

It's not a COM, QAxWidget or any other special library other than the ProEssentials library. As far as Qt libraries go, it's just the basics.
It's all running in a single process.

d_stranz
22nd December 2016, 20:14
So now if you do something like this:



QWindow * pWindow = QWindow::fromWinId( WinId( m_hRectPE ) );
pWindow->installEventFilter( this );


and implement MainWindow:: eventFilter(), do you see events coming from the chart?

TonyInSoMD
23rd December 2016, 12:29
in event filter I put:


bool CRat::eventFilter(QObject *pObject, QEvent *pEvent)
{
int x;
int y = (WId)m_hRectPE; // used to verify object throwing the event is the graph window
if(pObject->isWindowType)
{
x = ((QWindow*)pObject)->winId(); // used to verify the object throwing the event is the graph window
}
qDebug() << "Object = " << x << "Graph = " << y << "Event Type = " << pEvent->type();
return false;
}


The only events I can get to trigger are:
resize, but only during construction
Focus In
Focus About to Change
Focus Out
On left mouse click: Input Method Query
When using a menu option to open a user input window:
Window Blocked
Window Unblocked

I've tried everything I can think of to trigger another event, but nothing happens. The graph has built-in keyboard shortcuts, for example the "x" key brings up an export window. Pressing these do not trigger an event even though events (keyPress keyRelease) are obviously occurring.

d_stranz
23rd December 2016, 18:26
I am guessing that your plot window is probably a composite and that the handle you are getting is just the top-most parent. In that case, it is likely that whatever events the lower level windows are handling are either eaten by them or passed up to the top-most parent, which eats them instead.

You might have to drop into Windows code to determine 1) if there is actually a window hierarchy inside the top-level control, and 2) if you can create a QWindow around one of these and see the events you want.

There is an EnumChildWindows() method in the Windows API that you can use for this purpose. It takes a handle to the top-most window along with a pointer to a callback function that will be called for every child window found. You could use it like this:



// In MainWindow.cpp

#include <windows.h> // probably needed
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
MainWindow * pMain = (MainWindow *)lParam;
QWindow * pWind = QWindow::fromWinId( hwnd );
pWind->installEventFilter( pMain );
return TRUE;
}

void MainWindow::EnumeratePEChildren( HWND hPERect )
{
EnumChildWindows( hPERect, EnumWindowsProc, LPARAM( this ) );
}


This will result in the event filter you have already written being installed on every child window of the PERect window (if any). You'll be able to see (by way of the QObject argument to the event filter) which child window is sending which event. Hopefully you'll see some of what you are trying to intercept.

Note that the enumeration is recursive - you will get -all- children, all the way down the hierarchy if the hierarchy exists. You could try retrieving other information about the window (eg. GetWindowText(), etc.) to see if the name of the window makes sense or to narrow it down by what you do inside it. At that point, you should be able to determine exactly which child is the one to watch.

TonyInSoMD
27th December 2016, 12:12
Sorry about the long delay in my response, I was out for the Christmas holiday. I'm going to show my ignorance here. All my programming has been in Qt and I've never really had to resort to the Windows API. Don't assume there's I step I'm just going to "know" because any idiot would. I'm dumber than an idiot in this case. The main class for the program is CRat (RCS Analysis Tool). I added d_stranz's code exactly to the .cpp . The only difference is I also added


void EnumeratePEChildren( HWND hPERect );

to the .h file
Is there something I need to add to the .h file for the CALLBACK function? Where the callback function is called in EnumeratePEChildren it doesn't have any parameters or even parenthesis with it. Is this correct? When I try to compile I get an
error C2065: 'EnumWindowsProc' : undeclared identifier
error, which doesn't surprise me looking at the code. What step am I missing?
While I'm at it, I'm guessing I would run EnumeratePEChildren at the end of the constructor after all the other windows have been created. Is this correct? Can I use it in the constructor if the PE windows have been previously created in the constructor?
Again, thanks for all the help. You guys have really gone out of your way to help.

Tony

d_stranz
27th December 2016, 18:56
error C2065: 'EnumWindowsProc' : undeclared identifier

Did you add the function I wrote (EnumWindowsProc) to the MainWindow.cpp file? Did you add it before or after your implementation of MainWindow:: EnumeratePEChildren()? The compiler is complaining because it can't resolve your reference to that function when you use it in EnumeratePEChildren. So if you did add it, you probably need to move it ahead of your use of it in EnumPEChildren() (or add a forward declaration).

The #include <windows.h> is there because it is probably necessary for the declaration of the Windows API function EnumChildWindows() and the declarations of the BOOL and CALLBACK macros / typedefs.

I would not call the EnumeratePEChildren method in the constructor. It might be OK to do so, but it is probably a better idea to call it in the MainWindow showEvent(). At that point, all of the windows have been fully instantiated and are ready to be made visible. In the constructor, the widgets / windows have been created, but they might not be fully instantiated (eg. with zero size, etc.)

If you are using Visual C++, I think the linker will automatically link in the required Windows run-time libraries to resolve the reference to the EnuChildWIndows() function.

TonyInSoMD
27th December 2016, 19:24
I'm a little confused. Are you assuming I've sub-classed QMainWindow so that I can re-implement showEvent() and add to it's .cpp? If so, I haven't. It sounds like this is something I need to do in order to follow your suggestions. Is that correct?

d_stranz
27th December 2016, 19:45
Sorry, I re-read your first post and see that you've subclassed QFrame. Put the code in that QFrame subclass instead of subclassing QMainWindow.

TonyInSoMD
28th December 2016, 16:36
I tried it starting at the subclassed QFrame so that it gets it and all its children and still the only events I can get are
23 Focus about to change
9 Focus out
8 Focus in
207 InputMethodQuery
103 WindowBlocked
104 WindowUnblocked
PE obviously has it set up so that something is eating all the events. MFC accesses the events through PreTranslateMessage. Does Qt have an equivalent? I haven't been able to find one.
From what I understand of Qt, it just doesn't make sense that eventFilter isn't catching it.

d_stranz
29th December 2016, 18:24
Huh. About the only other thing I can suggest is to install an event filter using QCoreApplication::installNativeEventFilter(). You'll have to derive a class from QAbstractNativeEventFilter and implement the QAbstractNativeEventFilter::nativeEventFilter() method. Be sure to return "false" from the method, otherwise the messages will be eaten by your filter and never get to the rest of your app.

If this is successful at trapping messages from the PE window, then you could use this filter to create your own QEvents and post them to the event loop for handling by your QFrame.

TonyInSoMD
3rd January 2017, 19:54
Thanks, subclassing QAbstractNativeEventFilter and installNativeEventFilter worked. I doubt that this is what you meant, but this is what I did.
Subclassing QAbstractNativeEventFilter:


#include<QAbstractNativeEventFilter>
#include <QObject>

class CCustomEventFilter : public QObject, public QAbstractNativeEventFilter
{
Q_OBJECT

public:
CCustomEventFilter(QObject *parent = 0);
~CCustomEventFilter();

virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;

signals:
void ShiftKeyDown();
void ShiftKeyUp();
};

.cpp file
#include "ccustomeventfilter.h"
#include "windows.h"

standard default constructor that doesn't do anything

bool CCustomEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
if (eventType == "windows_generic_MSG")
{
MSG *pMsg = (MSG*)message;
if (pMsg->message == WM_KEYDOWN)
{
if (pMsg->wParam == VK_SHIFT)
{
emit ShiftKeyDown();
}
}
else if (pMsg->message == WM_KEYUP)
{
if (pMsg->wParam == VK_SHIFT)
{
emit ShiftKeyUp();
}
}
}
return false;
}


Then in my MainWindow program (CRat);


in CRat.h
#include "ccustomeventfilter"

CCustomEventFilter *m_pKeyFilter;

slots:
void OnKeyShiftDown();
void OnKeyShiftUp();

in CRat.cpp

in constructor
m_pKeyFilter = new CCustomEventFilter();
qApp->installNativeEventFilter(m_pKeyFilter);
connect(m_pKeyFilter, SIGNAL(ShiftKeyDown()), this, SLOT(OnKeyShiftDown()));
connect(m_pKeyFilter, SIGNAL(ShiftKeyUp()), this, SLOT(OnKeyShiftUp()));

in body of code
void CRat::OnKeyShiftDown()
{
set graph to vertical scroll
}

void CRat::OnKeyShiftUp()
{
set graph to horizontal scroll
}


I tried to find examples where a subclass native event filter sent out an event or something that could act on MainWindow objects (in my case, graph windows) but couldn't find any. All the examples I could find just showed how to print out a deBug() line when it hit the event you wanted. That doesn't tell me how to get it to do anything in the program I'm working from. This was the only way I could think of to make it either throw an event or emit a signal that MainWindow could use. It works, but there has GOT to be a prettier way to do it.
Anyway, without all your help d_stranz I would have never gotten it and I thank you from the bottom of my heart.

Tony

d_stranz
3rd January 2017, 22:04
Thanks, subclassing QAbstractNativeEventFilter and installNativeEventFilter worked. I doubt that this is what you meant, but this is what I did.

No, that's what I did mean, but the implementation was left as an exercise for the reader... Looks like you have been successful at generating proof of concept.

Shouldn't you be also checking to make sure that the window handle matches that of the PE window (or whichever child window) that is generating (some of) the messages? I would think that your native event filter would be called for -all- windows in your app (Qt and non-Qt) since it is installed at the app level and isn't restricted to any particular window. You wouldn't want the shift key to affect the PE control no matter where it is pressed in your app.

TonyInSoMD
4th January 2017, 11:45
Shouldn't you be also checking to make sure that the window handle matches that of the PE window (or whichever child window) that is generating (some of) the messages?
I think I mentioned in an earlier post that I haven't done any work with the windows API. Thanks for clueing me in on the windows handle. It wasn't until you mentioned it that I got to looking at the MSG struct and saw that it gave you the HWND of the window that generated the message. Now I can double check to make sure it was the PE graph that sent the message. Thanks! I had the signal/slot send/receive the HWND as a parameter and then checked it against the HWND of the PE graph in the MainWindow program (CRat) before making any changes. This thread has been a lot of help to me and I hope it helps some one else in the future that has a similar problem getting messages.

d_stranz
5th January 2017, 06:00
I had the signal/slot send/receive the HWND as a parameter

I think it would be more efficient to check it in the native filter so you can decide not to send the signal at all instead of sending the signal for every window. Since you have derived a class from QAbstractNativeEventFilter, you can add an HWND as a member variable of that class, set it to the PE control's HWND, and check it first thing when the event comes in. If it doesn't match, then you can immediately return false instead of firing off the signal. If you have more than one of these PE controls in your app, you could make the member variable a collection of HWNDs (a QHash< HWND > maybe).


This thread has been a lot of help to me and I hope it helps some one else in the future that has a similar problem getting messages.

Glad to help. I spent years drinking the Windows Koolade before learning about Qt, so it's good to dust off the old brain cells once in a while to remember some of that stuff.

anda_skoa
5th January 2017, 10:54
I tried to find examples where a subclass native event filter sent out an event or something that could act on MainWindow objects (in my case, graph windows) but couldn't find any.


You can create instances of Qt event classes and use QCoreApplication::postEvent() or QCoreApplication::sendEvent() to send them to a receiver object.

However using signals as you did looks fine as well and easier in this case.

Cheers,
_