[SOLVED] Enhancing placeholder text in QTextEdit
-
Hi,
I've got a few QTextEdits with a pre-defined text (kind of placeholder text). When user sets focus on the first QTextEdit and presses TAB, focus jumps to next QTextEdit (that already works). Problem is, that cursor is at the end of the placeholder text and user has to delete the whole text manually (that sucks).
What I'd prefer instead is that on TAB key pressed, next QTextEdit gets focus and placeholder text is fully preselected, so user just has to type any keyboard key and the whole placeholder text will be overwritten.I think I'll have to do this with focusInEvent(QFocusEvent* e), but I don't really understand what's going on with that...
In documentation I read, I have to subclass QTextEdit and implement this function... but what should I do then? I mean, that function is no SIGNAL, right? So simply implementing this function into my subclass will not solve my problem...
How do I connect a SIGNAL to this? -
You're mistaking signals/slots with events. These are two different mechanisms. When you subclass a QObject and reimplement an event this event will be called by Qt. Signals are another mechanism. For example some events may be implemented by emitting a signal from it, but that's not a rule.
Anyway I think you're overcomplicating things.
The behavior you described is kinda weird. How are you setting the focus jump and placeholder text? All you need to do is this:
@
textEdit->setTabChangesFocus(true);
textEdit->setPlaceholderText("Foo");
@
The placeholder text is grayed out and it's not selectable. When you focus in the cursor is set at the beginning. When you start typing the placeholder text vanishes.If you don't want to use the built-in placeholder functionality this is what the subclass would look like:
@
class TextEdit : public QTextEdit {
public:
TextEdit(const QString& text, QWidget* parent = nullptr)
: QTextEdit(text, parent) {
setTabChangesFocus(true); //to enable TAB jumping
}void focusInEvent(QFocusEvent* event) { QTextEdit::focusInEvent(event); //call the base implementation selectAll(); //this is the relevant part }
};
@ -
[quote]You’re mistaking signals/slots with events. These are two different mechanisms. When you subclass a QObject and reimplement an event this event will be called by Qt.[/quote]Hmm... I still don't understand the difference to signals...
In this case, Qt calls a function when the event "FocusIn" happens, so where is the difference to a signal called "FocusIn", which fires when "FocusIn" happens? Or can I say, everything is an event (clicked(), textChanged() etc.) and some events (like the two I mentioned) additionally fire a signal? I mean, where is the difference between "FocusIn" or "clicked()" ? Both have to happen and then, Qt reacts when I connect them to a function. Is "clicked()" also an event?
[quote]this event will be called by Qt[/quote]Why does Qt call events? I thought events happen while user does something (like selecting a QTextEdit --> focusIn), so why does Qt call that events? I'm confused...[quote]The behavior you described is kinda weird. How are you setting the focus jump and placeholder text?[/quote]It's not the built-In placeholder function but a pre-set text to modify, I just used this name to describe what my idea is...
[quote]If you don’t want to use the built-in placeholder functionality this is what the subclass would look like[/quote]That does the job very well, thank you! But unfortunatelly, I still don't understand how it works...
What am I doing there? I declare the "event" function:
@void focusInEvent(QFocusEvent* event);@
And now? Qt calls that function when the focusIn event happens? Well, but why do I have to type the following line, too?
@ QTextEdit::focusInEvent(event);@
Which one is the identifier for Qt to call when the focusIn event happens? I mean, if the last one is the needed identifier, it should be possible to rename the function to any function name I want, right?So weird... :-P
-
It's not weird. It's just c++ (with some Qt sprinkled here and there).I'll try to explain it better. Hope you won't get discouraged by the long text ;)
Signals, slots, and event handlers - all these are just usual class member functions from c++ language perspective.
The distinction is as follows:- signals - you only declare them. The body of the function is generated by Qt via "moc":http://qt-project.org/doc/qt-5/moc.html.
You call them via Qt extension keyword emit. This either calls any connected slot directly, or, in case of queued connections, puts the signal in a queue for later processing. - slots - these can be used in connect statement as the receivers of signals. As previously mentioned they get called either directly from connected signal invocation or indirectly in queued connections. In Qt5 any member function (even these not marked by slots: clause) can act as a slot when used with the new connect syntax(no SIGNAL/SLOT macros).
More on signals/slots "here":http://qt-project.org/doc/qt-5/signalsandslots.html
These two types of functions are used together. Events on the other hand are entirely different and independent mechanism. They have nothing to do with signals/slots, except, like any other function, they can emit a signal, but that's beside a point.- event handlers - these are specially named member functions. In various situations Qt (or the user) creates an event object and posts it for handling. There are "many event types":http://qt-project.org/doc/qt-5/qevent.html#Type-enum and, when one is posted, member with matching name of target object is called eg. if Qt posts an event of type QEvent::Close then a member called closeEvent() is called. More on how this is delivered is documented in the "notify() method":http://qt-project.org/doc/qt-5/qcoreapplication.html#notify You can also read more on the entire event system "here":http://qt-project.org/doc/qt-5/eventsandfilters.html
As for the previous wording I might have been imprecise, sorry. Qt does not "call" events. Qt creates QEvent object of appropriate for the situation type and "posts":http://qt-project.org/doc/qt-5/qcoreapplication.html#postEvent it to the event queue. The mentioned notify() function then calls similarly named method(called "event handler") in the target object.
Signals/slots and events are two different mechanisms for similar things - message delivery between objects. Because of how they are implemented though, events are usually faster as they better translate to underlying OS mechanisms. That's why they are used in low level and frequent tasks like mouse handling or windowing.
As for what happens in the code I gave you:
Base class of any widget is QWidget, and base of that is QObject. Both these types contain event handlers named accordingly eg. closeEvent(), mouseMoveEvent() and so on. Further subclasses add some more. As established before, these are called as a response to posted event.
We also established that these are just a normal c++ member functions. So as in any other c++ program you can subclass a class and overload any of its member to add functionality. So we subclass QTextEdit and overload method focusInEvent(). I assume you know what polymorphism is and how it works in c++. If not, pause here and go google it. So now, whenever focus gets transferred to our object, Qt posts an event of type QEvent::FocusIn. This is handled by calling our overloaded focusInEvent() method.
We're almost there. One thing is missing though. Since we overload the focusInEvent() method we discard any default functionality that was implemented in the base class. This might be drawing a colored frame around the focused control or some other stuff. We want to keep all of that behavior and just add to it some of our own stuff. So, when our focusInEvent() method is called, first thing we do is we call the implementation from the base class. The syntax for calling a base class method from a polymorphic overload is BaseClassName::member. That is what we did there. Next, when that is done, we add some of our new functionality, namely the selectAll() call.Hope this makes it more clear.
- signals - you only declare them. The body of the function is generated by Qt via "moc":http://qt-project.org/doc/qt-5/moc.html.
-
[quote]Hope you won’t get discouraged by the long text ;)[/quote]First of all, I really really like your long texts! I'm glad you take time for that and have the patience to explain that to me. What I really need is a "like"-button :)
Thank you very much for the detailed explanation, I read the whole event topic on Qt's documentation and now have a better overview. Your hint to the QCoreApplication::notify() function gave me the missing overview.What still interests me now is how Qt notices when a SIGNAL happens. Is this internally managed with events, too? I mean, how does Qt react on a mouseclick on a button? What happens is that a mousePressEvent() is created, so maybe Qt catches this event with an internal event handler and emits this SIGNAL clicked() ? If not, how does Qt notice the click event?
[quote] I assume you know what polymorphism is and how it works in c++.[/quote]Yes, I'm familiar with that functionallity and I often use it. I just didn't notice that the event handler is yet a member and so I overwrite it by declaring it in my subclass. Now everything makes sence, thank's for that! :)
Again thank you very much for that detailed information, I'm really greatful fot that!
-
Here's a somewhat simplified "flow" of how you get from a mouse click to a slot connected to clicked() signal on a Windows machine. Please remember that I simplify a lot of stuff. When I say "a list" it might be some complicated container or a mechanism. When I say "a tuple" it my be totally different contraption. There are whole articles and books on how that stuff works exactly and I don't have that kind of time, but the simple premise should be the same ;)
@
QPushButton btn("foo");
SomeObject handler;
connect(&btn, &QPushButton::clicked, &handler, &SomeObject::someSlot);
@step one user presses a button
- OS sends a WM_LBUTTONDOWN message to your app window.
- inside of exec() loop of your app Qt runs a OS specific handler for messages. The specifics are not important. "This":http://msdn.microsoft.com/en-us/library/aa383682.aspx and "this":http://msdn.microsoft.com/en-us/library/aa383738.aspx is what it might look like if you're interested. The point is Qt knows a button was pressed. This message also contains information on cursor position so Qt can calculate which widget lies under the cursor
- Qt "posts an event":http://qt-project.org/doc/qt-5/qcoreapplication.html#postEvent of type QEvent::MouseButtonPress and the button as a receiver.
- The same exec() loop runs notify() at some point. Inside notify() the event is taken off of the queue and delivered by calling receiving_button->keyPressEvent(event)
- the default implementation of QPushButton draws an indented frame etc.
step two - user releases a button, similar to above
6) OS sends a WM_LBUTTONUP message to your app window.
7) exec() calculates which widget should get it
8) exec() posts an event to the queue
9) notify() dispatches that to the button keyReleaseEvent()
10) NEW STUFF: default implementation of keyReleaseEvent calls emit clicked();step three
This part heavily depends on the type of connection. For the usual single-threaded direct connection it goes something like this:
Qt stores a list of connected objects. You can imagine it as a list of tuples: <sender, receiver, signal, slot , conn_type>. When you call connect() Qt puts all the needed info in that list. As you also remember the actual signals are not implemented by the programmer, but generated by moc at compile time. So now:
11a) The generated imlementation of clicked() goes through the list of tuples and checks if anything is connected to that signal with that specific sender. It finds the button and the pointer to the slot function.
12a) It calls that member function on that receiver and passes all the parameters along with sophisticated template machinery that I won't discuss.On a multi-threaded connection it's a little more complicated.
11b) The generated clicked() method goes through the list and finds the receiver and the slot. It also finds that the conn_type is not direct but queued.
12b) Each QObject is assigned "thread affinity" i.e. it belongs to specific thread object. clicked() gets that thread and posts a signal to the queue of that thread
13b) The loop in that other thread checks the queue and finds a signal stored and waiting to be delivered.
14b) Similarly to the single-threaded version it gets the receiver and slot pointer and calls it via template machinery. -
Ah that's the way it goes. So my first assumption was not that bad. Thank's.
[quote]There are whole articles and books on how that stuff works exactly and I don’t have that kind of time, but the simple premise should be the same ;)[/quote]Yes that's right, and all the more I appreciate your detailed and comprehensible answers. Unfortunatelly, I do that stuff just as a sideline while studying something completely different, so I have to learn all that stuff by reading and googling through the internet, with only little time.
I hope I do not annoy you with all those detailed questions, I just try to get an overview of that sophisticated stuff like memory management (last topic, you may remember :) ) or event handling. I'm additionally reading that stuff in Qt's documentation, but there are very often a few questions unanswered...Thank's a lot for that great support!