I have this code:
QObject::connect(lineEdit_1, SIGNAL(textChanged(const QString &)), MainWindow, SLOT(myMethod(const QString &, QLineEdit* )) );
This code works correctly when myMethod has only the first argument (equal to the SIGNAL) but I need to pass a pointer lo lineEdit_1 in order to allow myMethod to know on which LineEdit it has to operate.
What I should to do?
Thanks a lot.
It is not necessary that you send as an additional argument the object that emits the signal for it, the QObjects have the sender() method that allows us to obtain that object:
QObject::connect(lineEdit_1, &QLineEdit::textChanged, MainWindow, &Your_Class::myMethod);
void Your_Class::MyMethod(const QString & text){
if(QLineEdit *le = qobject_cast<QLineEdit *>(sender())){
qDebug() << le;
}
}
If you need to pass other arguments you can use the lambda functions but always take the time to see the limitations (how to use it depends on the context):
QObject::connect(lineEdit_1, &QLineEdit::textChanged, [ /* & or = */](const QString & text){
MainWindow->MyMethod(text, another_arguments);
});
Related
I have a QAction which when clicked emits a triggered signal which calls the slot method RegroupEtudes::OnNouvelleEtude
connect(ui.actionNouvelle_tude, &QAction::triggered, this, &RegroupEtudes::OnNouvelleEtude);
It is possible for void RegroupEtudes::OnNouvelleEtude() to take a QString parameter if I change its definition. But how can i have QAction::triggered give its text QString parameter to void RegroupEtudes::OnNouvelleEtude() ?
That is, if my QAction is named "etude 1", is there anyway that RegroupEtudes::OnNouvelleEtude(QString) gets called as RegroupEtudes::OnNouvelleEtude("etude 1") ?
Since Qt5, you can use a lambda expression and the new connect synthax to pass an extra parameter to your slot:
QAction *actionNouvelle_tude = new QAction(tr("Nouvelle étude..."), this);
connect(actionNouvelle_tude, &QAction::triggered, this, [openAct, this]() {
this->OnNouvelleEtude(openAct->text());
});
It will call OnNouvelleEtude with the text of your action as parameter
I realized downloading multiple files, but I don't know how to implement the total progress bar of the download, that is common.
My code:
QNetworkAccessManager manager;
QList<QNetworkReply *> currentDownloads;
void MainWindow::checkUpdate()
{
QStringList files;
files << "http://cavexp.net/uploads/game/Theugry/zips/resourcepacks.zip"
<< "http://cavexp.net/uploads/game/Theugry/zips/resourcepacks.zip";
doDownload(files);
}
void MainWindow::doDownload(const QVariant& v)
{
if (v.type() == QVariant::StringList) {
foreach (QString url, v.toStringList()) {
QNetworkReply* reply = manager.get(QNetworkRequest(QUrl(url)));
connect(&manager, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(updateDownloadProgress(qint64, qint64)));
currentDownloads.append(reply);
}
}
}
void MainWindow::downloadFinished(QNetworkReply *reply)
{
currentDownloads.removeAll(reply);
reply->deleteLater();
}
void MainWindow::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes)
{
ui->progressBar->setMaximum(totalBytes);
ui->progressBar->setValue(bytesRead);
}
I would be grateful for any help and hints! Thank you.
You probably need to iterate over your 'currentDownloads' list and connect to each one's signal downloadProgress. Then your slot(s) will be called from all of them. In that slot(s) you'll have to sum up all information coming as parameters of QNetworkReply::downloadProgress signal.
You can create a dedicated object for each QNetworkReply instance of your currentDownloads list so that you know from to which file a coming signal belongs, but if I am not mistaking you can also use single slot for all of them and then there is a meta function in Qt that will tell you from which sender the signal came.
P.S. In response to your request for small example here is "straight-forward" approach (without using QSignalMapper or QObject::sender()):
Implement a class "ProgressListenner" something like this (beware I am writing pseudo-code and you'll need to add/fix some necessarily stuff to make it actually working):
class ProgressListenner
{
public:
ProgressListenner() : _lastKnownReceived(0), _lastKnownTotal(0){}
qint64 _lastKnownReceived;
qint64 _lastKnownTotal;
slots:
onDownloadProgress ( qint64 bytesReceived, qint64 bytesTotal )
{
_lastKnownReceived = bytesReceived;
_lastKnownTotal = bytesTotal;
}
}
Than after your line QList<QNetworkReply *> currentDownloads; add QList<ProgressListenner*> downloadListenners;. Inside your foreach each time you are adding new QNetworkReply object to currentDownloads also:
1. create new instance of ProgressListenner and add it to downloadListenners.
2. connect signal of that particular QNetworkReply to that corresponding ProgressListenner's slot: connect(reply, SIGNAL(downloadProgress(qint64, qint64)), pListenner, SLOT(onDownloadProgress (qint64, qint64)));
This way every time some QNetworkReply will fire it's progress signal, slot of corresponding ProgressListenner will be called.
Next step is sum up numbers from all downloads. One simple way is:
1. Create one more function in ProgressListenner class and make it static (important). Let say the name of function is CommonProgress.
2. At the end of onDownloadProgress function call also call CommonProgress
3. In CommonProgress function (taking care about thread safety!) iterate over all elements of downloadListenners and sum up their _lastKnownReceived and _lastKnownTotal. Do the necessarily arithmetic... Don't forget that bytesTotal can be -1!!!
Can QRegExpValidator be used with QTextEdit widget ?
I tried to implement through setValidator() and also set also the qtextedit as parent object. But its not working.
You should use
virtual QValidator::State QRegExpValidator::validate(QString & input, int & pos) const or bool QRegExp::exactMatch(const QString & str) const by yourself. It should not be hard, you just need to determine where to start validate.
You can do the following things
define another slot that will be called when textChanged() signal is
emitted
emit a signal with two parameters(data in qtextedit and
length of same data)
connect the validate() slot with the above slots
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 ;)
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
}