On Qt doc website in QHeaderView class i found two signals with similar descriptions:
void QHeaderView::sectionDoubleClicked(int logicalIndex)
and
void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
what's the difference between the two of these? When should I use the first, and when the other?
Although the documentation strings are exactly the same,
void QHeaderView::sectionDoubleClicked(int logicalIndex)
This signal is emitted when a section is double-clicked. The section's logical index is specified by logicalIndex.
[signal]void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
This signal is emitted when a section is double-clicked. The section's logical index is specified by logicalIndex.
The signals are emitted in different cases. From KDE's copy of Qt5,
void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
{
...
int handle = d->sectionHandleAt(pos);
if (handle > -1 && sectionResizeMode(handle) == Interactive) {
emit sectionHandleDoubleClicked(handle);
...
} else {
emit sectionDoubleClicked(logicalIndexAt(e->position().toPoint()));
}
}
The documentation doesn't make it particularly clear, though, when "handles" might be present and when they aren't. At a guess, if your sections are resizable you may get a handle -- for resizing -- and then you can (double) click on either the handle or the section-body.
Related
I am working on a project where I receive a message via UDP and based on that message I am emitting different signals and all of them have the same parameters.
The structure is like this:
if(command_type == COMMAND_TYPE_MOVE)
{
emit sigMoveForward(data);
}
else if(command_type == COMMAND_TYPE_STOP)
{
emit sigStopMove(data);
}
This gets really tedious to programm and maintain when getting past like 10 commands. Is there a way to do this better?
I thought of creating a QMap and doing a lookup on it and emiting the signal I am getting. Is this possible in Qt to have a pointer to the function and omit it this way?
There is no difference between
emit sigMoveForward(data);
and
sigMoveForward(data);
So, you can create QMap with pointers to signals or beter plain array if commands are continuous. It has quite tricky syntax, though.
void (Your_class::*signals[COMMAND_COUNT])(your_data_type) ;
...
signals[COMMAND_TYPE_MOVE] = &Your_class::moveForward;
signals[COMMAND_TYPE_STOP] = &Your_class::stopMove;
....
And emit it like that:
(this->*signals[command_type])(data);
If all signals contain the same data, you could send a unique signal containing 2 parameters (the command_type + the data) and then check the command_type in the receiving slot
//...
emit sigCommand(command_type, data)
//...
void MyClass::commandReceived(CommandType command_type, QVariant data) {
if(command_type == COMMAND_TYPE_MOVE)
{
moveForward(data);
}
else if(command_type == COMMAND_TYPE_STOP)
{
stopMove(data);
}
//...
}
I want to convert all lowercase characters as I type to uppercase in a QPlainTextEdit. In a QLineEdit I do the same via a validator, but there seems to be no validator for QPlainTextEdit.
I have tried ui->pte_Route->setInputMethodHints(Qt::ImhUppercaseOnly); but it does nothing, most likely using it wrong.
Any better option as using my "own" class?
A quick test using an event filter seems to work reasonably well...
class plain_text_edit: public QPlainTextEdit {
using super = QPlainTextEdit;
public:
explicit plain_text_edit (QWidget *parent = nullptr)
: super(parent)
{
installEventFilter(this);
}
protected:
virtual bool eventFilter (QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::KeyPress) {
if (auto *e = dynamic_cast<QKeyEvent *>(event)) {
/*
* If QKeyEvent::text() returns an empty QString then let normal
* processing proceed as it may be a control (e.g. cursor movement)
* key. Otherwise convert the text to upper case and insert it at
* the current cursor position.
*/
if (auto text = e->text(); !text.isEmpty()) {
insertPlainText(text.toUpper());
/*
* return true to prevent further processing.
*/
return true;
}
}
}
return super::eventFilter(obj, event);
}
If it does work sufficiently well then the event filter code can always be pulled out separately for re-use.
Using event filters for such a simple task does not look like a good idea, since you are forced to implement either a separate class inheriting QPlainTextEdit or create some separate class working as a filter. Instead, you could also do the following:
// Note. This is just a sample. Assume that 'this' is context of some class (e.g. class implementing QDialog/QMainWindow)
auto lineEdit = new QLineEdit();
/*
Here, you can use also &QLineEdit::textChanged, and it would not cause any stackoverflow,
since Qt is pretty optimized here, i.e. if text does not change actually (value of QString
remains the same), Qt won't fire the signal. However, it is probably better to use
&QLineEdit::textEdited, since you expect the user to enter the text.
*/
connect(lineEdit, &QLineEdit::textEdited, this, [lineEdit](const QString& text)
{
lineEdit->setText(text.toUpper());
});
In other words, you can achieve the same behavior desired through simple signals and slots mechanism that Qt gives us. If you can achieve what you want through standard framework mechanisms, then you should try this instead of trying to implement event filter which might cause problems you might even be unaware of. Keep in mind that event filter is another mechanism provided by Qt that gives you more freedom to do what you want to do, but also you have to take of much more corner cases.
I got a problem with eventFilter method and I used a simpler solution:
protected:
void keyPressEvent(QKeyEvent* e) override {
if (!e->text().isNull() && !e->text().isEmpty() &&
e->modifiers() == Qt::NoModifier &&
e->key() >= Qt::Key_A && e->key() <= Qt::Key_Z)
{
insertPlainText(e->text().toUpper());
}
else
QPlainTextEdit::keyPressEvent(e);
}
I am using CodeEditor class from Qt examples which inherits from QPlainTextEdit.
At this point I'm in a dilemma to when to emit a signal vs calling a method in another class directly (same thread). For example, in the tutorial I'm doing I'm connecting the NotifyConnected signal of the Instrument class (Model) to the onConnected slot of 'this' aka The View Manager, refer to SetupViewManager::WireButtons(), third line in code. (I'm using MVVM design pattern). Here signals and slots makes sense as the Instruments class (Model) should not know anything about the View Manager. (i.e. Passing a reference of the view manager to the model is a no no as it would break the MVVM design pattern.) Brilliant.
The issue I have is that next in the tutorial the onConnected slot of the ViewManager then emits other signals which then I have to proceed to manually connect to the slots of another View class i.e. SetupTab (ref void SetupViewManager::onConnected and void SetupViewManager::WireDisplayUpdate() in code).
My question is, why not just replace all the emits in the onConnected slot with calling the methods directly of SetupTab? Feels like over-complicating code to me.
What is the advantage of going the extra mile to emit signals and having to wire up everything just to simply call a public function (signal) from another class which i have a reference for? It's not a multi-threaded application (I know signals and slots are thread safe).
Please enlighten me.
Thanks.
setupviewmanager.cpp:
#include "setupviewmanager.h"
#include "View/setuptab.h"
#include "Model/instrument.h"
#include "Model/settings.h"
#include "utils.h"
namespace Ps
{
SetupViewManager::SetupViewManager(QObject *parent,
SetupTab &tab,
Instrument &inst,
Settings &config) :
QObject(parent),
m_setupTab(tab),
m_instrument(inst)
{
WireSettings(config);
config.ParseJsonData();
WireHostAndPort();
WireMessages();
WireButtons();
WireDisplayUpdate();
m_setupTab.SetHostName(config.getHostName());
m_setupTab.SetPort(config.getPortNumber());
m_setupTab.SetCommands(config.getCommandsAsModel());
auto long_wait = config.getLongWaitMs();
auto short_wait = config.getShortWaitMs();
m_instrument.SetlongWaitMs(long_wait);
m_instrument.SetShortWaitMs(short_wait);
emit NotifyStatusUpdated(tr("Long wait Ms: %1").arg(long_wait));
emit NotifyStatusUpdated(tr("Short Wait Ms: %1").arg(short_wait));
onDisconnected();
}
SetupViewManager::~SetupViewManager()
{
Utils::DestructorMsg(this);
}
void SetupViewManager::WireSettings(Settings &config)
{
connect(&config, &Settings::NotifyStatusMessage, &m_setupTab, &SetupTab::onStatusUpdated);
}
void SetupViewManager::WireHostAndPort()
{
connect(&m_setupTab, &SetupTab::NotifyHostNameChanged, &m_instrument, &Instrument::onHostNameChanged);
connect(&m_setupTab, &SetupTab::NotifyPortChanged, &m_instrument, &Instrument::onPortChanged);
}
void SetupViewManager::WireMessages()
{
connect(&m_instrument, &Instrument::NotifyErrorDetected, &m_setupTab, &SetupTab::onStatusUpdated);
connect(&m_instrument, &Instrument::NotifyStatusUpdated, &m_setupTab, &SetupTab::onStatusUpdated);
connect(this, &SetupViewManager::NotifyStatusUpdated, &m_setupTab, &SetupTab::onStatusUpdated);
}
void SetupViewManager::WireButtons()
{
connect(&m_setupTab, &SetupTab::NotifyConnectClicked,&m_instrument, &Instrument::Connect);
connect(&m_instrument, &Instrument::NotifyConnected, &m_setupTab, &SetupTab::onConnected);
connect(&m_instrument, &Instrument::NotifyConnected, this, &SetupViewManager::onConnected);
connect(&m_setupTab, &SetupTab::NotifyDisconnectClicked,&m_instrument, &Instrument::Disconnect);
connect(&m_instrument, &Instrument::NotifyDisconnected, &m_setupTab,&SetupTab::onDisconnected);
connect(&m_instrument, &Instrument::NotifyDisconnected, this, &SetupViewManager::onDisconnected);
connect(&m_setupTab, &SetupTab::NotifySendClicked,&m_instrument, &Instrument::onSendRequest);
connect(&m_instrument, &Instrument::NotifyDataSent,&m_setupTab, &SetupTab::onDataSent);
connect(&m_setupTab, &SetupTab::NotifyReceiveClicked,&m_instrument, &Instrument::onReceiveRequest);
connect(&m_instrument, &Instrument::NotifyDataReceived,&m_setupTab, &SetupTab::onDataReceived);
}
void SetupViewManager::WireDisplayUpdate()
{
connect (this, &SetupViewManager::NotifyConnectEnabled, &m_setupTab, &SetupTab::onConnectEnabled);
connect (this, &SetupViewManager::NotifyDisconnectEnabled, &m_setupTab, &SetupTab::onDisconnectEnabled);
connect (this, &SetupViewManager::NotifyDirectCommandsEnabled, &m_setupTab, &SetupTab::onDirectCommandsEnabled);
connect (this, &SetupViewManager::NotifyControlTabEnabled, &m_setupTab, &SetupTab::onControlTabEnabled);
}
void SetupViewManager::onConnected()
{
emit NotifyConnectEnabled(false); // HERE. Why not just call method directly with m_setupTab.onConnectEnabled(false); etc...?
emit NotifyDisconnectEnabled(true);
emit NotifyDirectCommandsEnabled(true);
emit NotifyControlTabEnabled(true);
}
void SetupViewManager::onDisconnected()
{
emit NotifyConnectEnabled(true);
emit NotifyDisconnectEnabled(false);
emit NotifyDirectCommandsEnabled(false);
emit NotifyControlTabEnabled(false);
}
}
Advantages of signal-slot mechanism:
easy to use when your class has no information about it's clients;
may be used for thread-safe calls;
you must not manually remember all objects to notify them;
the only rule to connect two objects is that they both must be QObject subclasses.
Disadvantages:
slower call (each signal emit scans list of all connected objects);
possibly complicated spaghetti-code; you don't know, who and when will call any slot or who will get emitted signal.
You should think yourself about your case. If there is no signal "listeners" outside the SetupViewManager, try direct calls. If someone else can connect to this signals, your choice is emitting them.
There also may be other reasons to use signals. But there is no reason to use them just to call a function. In one thread, at least.
Signals and slots are used to decouple classes so that they don't need to explicitly know who uses their functionality and how. In many cases, decoupling is a desirable trait of the design of your software. Of course it's not an end in itself, it's useful when it helps you to reason about the code's correctness and makes it easier to maintain. Decoupling help in understanding/reasoning about the code as it leads to smaller units of code that you can analyze in isolation. Another way to look at it is separation of concerns: let one unit of code do one thing, e.g. focus one class on one aspect of functionality.
When you have a pair of classes and wish to decide whether to couple them or not, think of whether they could be used with other classes. A could be coupled to B, but could the interface that couples the pair be used by C instead of B? If so, then some decoupling pattern must be used, and signal-slot pattern is one of them.
For example, let's compare how these two interfaces affect coupling with user code. The objective is simple: add debug output to an object's destructor:
class QObject {
...
Q_SIGNAL void destroyed(QObject * obj = Q_NULLPTR);
};
class QObjectB {
...
virtual void on_destroyed();
};
int main() {
QObject a;
struct ObjectB : QObjectB {
void on_destroyed() override { qDebug() << "~QObjectB"; }
} b;
QObject::connect(&a, &QObject::on_destroyed, []{ qDebug() << "~QObject"; });
}
The signal-slot interface allows you to easily add functionality to existing objects without a need to subclass them. It is a particularly flexible implementation of the Observer pattern. This decouples your code from the code of the objects.
The second implementation, using the template method lookalike pattern, forces a closer coupling: to act on ObjectB's destruction, you must have an instance of a derived class where you implement the desired functionality.
I am writing an application for arm board. I have a mainwindow and I need to get the touch co-ordinates for single touch. Due to lack of Qt knowledge I am facing a problem of getting the touch event.
As per the QTouchEvent Class document, I have declared a slot as below.
#include <QTouchEvent>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(int x, int y, QWidget *parent = 0);
public slots:
void touchEvent(QTouchEvent *ev);
};
and in mainwindow.cpp I redefine it as below. The FFLabel[1] is already declared by me and I am writing it to confirm if the event is received.
void MainWindow::touchEvent(QTouchEvent *ev)
{
ui->FFLabel[1]->setText(QString("Event"));
switch (ev->type())
{
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
{
}
}
}
I am not able to get the touchEvent. Can someone please help me.
I have verified the /dev/input/event2 and am receiving the event there.
QWidget has a number of protected virtual members, e.g., QWidget::mousePressEvent. They are called when the event occurs.
Usually one overrides such a function to run some code when a certain event happens.
The problem is, that, unlike for mouse press, there is no such virtual function QWidget::touchEvent (as of Qt 5.12). That is, you can't override it and it cannot be called when a touch event happens as QWidget is not aware of such a method.
C++11 introduced the override-keyword. If you had used it, you would have noticed that issue.
In order to receive touch events, you must override the more general QWidget::event method. Then filter for the events you're actually interested in:
bool MainWindow::event(QEvent* event)
{
switch (event->type()) {
case QEvent::TouchBegin:
qDebug() << "touch!";
return true;
default:
// call base implementation
return QMainWindow::event(event);
}
}
Note that you must state setAttribute(Qt::WA_AcceptTouchEvents); in order to receive any touch events (put it in the MainWindow-constructor).
You can read more details in the docs.
Events aren't slots. Declare it as a protected function.
protected:
void touchEvent(QTouchEvent *ev);
Edit: And you didn't put anything in your switch statement to show if you get the event.
void MainWindow::touchEvent(QTouchEvent *ev)
{
switch (ev->type())
{
case QEvent::TouchBegin:
ui->FFLabel[1]->setText("Event began.");
break;
case QEvent::TouchEnd:
ui->FFLabel[1]->setText("Event ended.");
break;
case QEvent::TouchUpdate:
{
ui->FFLabel[1]->setText("Event updated.");
break;
}
}
}
Edit: If it's still not working, it means your mainwindow isn't accepting touch events. You need to do this in your mainwindow's constructor:
setAttribute(Qt::WA_AcceptTouchEvents, true);
Disclaimer:
I had a similar issue and this is not a solution but may be a workaround depending on what you want to do.
In my case, the touch events were interpreted as mouse events so, MainWindow::touchEvent was never called but, the mouse events handler were.
I used MainWindow::mousePressEvent to get the touch point and MainWindow::mouseMoveEvent to get the drag.
This does not handle the multi-touch (it works for the separate contact points but you will have only one move event for all your points)
From Qt5.4, one may try (I didn't test as I'm stuck with Qt4), in the mouse events handler, to check the MouseEventSource If it's MouseEventSynthesizedBySystem it means from the documentation that:
the mouse event was synthesized from a touch event by the platform.
One can find more details checking this solution provided by Chirag Sachdeva in this post
And of course, with regards to the OP code, as user4516901 said, events aren't slots.
My intent is create a context menu to copy the cell content to the clipboard. With the help of sender() I’m able to connect the same function to two different QTableWidget. Everything works, except for this error message:
"QObject::connect: Incompatible sender/receiver arguments
QAction::triggered(bool) --> MainWindow::copyToClipboard(QTableWidget*,int,int)"
This is the part of code that generates the error
void MainWindow::ProvideContextMenu(const QPoint& pos) // this is a slot
{
QTableWidget *tw = (QTableWidget *)sender();
int row = tw->currentRow();
int col = tw->currentColumn();
QMenu menu;
menu.addAction(QString("Test Item"), this,
SLOT(copyToClipboard(QTableWidget *, int,int)));
menu.exec(tw->mapToGlobal(pos));
}
void MainWindow::copyToClipboard(QTableWidget *tw, int row, int col) {
clipboard = QApplication::clipboard();
clipboard->setText(tw->item(row, col)->text());
}
I've been looking in the official documentation for hours, but found nothing about this. There is a solution?
From the documentation:
The signals and slots mechanism is type safe: The signature of a signal must match the signature of the receiving slot. (In fact a slot may have a shorter signature than the signal it receives because it can ignore extra arguments.) Since the signatures are compatible, the compiler can help us detect type mismatches when using the function pointer-based syntax. The string-based SIGNAL and SLOT syntax will detect type mismatches at runtime.
This is the culsprit:
menu.addAction(QString("Test Item"), this,
SLOT(copyToClipboard(QTableWidget *, int,int)));
You cannot have non-matching signal-slot parameters like that. You can only connect slots that have no parameters or one boolean to the triggered(bool) signal. You have to reconsider your design.