Custom events in Qt - c++

I am experimenting with custom events in Qt Creator. I am currently examining this example code on another site:
bool MyClass::event(QEvent* e)
{
if (e && e->type() == MyCustomEventType) {
MyCustomEvent* ce = dynamic_cast<MyCustomEventType*>(e);
return handleCustomEvent(ce);
}
// very important: still handle all the other Qt events!
return QObject::event(e);
}
The conditional statement checks if the event passed is the custom event, then it executes code that it wants to happen when the event occurs. What I do not understand is return handleCustomEvent(e) (what is this function supposed to do and where is it supposed to be declared?) and what return QObject::event(e) does. From what I read on the Qt documentation, the only thing this function does is return whether the event's function (is this handleCustomEvent?) is "recognized and processed". Is this supposed to handle all other events in the loop?

handleCustomEvent() is the method you need to implement in your class MyClass which will process your Custom Event MyCustomEventType.
If it's not your custom event, the last line return QObject::event(e); will be called to handle other events type.
So the method in your snippet bool MyClass::event(QEvent* e), is acting like a routing code, to decide where to send the event for processing, and does not actually process the events.
Once decided that 'e' is of type MyCustomEventType - it invokes handleCustomEvent() which will contain your code to handle this event type.
If not - the last line calls QObject::event() to process it instead. This will handle all other remaining types of events.
So, no, you need not worry about handling other events, unless you want to.
So, you'd declare the handleCustomEvent() in MyClass and implement it as well.
Something like:
class MyClass {
...
...
public:
bool handleCustomEvent(MyCustomEventType* e);
...
...
};
In the implementation you may have the logic as you require - to actually do the processing for your custom-event type MyCustomEventType.

Related

call wxHyperlinkEvent without clicking on link

I am using wxHyperLink. OnHyperlinkClicked(wxHyperlinkEvent&) is called when user clicks on the link. OnHyperlinkClicked() helps to switch between 2 screens. I want to call this function explicitly - without user interaction.
I am using the following code for that:
BEGIN_EVENT_TABLE(UserRegistrationFrame, wxFrame)
EVT_HYPERLINK(wxID_ANY, UserRegistrationFrame::OnAuthCodeOptionLinkClicked)
END_EVENT_TABLE()
void MyClass::MyFunction()
{
wxHyperlinkEvent event;
OnAuthCodeOptionLinkClicked(event);
}
void MyClass::OnAuthCodeOptionLinkClicked(wxHyperlinkEvent& event)
{
// code to switch
}
Is this the correct way to achieve my goal? Or do I have to use something different?
The code is not correct, because with EVENT_TABLE you are setting a handler that doesn't exist.
When the user clicks on the control the event is fired, and the static table will call UserRegistrationFrame::OnAuthCodeOptionLinkClicked, not MyClass::OnAuthCodeOptionLinkClicked
I'd better forget EVENT_TABLES and go with Dynamic Event Handling. Using Bind() gives you more features.
Now, you want to call the event-handler function directly, not coming from a user action. Then you need to create an event just to pass it to the handler. That's what you do in MyClass::MyFunction(), right?
Don't mix jobs. If both your direct-call and the event-handler use a common feature, put it in a different function and call it directly or from the event-handler.
But the question is Why do you use a wxHyperlinkCtrl for a task like screen switching? Do you want also the default browser to be launched?

Handling Signal without interrupting IO

How is it possible to catch a signal by a process , handle it such that a current ongoing IO output is not interrupted?
Can this be achieved by calling all registered callbacks handleExit() in exitSignalHandling till one handleExit() returns a status which tells that it handled the exit signal. The signal is handled in objectB if it has been marked to handle the exit, this is the case when the process is currently inside the relevant function which need special care :
void exitSignalHandling(){
/** call all registered callbacks */
}
while(1){
objectB.compute();
objectA.write(some data) /* when process enters: set flag to handle exit signal , objectB registered a call back objectB::handleExit()*/
}
class objectA{
bool handleExit(){
if( handleExit == true){
exitAfterWrite = true;
return true;
}
return false;
}
write(){
handleExit=true;
/*write data*/
if(exitAfterWrite){ exit(SUCCESS) }
}
}
Well obviously, the problem is that by handling a signal, you're exiting the object context and are back to static C code.
What you need to do is re-enter the object context with e.g. a singleton class or a global variable. This class would act as the holder class for all the objects that are registered for signal-uninterruptible I/O.
There are different ways of doing this. You can either employ an abstract class with bool handleExit() = 0; method. Other solution would be binding a handler method with std::bind(&class::handler, this) and storing it in a container.
When you start/end signal-uninterruptible I/O, you need to register/unregister your handler object with the holder. I think that using dynamic polymorphism would be the easiest way here.
I also have to state that your idea is not exactly thought-through. If I call handleExit, I get a value, whether exit was already set before. I don't see any use of it. But that's a minor problem.
What intrigues me the most is the use of exit call. Using this way of ending the application is not very bright. Imagine you would have two objects doing uninterruptible I/O at the time a interrupting signal comes. Only the first one will finish, the second one will still get killed along the way by the exit call from the first object.
Generally, I think it would be much better idea to create one class that is responsible for all the signal handling and decides to kill the application when no I/O is pending.

