Qt No matching function for call to mainWindow::connect() - c++

I'm trying to connect a combo box value and a label such that when the combo box changes the label reflects that. I have googled my heart out trying to find an answer but, as of yet, nothing has worked; I still get the error:no matching function for call to mainWindow::connect(QComboBox*&, const char [38], QString*, const char [26])
I have tried QObject::connect, QWidget::connect and anything else dealing with Qt, but to no avail.
Creating a label that says the combo box value is not my final intention for the program. Rather, I wish to get it working with a simple label then change it to what I want it to display (thus the tempLabel).
mainwindow.h:
class MainWindow : public QMainWindow
{
public:
MainWindow();
private slots:
QString getClass(QComboBox *box);
};
mainwindow.cpp:
MainWindow::MainWindow()
{
QString qMathClassName;
QComboBox* mathClassCombo = new QComboBox;
QLabel* label = new QLabel(qMathClassName);
// omitting layout code...
connect(mathClassCombo, SIGNAL(currentIndexChanged(const QString &)),
&qMathClassName, SLOT(getClass(mathClassCombo)));
}
QString MainWindow::getClass(QComboBox *box)
{
return box->currentText();
}
Any help would be greatly appreciated!

You are connecting a signal to a slot with a different signature. You have to change your slot to something like
getClass(const QString &)
to match currentIndexChanged signal.

I think you need to read Qt's signals and slots documentation. Again, if you've already done so. Pay special attention to their examples.
I think that you had these misconceptions about Qt in C++:
That QLabel takes a reference to a QString, and that it will update its text when that string changes. It won't. QLabel will display the value of the string when you give it the string. That is the only time it will update.
That objects constructed on the stack will not be destroyed at the end of the function. They will not. At the end of the constructor, qMathClassName will be destroyed and any reference to it will become invalid. Thus, you'd not want to make a connection to it, even if you could.
That the third argument of QObject::connect is a pointer to a place to put the return value for the slot. It's not. The third argument is a pointer to the QObject on which to call the slot. The return value of a slot is unused for any calls made to it via QObject::connect.
That you can bind values to slots in your connection. Unfortunately not. Within the SLOT macro, you must put the function signature of the slot. You may not reference any variables. The arguments section must have only class names. That is SLOT(getClass(QComboBox*)), not SLOT(getClass(mathClassCombo)).
The simplest way to ensure the contents of a combo box are displayed in a label are this:
QComboBox* mathClassCombo = new QComboBox;
QLabel* tempLabel = new QLabel;
connect(mathClassCombo, SIGNAL(currentIndexChanged(const QString&)),
tempLabel, SLOT(setText(const QString&)));
If you want to do something more complicated, I recommend just making a slot on your window that can handle those complications. For example:
mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private slots:
void updateLabelText(const QString& className);
private:
QComboBox* mathClassCombo;
QLabel* tempLabel;
}
mainwindow.cpp:
MainWindow::MainWindow()
{
mathClassCombo = new QComboBox;
tempLabel = new QLabel;
// omitting layout code...
connect(mathClassCombo, SIGNAL(currentIndexChanged(const QString&)),
this, SLOT(updateLabelText(const QString&)));
}
void MainWindow::updateLabelText(const QString& className)
{
QString newLabelString = className + " is the best class ever!";
tempLabel->setCurrentText(newLabelString);
}

Related

Passing a QString to a class method of parent with QSignalMapper

