how to use connect with member function - qt

I want to connect the tabBarDoubleClicked signal with a member function but the compiler keeps barking at me with:
/home/ron/src/kterminal/sessionstack.cpp:79:56: error: invalid use of non-static member function
this, SessionStack::editTabLabel(session->id()));
^
and I'm not sure how to fix it, my code looks like:
int SessionStack::addSession(Session::SessionType type)
{
Session* session = new Session(type, this);
connect(session, SIGNAL(titleChanged(int,QString)), this, SIGNAL(titleChanged(int,QString)));
connect(session, SIGNAL(terminalManuallyActivated(Terminal*)), this, SLOT(handleManualTerminalActivation(Terminal*)));
connect(session, SIGNAL(activityDetected(Terminal*)), m_window, SLOT(handleTerminalActivity(Terminal*)));
connect(session, SIGNAL(silenceDetected(Terminal*)), m_window, SLOT(handleTerminalSilence(Terminal*)));
connect(session, SIGNAL(destroyed(int)), this, SLOT(cleanup(int)));
m_sessions.insert(session->id(), session);
QString tab_label = QString("Shell (") + QString::number(session->id(), 16) + ")";
addTab(session->widget(), tr(qPrintable(tab_label)));
emit sessionAdded(session->id());
raiseSession(session->id());
connect(this, tabBarDoubleClicked,
this, SessionStack::editTabLabel(session->id()));
return session->id();
}
void SessionStack::editTabLabel(int tabIndex)
{
How can I get editTabLabel() invoked when the tab is double clicked?
EDIT1
In my header I have the following declared:
class SessionStack : public QTabWidget
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kterminal")
public:
explicit SessionStack(QWidget* parent, QWidget* window);
~SessionStack();
private slots:
void tabBarDoubleClicked(int index);
void editTabLabel(int tabIndex);
};

the error occurs because you just forgot to add SIGNAL and SLOT macros at
connect(this, tabBarDoubleClicked, this, SessionStack::editTabLabel(session->id()));
You can't pass session->id() while connecting, you can only pass the argument when you emit the signal.
Please note that your code is not going to connect anyway in runtime since the signal misses an argument (int), so that it matches the editTabLabel slot. So you need to fix that as well.. should be
connect(this, SINGAL(tabBarDoubleClicked(int), this, SLOT(editTabLabel(int)));
if the signal "tabBarDoubleClicked" is builtin and you can't change it to add an argument then you might consider QSignalMapper http://doc.qt.io/qt-5/qsignalmapper.html

Related

QfileSystemWatcher doesn't work

This is how I am initializing the watcher:
QFileSystemWatcher watcher;
bool isWatched = watcher.addPath("../stylesheets/main.style");
if (isWatched) qDebug() << "Stylesheet is being watched.";
connect(&watcher, &QFileSystemWatcher::fileChanged, this, &PCLViewer::updateStyle );
But my update style function never gets called, when I modify, delete or rename the file! I've also tried connecting the slots and signals like this:
connect(&watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateStyle(QString)) );
The signature for the updateStyle functions is this:
public slots:
void updateStyle(const QString &path);
I'm using ubuntu.
If anyone is having the same problem.
Try this:
QFileSystemWatcher *watcher = new QFileSystemWatcher();
bool beingWatched = watcher->addPath("Enter your path here");
if (beingWatched ) qDebug() << "Being watched";
QObject::connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(handleFileChanged(QString)));
I don't know why but you need to create a pointer, at least on my system, to emit a signal or it won't work.
The slot function signature was wrong. I had to use void updateStyle (QString path); for it to get called.

Qt and "No such slot" error

I wrote the class and add a slot:
class graphShow : public QObject {
Q_OBJECT
public:
graphShow(){}
public slots:
void upd(QGraphicsScene &S);
};
Implementation of graphShow::upd is here:
void graphShow::upd(QGraphicsScene &S) {
QGraphicsTextItem* pTextItem = S.addText("Test");
pTextItem->setFlags(QGraphicsItem::ItemIsMovable);
}
Connection:
graphShow gr;
QPushButton* p1 = new QPushButton("Show");
/*...*/
QObject::connect(p1,SIGNAL(clicked()),&gr,SLOT(upd(&scene);));
During compiling I have no errors but when program starts I see this message:
Object::connect: No such slot graphShow::upd(&scene); in main.cpp:93
What am I doing wrong?
You need to set up connection in the following way:
QObject::connect(p1, SIGNAL(clicked()), &gr, SLOT(upd(QGraphicsScene &)));
However this also may not wark, because Qt docs state:
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.)
By the way, you doing it wrong. You could not connect signal without arguments to slot with argument. For your case you should use QSignalMapper.

Work around for signal and slot argument limitation

I want to make a PushButton when it is clicked, its text change into 'clicked'. I tried it by
connect(button1, SIGNAL(clicked()), this, SLOT(markClicked(button1)));
where this refer to the MainWindow and
void MainWindow::markClicked(QPushButton *button) { button->setText("Clicked"); }
It does not seem to work because I think SLOT cannot take more arguments than SIGNAL. If there any approach to work around this limitation?
Thanks.
Qt signals/slots mechanism can only transfer signal to slot function with similar parameters. As a workaround, you should use QSignalMapper:
QSignalMapper mapper;
...
connect(button1, SIGNAL(clicked()), &mapper, SLOT(map()));
mapper.setMapping(button1, button1); // not sure whether this is mandatory or not
...
connect(&mapper, SIGNAL(mapped(QWidget*)), this, SLOT(markClicked(QWidget*)));
and function markClicked is
void MainWindow::markClicked(QWidget *widget) {
QPushButton *button = qobject_cast<QPushButton*>(widget);
button->setText("Clicked");
}
The other way you could do this is to use a default value for the argument and then use the sender() method:
In MainWindow:
void markClicked(QPushButton *button = NULL);
then:
connect(button1, SIGNAL(clicked()), this, SLOT(markClicked()));
and:
void MainWindow::markClicked(QPushButton *button) {
if (button==NULL) { button = qobject_cast<QPushButton*>(sender()); }
button->setText("Clicked");
}