How to override keyPressEvent of QTextEdit?

I override keyPressEven() of widget QTextEdit:
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_0)
{
qDebug() << "Ok";
}
}
Button 0 works - show "Ok", but does not write in field of QTextEdit. Why? Thanks advance.
You need to call the base class implementation if you want to keep the default behaviour:
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_0)
{
qDebug() << "Ok";
}
QTextEdit::keyPressEvent(e);
}
See the docs for keyPressEvent.
In case someone using PySide2 is having trouble overriding QTextEdit's built-in keybindings, I post my solution here. Hopefully this is also useful for C++.
Scenario:
We are using a QTextEdit in an application and want to distribute a series of keybindings, but the text editor has already several hardcoded bindings. We want the editor to ignore them and hand them over to its parent, so they can be eventually handled by our code.
Problem:
While the docs say that whenever an event is ignored (e.g. by returning True in the installed eventFilter method) it automatically gets passed on to the parent, the truth is that when doing that for predefined keybindings QTextEdit did not hand them over: the event got ignored AND absorbed. So any textedit built-in keybindings filtered this way will be effectively globally disabled.
Direct event passing via sendEvent inside the editor's eventFilter had an interesting effect:
When calling sendEvent and returning super().sendEvent, the keybinding got executed by the editor AND the event passed to the receiver.
When calling sendEvent and returning True, the keybinding didn't get executed, and the event didn't get passed to the receiver.
When calling sendEvent and returning False, the keybinding didn't get executed, and the event passed to the receiver twice.
Furthermore:
Using event.ignore() didn't have any effect: the editor executed the built-in anyway.
Trying to discriminate via event.spontaneous() caused a segfault due to a missing pointer. Probably something got GCed but didn't try to debug that.
Trying to replace the event with a "dummy event" and call super also didn't work. Magically, the text editor kept executing the built-ins.
Maybe I missed something. Anyway, below I detail the approach that worked for me.
Solution:
The plan is to completely block the event, but broadcast it via signals, and then connect to them wherever we want. In your text editor instance, define the signal e.g. as follows:
eventCatched = QtCore.Signal(QtCore.QEvent)
Then, e.g. the following event filter will prevent execution of a few keybindings, and emit them once via eventCatched:
def eventFilter(self, obj, evt):
"""
Remember to install via self.installEventFilter(self)
"""
catch_control_keys = {QtCore.Qt.Key_Left, QtCore.Qt.Key_Right}
catch = False
# documentation for keys and modifiers:
# https://doc.qt.io/qtforpython-5/PySide2/QtCore/Qt.html
if evt.type() == QtCore.QEvent.KeyPress:
modifiers = evt.modifiers()
ctrl = bool(modifiers & QtCore.Qt.ControlModifier)
shift = bool(modifiers & QtCore.Qt.ShiftModifier)
alt = bool(modifiers & QtCore.Qt.AltModifier)
key = evt.key()
# catch all undo/redo builtins
if ((ctrl and shift and key == QtCore.Qt.Key_Z) or
evt.matches(QtGui.QKeySequence.Undo) or
evt.matches(QtGui.QKeySequence.Redo)):
catch = True
# catch specified control-keys
if ctrl and not shift and not alt:
if key in catch_control_keys:
catch = True
#
if catch:
# block event but send it as signal
self.eventCatched.emit(evt)
return True
else:
return super().eventFilter(obj, evt)
Then, we are free to connect the signal wherever we want to, we just need a method that handles events. In my case, I just wanted to pass them to the main window, which can be done with the following one-liner in the constructor:
text_editor.eventCatched.connect(lambda evt: QtCore.QCoreApplication.sendEvent(self, evt))
This way, whenever we catch an event in the text editor, it will be ignored and won't be propagated the standard way. Instead, a signal will be emitted, and we can subscribe to that signal to e.g. restart the propagation tree at a different point, as shown here via sendEvent.
Hope this helps!
Cheers,
Andres

Fire events in one class subscribed from another