I have a parent class with its method to change a labels picture on certain Signals. E.g. When something happens in a QComboBox... (valueChanged, activated)
class parentClass : public QMainWindow
{
Q_OBJECT
...
public slots:
//this is the slot i want to connect to some signal of
//e.g a combo box to change the picture by passed in string
void changePicture(QString fileName);
Then i have this child:
class childClass : public QObject
{
Q_OBJECT
public:
childClass(parentClass *parent, QTabWidget *tab, QStringList *guards=0);
private:
bool readCombo(QXmlStreamReader *xmlreader);
Now inside of readCombo i want to read a string and pass it to change picture:
QString imageFileName = xmlreader->attributes().value("image").toString();
QSignalMapper * signalMapper = new QSignalMapper(parent);
//this is just one of many trials to get this working, i hope you get the picture
connect(combo , SIGNAL(activated(int)), parent, SLOT(changePicture(QString *)));
signalMapper->setMapping(combo, imageFileName);
But this gives me either No such Signal , No such Slot or in the upper case Incompatiple sender/receiver arguments
I would appreciate some help on this one since the syntax is really not being intuitive (imo) and i can't find any good reference that is working for my case (did trial and error a lot before asking)
Some issues with your code.
you really can't connect to a private slot of a QObject
You are creating a new signal mapper every time you call readCombo which you aren't clearing - resulting memleaks.
calling connect multiple times creates multiple connections i.e., you will call the same slot multiple times with single signal.
From your example code, I see that you can solve this either by making the parentClass slot public OR add a signal to the childClass and connect to it in the parentClass.
Other option is change the readCombo like this:
QString imageFileName = xmlreader->attributes().value("image").toString();
parentClass->changePicture(imageFileName);
and your parent class as
class parentClass : public QMainWindow
{
Q_OBJECT
...
public slots:
//this is the slot i want to connect to some signal of
//e.g a combo box to change the picture by passed in string
void changePicture(QString fileName);
I see several errors:
You are connecting to a private slot in parentClass from childClass, you should make it public if you want to connect it.
You connect a signal to a slot with another function signature. Your signal has parameter type int, and slot type QString*. The functions should share the same type of parameters.
In your connect you refer use the parameter type QString* in your slot, but in parentClass the parameter type is QString.
For more information about signals and slots see: Signals & Slots.
PS: your naming of childClass and parentClass is not clear either since they both inherit from QObject. See C++ inheritance.
OK, got it:
QString imageFileName = xmlreader->attributes().value("image").toString();
QSignalMapper * signalMapper = new QSignalMapper(parent);
signalMapper->setMapping(combo, imageFileName);
connect(signalMapper, SIGNAL(mapped(QString)), parent, SLOT(changePicture(QString)));
connect( combo, SIGNAL(activated(int)), signalMapper, SLOT(map()) );

Qt QObject::connect() function cannot be connected

I have the code like this:
class MyListView : public QListView
{
public:
MyListView();
~MyListView();
public slots:
void insertData();
void deleteData();
void showData();
private:
QStringListModel *model;
QListView *listView;
};
And the constructor is like:
MyListView :: MyListView()
{
QStringList data;
data << "Letter A" << "Letter B" << "Letter C";
model = new QStringListModel;
model->setStringList(data);
listView = new QListView;
listView->setModel(model);
/* the three buttons */
QPushButton *insertBtn = new QPushButton(QObject::tr("insert"),this);
QObject::connect(insertBtn,SIGNAL(clicked()),this,SLOT(insertData()));
QPushButton *deleteBtn = new QPushButton(QObject::tr("delete"),this);
QObject::connect(deleteBtn,SIGNAL(clicked()),this,SLOT(deleteData()));
QPushButton *showBtn = new QPushButton(QObject::tr("show"),this);
QObject::connect(showBtn,SIGNAL(clicked()),this,SLOT(showData()));
/* layout */
QHBoxLayout *btnLayout = new QHBoxLayout;
btnLayout->addWidget(insertBtn);
btnLayout->addWidget(deleteBtn);
btnLayout->addWidget(showBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(listView);
mainLayout->addLayout(btnLayout);
setLayout(mainLayout);
}
So I want to connect the push button to the slot functions, but when I compile it, I got the error message as:
QObject::connect: No such slot QListView::insertData()
I think the problem comes from the connect function, in which, "this" is not the right pointer, any help? Thanks in advance.
You need to add the Q_OBJECT macro in your MyListView
From Qt API docs:
Notice that the Q_OBJECT macro is mandatory for any object that
implements signals, slots or properties. You also need to run the Meta
Object Compiler on the source file. We strongly recommend the use of
this macro in all subclasses of QObject regardless of whether or not
they actually use signals, slots and properties, since failure to do
so may lead certain functions to exhibit strange behavior.
So, it should be:
class MyListView : public QListView
{
Q_OBJECT
public:
...
}

MDI window and QSignalMapper basics

First of all my apologies for big looking question but indeed it's not. I’m reading Foundation of qt development book and while reading fourth chapter author tells the basics of MDI window by showing this example :
MdiWindow::MdiWindow( QWidget *parent ) : QMainWindow( parent ) {
setWindowTitle( tr( "MDI" ) );
QWorkspace* workspace = new QWorkspace;
setCentralWidget( workspace );
connect( workspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(enableActions()));
QSignalMapper* mapper = new QSignalMapper( this );
//my problem is in this line
connect( mapper, SIGNAL(mapped(QWidget*)), workspace, SLOT(setActiveWindow(QWidget*)) );
createActions();
createMenus();
createToolbars();
statusBar()->showMessage( tr("Done") );
enableActions();
}
His this para of explanation completely eluded me (is it me or others having problem understanding it too?) :
Next, a signal mapping object called QSignalMapper is created and
connected. A signal mapper is used to tie the source of the signal to
an argument of another signal. In this example, the action of the menu
item corresponding to each window in the Window menu is tied to the
actual document window. The actions are in turn connected to mapper.
When the triggered signal is emitted by the action, the sending action
has been associated with the QWidget* of the corresponding document
window. This pointer is used as the argument in the mapped(QWidget*)
signal emitted by the signal mapping object.
My question : I still don’t get what is signal mapper class, how it’s used and what's functionality it's doing in the example above?. Can anyone please explain the above para using easy terms? also It’d be awesome if you could please teach me about mapper class’s basics with simple example? possibly in layman’s term?
P.S : A confusion is when we have MDI window, do menu changes (though actions are disabled/enabled) e.g suppose for one particular document we have menu “File/close” and for other document we have “File/remaper” ?
The QSignalMapper is used to re-emit signals with optional parameters. In other words (from the documentation):
This class collects a set of parameterless signals, and re-emits them
with integer, string or widget parameters corresponding to the object
that sent the signal.
A good example (also from the doc - take a look at it) is set as follows:
Suppose we want to create a custom widget that contains a
group of buttons (like a tool palette). One approach is to connect
each button's clicked() signal to its own custom slot; but in this
example we want to connect all the buttons to a single slot and
parameterize the slot by the button that was clicked.
So imagine you have a number of buttons encapsulated in a class, say ButtonWidget, with a custom signal void clicked(const QString &text). Here is the definition:
class ButtonWidget : public QWidget {
Q_OBJECT
public:
ButtonWidget(QStringList texts, QWidget *parent = 0);
signals:
void clicked(const QString &text);
private:
QSignalMapper *signalMapper;
};
The constructor could then be defined like the following:
ButtonWidget::ButtonWidget(QStringList texts, QWidget *parent)
: QWidget(parent)
{
signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i) {
QPushButton *button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, texts[i]);
gridLayout->addWidget(button, i / 3, i % 3);
}
connect(signalMapper, SIGNAL(mapped(const QString &)),
this, SIGNAL(clicked(const QString &)));
setLayout(gridLayout);
}
So what happens here? We construct a grid layout and our buttons of type QPushButton. The clicked() signal of each of these is connected to the signal mapper.
One of the forces using QSignalMapper is that you can pass arguments to the re-emitted signals. In our example each of the buttons should emit a different text (due to the definition of our signal), so we set this using the setMapping() method.
Now all that's left to do is map the signal mapper to the signal of our class:
connect(signalMapper, SIGNAL(mapped(const QString &)),
this, SIGNAL(clicked(const QString &)));
Assume we have a testing class called TestClass then ButtonWidget can be used thusly:
TestClass::TestClass() {
widget = new ButtonWidget(QStringList() << "Foo" << "Bar");
connect(widget, SIGNAL(clicked(const QString &)),
this, SLOT(onButtonClicked(const QString &)));
}
void TestClass::onButtonClicked(const QString &btnText) {
if (btnText == "Foo") {
// Do stuff.
}
else {
// Or something else.
}
}
By using the signal mapper this way you don't have to declare and manage all the buttons and their clicked signals, just one signal pr. ButtonWidget.
The buttom line is that the signal mapper is great for bundling multiple signals and you can even set parameters when it re-emits them. I hope that gave some intuition about the usage of QSignalMapper.
Your example code
The explanation (your "para") states that all the actions are each individually mapped to a specific QWidget*. When triggering an action its respective QWidget* will be passed to the slot QWorkspace::setActiveWindow(QWidget*) of workspace, which in turn activates the widget.
Also note that the mapping from action to widget has to happen somewhere in your code. I assume it is done in createActions() or enableActions() perhaps.
A QSignalMapper allows you to add some information to a signal, when you need it. This object internally have a map like QMap<QObject*,QVariant>. Then you connect an object to it, and when the slot is called, it re-emit the signal with the associated value.
Workflow:
mySignalMapper:
[ obj1 -> 42 ]
[ obj2 -> "obiwan" ]
[ obj3 -> myWidget ]
connect(obj1,mySignal,mySignalMapper,SLOT(map())); // idem for obj2 and obj3
(obj2 emits "mySignal")
-> (mySignalMapper::map slot is called)
-> (sender() == obj2, associated data = "obiwan")
-> (mySignalMapper emits mapped("obiwan"))
I was going to add a more detailed example, but Morten Kristensen was faster than me ;)

Don't understand why I can't call setCentralWidget in QMainWindow subclass

//clposter.h
class CLPoster : public QMainWindow
{
Q_OBJECT
public:
CLPoster();
private slots:
QWidget createCentralWidget();
void createActions();
void createMenu();
void createStatusBar();
void loadSavedPosts();
};
//clposter.cpp
CLPoster::CLPoster()
{
setWindowTitle("Craigslist Poster");
QWidget mainWidget = createCentralWidget();
setCentralWidget(mainWidget);
// createActions();
// createMenu();
// createStatusBar();
// loadSavedPosts();
// checkForActionsNeeded(); //May want to break up into more functions
}
The error I'm getting is this:
/usr/include/qt4/QtGui/qwidget.h:: In constructor ‘CLPoster::CLPoster()’:
/usr/include/qt4/QtGui/qwidget.h:787: error: ‘QWidget::QWidget(const QWidget&)’ is private
/home/brett/projects/CLPoster/CLPoster-build-desktop/../CLPoster/clposter.cpp:9: error: within this context
/home/brett/projects/CLPoster/CLPoster-build-desktop/../CLPoster/clposter.cpp:10: error: no matching function for call to ‘CLPoster::setCentralWidget(QWidget&)’
/usr/include/qt4/QtGui/qmainwindow.h:141: candidates are: void QMainWindow::setCentralWidget(QWidget*)
I'm having trouble interpreting the error message. It says there is no matching function call, but it should be inheriting it from QMainWindow. It could just be a lack of understanding C++ more so than QT, first time I've used it, but dunno. Thanks for the help.
All QWidget items must be allocated in the free-store (new) since they all have to have "parents". In Qt a parent will delete its children (with delete). This is why any function returning, accepting, whatever a widget is going to do so on a pointer to a widget, not the widget itself; you need to do the same.
The setCentralWidget function expects that you are sending a pointer to a QWidget (QWidget*), whereas you are trying to send the actual object (or a reference to the object, QWidget&, as implied by the compiler error) in your code. If you create your central widget as a pointer (change the member function to QWidget* createCentralWidget()) and pass the pointer to the setCentralWidget function you should be good to go.
e.g.
QWidget* CLPoster::createCentralWidget()
{
QWidget* w = new QWidget;
// Do stuff..
return w;
}
Then in your constructor, you can just call setCentralWidget(createCentralWidget()). The QMainWindow destructor will ensure that your central widget is deleted.
Don't you need to allocate the Widget on the heap and pass a pointer to setCentralWidget()

How to pass variables to slot methods in QT?

I'm making a little chat messenger program, which needs a list of chat channels the user has joined. To represent this list graphically, I have made a list of QPushButtons, which all represent a different channel. These buttons are made with the following method, and that's where my problem kicks in:
void Messenger::addToActivePanels(std::string& channel)
{
activePanelsContents = this->findChild<QWidget *>(QString("activePanelsContents"));
pushButton = new QPushButton(activePanelsContents);
pushButton->setObjectName("pushButton");
pushButton->setGeometry(QRect(0, 0, 60, 60));
pushButton->setText("");
pushButton->setToolTip(QString(channel.c_str()));
pushButton->setCheckable(true);
pushButton->setChecked(false);
connect(pushButton, SIGNAL(clicked()), this, SLOT(switchTab(channel)));
}
(activePanelContents is a QWidget that holds the list.)
The point is that each button should call the switchTab(string& tabname) method when clicked, including the specific channel's name as variable. This implementation doesn't work though, and I haven't been able to find out how to properly do this.
For strings and integers, you can use QSignalMapper. In your Messenger class, you would add a QSignalMapper mapper object, and your function would look like:
void Messenger::addToActivePanels(std::string& channel)
{
activePanelsContents = this->findChild<QWidget *>(QString("activePanelsContents"));
pushButton = new QPushButton(activePanelsContents);
// ...
connect(pushButton, SIGNAL(clicked()), &mapper, SLOT(map()));
mapper.setMapping(pushButton, QString(channel.c_str()));
}
and after you have added all channels to your active panels, you call
connect(&mapper, SIGNAL(mapped(const QString &)), this, SLOT(switchTab(const QString &)));
Use QSignalMapper to pass variables;
QSignalMapper* signalMapper = new QSignalMapper (this) ;
QPushButton *button = new QPushButton();
signalMapper -> setMapping (button, <data>) ;
connect (signalMapper, SIGNAL(mapped(QString)), this,
SLOT(buttonClicked(QString))) ;
in slot i.e
void class::buttonClicked(QString data){
//use data
// to get sender
QSignalMapper *temp = (QSignalMapper *)this->sender();
QPushButton *btn = (QPushButton *)temp->mapping(data);
// use btn
}
Hope my ans may help you
Don't use the sender method unless you absolutely have to. It ties the function directly to being used only as a slot (can't be called directly). Retain the behavior of having the function accept a string and simply make a mechanism by which you can call it.
One method, among others you might find, is to leverage use of QSignalMapper. It will map objects to values and regenerate signals of the appropriate signature.
I would do it with "relay" objects:
Create TabSwitchRelay which is a sub-class of QObject with this constructor:
TabSwitchRelay::TabSwitchRelay(QObject *parent, Messanger * m, const QString & c)
: QObject(parent), m_messanger(m), m_channel(c)
{
}
It also has a slot clicked():
void TabSwitchRelay::clicked()
{
m_messager->switchTab(m_channel);
}
Now replace the line in your code that does connect with this:
TabSwitchRelay * tabRelay = new TabSwitchRelay(pushButton, this, channel);
connect(pushButton, SIGNAL(clicked()), tabRelay, SLOT(clicked()));
It's not tested but you get teh basic idea.
You could try having your switchTab slot take no argument and use QObject::sender to get the object that sent the signal.
Messenger::switchTab()
{
QObject* sender = this->sender();
QPushButton* button = qobject_cast<QPushButton*>(sender);
if(button)
{
// Do stuff...
}
}
if you're using Qt5, you can do it through lambda:
connect( sender, &Sender::valueChanged, [=](){ myMethod(5); } );
void myMethod(int value)
{
// do stuff
}