How to delete a QProcess instance correctly?

I have a class looking like this:
class FakeRunner : public QObject
{
Q_OBJECT
private:
QProcess* proc;
public:
FakeRunner();
int run()
{
if (proc)
return -1;
proc = new QProcess();
QStringList args;
QString programName = "fake.exe";
connect(comp, SIGNAL(started()), this, SLOT(procStarted()));
connect(comp, SIGNAL(error(QProcess::ProcessError)), this,
SLOT(procError(QProcess::ProcessError)));
connect(comp, SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(procFinished(int, QProcess::ExitStatus)));
proc->start(programName, args);
return 0;
};
private slots:
void procStarted() {};
void procFinished(int, QProcess::ExitStatus) {};
void procError(QProcess::ProcessError);
}
Since "fake.exe" does not exist on my system, proc emits the error() signal. If I handle it like following, my program crashes:
void FakeRunner::procError(QProcess::ProcessError rc)
{
delete proc;
proc = 0;
}
It works well, though, if I don't delete the pointer. So, the question is how (and when) should I delete the pointer to QProcess? I believe I have to delete it to avoid a memory leak. FakeRunner::run() can be invoked many times, so the leak, if there is one, will grow.
Thanks!
You can't delete QObject instance inside slot which is connected to a signal in this instance using normal delete operator. This is due to the fact that if signal and slot connected using direct connection then the slot actually called from the signal implementation made by moc. This is like attempt to delete this; from inside the member of a class. There is a solution QObject::deleteLater(). Object will be deleted by Qt event loop inside events processing function. So you need to call proc->deleteLater() in your case.
And you don't need to disconnect signal from slot since Qt do it automatically when QObject is deleted.

Qt signal to specific object's slot

I would like to know which of the following is the proper way of doing thing with signal/slot in Qt.
I need a way to have multiple instance of a Dialog, i.e: A and B. And I need to tell A to print "A" and B to print "B" from a different thread. So I believe I need something like either:
OPTION 1) A->print("A") and B->print("B")
or is it better to do:
OPTION 2) emit print("A") and emit print("B") and use a way that I don't know so only A catch the "A" and only B catch the "B".
I got the option 1 working like this:
class myClass : public QMainWindow
{
Q_OBJECT
public:
myClass (QWidget *parent = 0, Qt::WFlags flags = 0);
~myClass ();
void doPrint(char* text)
{
emit mySignal(text);
}
private:
Ui::myClass ui;
public slots:
void newLog(char* msg);
signals:
void mySignal(char* msg);
};
myClass::myClass(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags)
{
ui.setupUi(this);
connect(this, SIGNAL(mySignal(char*)), this, SLOT(newLog(char*)));
}
void myClass::newLog(char* msg)
{
ui.textEdit->append(msg);
}
and then all I have to do is:
myClass* instanceA = new myClass();
myClass* instanceB = new myClass();
instanceA->doPrint("A");
instanceB->doPrint("B");
is this right?
Thanks!
Since your slot is in another thread, you have to use the Meta-Object System to invoke the method asynchronously. The proper way to do this is to use QMetaObject::invokeMethod
DO NOT subclass QThread and override the run method. For details on this see: https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong
void otherClass::printTo(myClass* instance, char* text)
{
QMetaObject::invokeMethod(instance, // pointer to a QObject
"doPrint", // member name (no parameters here)
Qt::QueuedConnection, // connection type
Q_ARG(char*, text)); // parameters
}
void myClass::doPrint(char* text)
{
ui.textEdit->append(text);
}
myClass* instanceA = new myClass();
myClass* instanceB = new myClass();
printTo(instanceA, "A");
printTo(instanceB, "B");
If the char* type hasn't been registered with the Meta-Object System yet, do so with
Q_DECLARE_METATYPE(char*);
then:
qRegisterMetaType<char*>("charPtr");
In this simplified example, I think you are on the correct path with option 1. However, it would be even better if you didn't need the doPrint() method, which would also eliminate the need for the mySignal signal (at least in myClass). Instead, I would suggest inheriting your threads from QThread if the aren't already, and doing something like this:
class myThread : public QThread
{
Q_OBJECT
public:
myThread (QWidget *parent = 0 ) : QThread(parent) {}
~myThread () {}
void run(char* text)
{
emit mySignal(text);
}
signals:
void mySignal(char* msg);
};
Then you need to do something like this:
myClass* instanceA = new myClass();
myThread* threadA = new myThread();
connect(threadA, SIGNAL(mySignal(char*)), instanceA, SLOT(newLog(char*)), Qt::QueuedConnection);
threadA->run( "A" );
Obviously, in most non-example code, you'd not pass the string into run, but rather generate strings to be run as threadA is running. The advantage is that this keeps the thread considerations out of myClass, and you only need to think about them where they are connected. On the flip side, you introduce fewer dependencies into the threads, since they don't need to know about myClass to be able to log.

Resources