I have a class AwesomeMousePointer that has some function to start playing animations on the mouse:
class AwesomeMousePointer{
void startAnimat();
void stopAnimat();
};
I have another object to which I have given the responsibility of figuring out whether the mouse anims should start or not (this is based on the internal hit testing on the object, for eg: if the mouse is inside the object for a specific time)
class SomeShape(){
Event<MouseArgs> startAnim
Event<bool> interrutptAnim
bool hitTest(int x, int y);
//Inside some loop function, check if the mouse is inside the object
if(hitTest(mouseXPos, mouseYPos)){
//if the mouse if inside for x time
NotiftyEvent(startAnim, MouseArgs);
}
else{
//mouse left the object
NotifyEvent(interruptAnim, false);
}
Now, again inside my AwesomeMousePointer, I'll add listeners for the events i.e
AddListener(SomeShape::startAnim, &AwesomeMousePointer::startAnim);
AddListener(SomeShape::interruptAnim, &AweseommousePointer::interruptAnim);
The single event system by using NotifyEvent and AddListener are working correctly in short different example I tried. Now inside this application of mine, I have a lot of the objects of SomeShape and a single AwesomeMousePointer. My question is if the above logic for the anims will work or should I explicitly pass the SomeShape object explicitly to subscribe to their events, in which things would become a bit difficult.
For eg:
AddListener(shapeObject1.startAnim, &AwesomeMousePointer::startAnimat);
AddListener(shapeObject2.startAnim, &AwesomeMousePointer::startAnimat);
AddListener(shapeObject3.startAnim, &AwesomeMousePointer::startAnimat);
OR
AddListener(SomeShape::startAnim, &AwesomeMousePointer::startAnimat);
Will the second one from the above work out? If not, how will that be done without explicitly passing the object since that makes the unclear and ShapeObjects shouldn't be inside the MousePointer.
Will this work if I make the events inside SomeShape as static?
static Event<MouseArgs> startAnim
static Event<bool> interrutptAnim
You say that you don't want to have coupling between the senders and targets. In this case, usage of Poco events is not appropriate. According to http://pocoproject.org/slides/090-NotificationsEvents.pdf, you should use Notifications instead of Events, because your senders and targets do not need to know each other then.

Events wxWidgets

I'd like to understand how to do a specific task. I was trying to set up event tables in wxWidgets and ran into an issue with their behaviour.
I set up some code in one class:
void TopRightPanel::OnSelChanged(wxTreeEvent& event)
{
wxTreeItemId item = event.GetItem();
TreeItemData* data = (TreeItemData *) GetItemData(item);
if(data != NULL)
{
particleSystemManager->SetSelectedParticleSystem(data->particleSystem);
}
}
This works fine and has the right values as expected. My problem with this though is that it's self contained and I want the class above it in the hierarchy to read the treeCtrl's action and make changes to all aspects of the U.I. So I tried this:
void Window::OnSelChanged(wxTreeEvent& event)
{
wxTreeItemId item = event.GetItem();
TreeItemData* data = (TreeItemData *) topRightPanel->GetItemData(item);//item.m_pItem.//m_MyTreeCtrl->GetItemData(itemId);*/
if(data != NULL)
{
particleSystemManager.SetSelectedParticleSystem(data->particleSystem);
}
}
Now I get an unhandled exception when topRightPanel->GetItemData(data) is called. The topRightPanel it uses doesn't seem to be updated and seems to be pointing to data before it's enstantuated in the class' constructor. Is there anyway I can get round this?
Edit:
I declare the event table like so:
#if USE_GENERIC_TREECTRL
BEGIN_EVENT_TABLE(TopRightPanel, wxGenericTreeCtrl)
#else
BEGIN_EVENT_TABLE(TopRightPanel, wxTreeCtrl)
#endif
EVT_TREE_SEL_CHANGED(TopRightPanel_Ctrl, Window::OnSelChanged)
END_EVENT_TABLE()
and I then declare the table in the header using DECLARE_EVENT_TABLE.
You must use the same class name in the event table macros that you used in BEGIN_EVENT_TABLE. IOW, your handler must be defined in Window event table and not in TopRightPanel event table. As tree events are wxCommandEvents, they are propagated upwards to the parent so if Window contains the tree control, this will work. Otherwise, e.g. if they are siblings, you would have to use Connect() as indicated in another answer.
(Re)reading the event handling overview would be highly recommended.
You don't show how do you connect this event handler but the problem is almost surely there and not in the code you show. If you're using Connect(), make sure that you pass the pointer to Window object as its last argument, otherwise you end up calling a method of Window class on TopRightPanel object with the unsurprisingly catastrophic consequences.
If you're sure that you do call the method on the right object, then the only other explanation I see is that you don't initialize topRightPanel before the first event of this type is generated. If this is the case, the simplest solution is to initialize the pointer to NULL and set it correctly at the end of the initialization code. And, of course, check for the pointer being non-NULL in the event handler.