How to pass a widget variable to slot? - qt

I am a QT beginner and I am writing a GUI app that has two QPushButtons and two QTextEdits. When button 1 is clicked, I want only QTextEdit 1 to show something, when button 2 is clicked, I want only QTextEdit 2 to show something, below is my code, but it doesn't work because it seems that I cannot pass parameters to slot method....
class EventProcessor: public QObject {
Q_OBJECT
public slots:
void PopulateEditTest(QTextEdit *textEdit, QString text)
{
textEdit->setText(text);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *window = new QWidget;
QTextEdit *result1 = new QTextEdit();
QTextEdit *result2 = new QTextEdit();
QPushButton *btFacility1 = new QPushButton("Facility 1");
QPushButton *btFacility2 = new QPushButton("Facility 2");
EventProcessor eventprocessor;
QObject::connect(btFacility1, SIGNAL(clicked()), &eventprocessor, SLOT(PopulateEditTest
(result1, "textEdit1")));
QObject::connect(btFacility2, SIGNAL(clicked()), result, SLOT(PopulateEditTest
(result2, "textEdit2")));
..........
return app.exec();
}
I am wondering if there is a similar way to get this working? Thank you for your answer.

You are correct in that you cannot specify arguments to be passed to a slot when you call QObject::connect. All you are providing are function definitions, not function calls.
The simplest way to accomplish what you're trying to do would be to add a couple of slots to your event processor class:
class EventProcessor: public QObject {
Q_OBJECT
public slots:
void PopulateEditTest(QTextEdit *textEdit, QString text) { ... }
void button1Clicked() {
PopulateEditTest(dynamic_cast<QTextEdit*>(sender()), "textEdit1");
}
void button2Clicked() {
PopulateEditTest(dynamic_cast<QTextEdit*>(sender()), "textEdit1");
}
};
...
QObject::connect(btFacility1, SIGNAL(clicked()), &eventprocessor, SLOT(button1clicked()));
QObject::connect(btFacility2, SIGNAL(clicked()), &eventprocessor, SLOT(button2Clicked()));
There are various reasons why it is generally considered to be "evil" to use the QObject::sender() function in this manner, but it is the simplest way to accomplish what you're trying to do without rewriting all of your code, which would be pointless me of to do without knowing what your long term goals are.

Related

QTextEdit refresh after setText()/insertPlainText()

I have a QTextEdit widget in a private slot, which I update regularly with setText() and insertPlainText().
I have found that setText()/insertPlainText() does not update the QTextEdit widget immediately. Instead, the QTextWidget is updated when the slot function returns. To test this, I have put a sleep() just after the setText()/insertPlainText().
class MyWindow : public Widget
{
MyWindow()
{
my_button = new QPushButton(this);
my_edit = new QTextEdit(this);
connect(my_button,
&QPushButton::clicked,
this,
&MyWindow::my_callback);
}
private slots:
void my_callback()
{
my_edit->setText("sample text");
// nothing happens; the QTextEdit
// widget does not show "sample text"
sleep(10);
// the QTextEdit widget will show
// "sample text" AFTER the sleep,
// when my_callback returns.
}
private:
QPushButton* my_button;
QTextEdit* my_edit;
}
This is a problem for me because I need to print a message in my QTextEdit widget BEFORE launching a time-consuming process (using QProcess). Currently, this message is not being printed until after QProcess process has returned.
Does anyone know how I can get the QTextEdit widget to show its contents right after setText()/insertPlainText()?
Using Qt5 on Fedora 29.
Never execute a task that consumes a lot of time in the GUI thread. In general, the solution is to execute that task in another thread, but in your case it indicates that you use QProcess, so I assume that you are using one of the methods waitForFinished(), waitForStarted() or waitForReadyRead(), instead you should use the signals:
#include <QtWidgets>
class Widget: public QWidget{
Q_OBJECT
public:
Widget(QWidget *parent=nullptr):
QWidget(parent)
{
button.setText("Press me");
QVBoxLayout *lay = new QVBoxLayout{this};
lay->addWidget(&button);
lay->addWidget(&textedit);
connect(&button, &QPushButton::clicked, this, &Widget::onClicked);
connect(&process, &QProcess::readyReadStandardError, this, &Widget::onReadyReadStandardError);
connect(&process, &QProcess::readyReadStandardOutput, this, &Widget::onReadAllStandardOutput);
}
private Q_SLOTS:
void onClicked(){
textedit.setText("sample text");
process.start("ping 8.8.8.8");
}
void onReadyReadStandardError(){
textedit.append(process.readAllStandardError());
}
void onReadAllStandardOutput(){
textedit.append(process.readAllStandardOutput());
}
private:
QPushButton button;
QTextEdit textedit;
QProcess process;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
I wonder if calling
QCoreApplication::processEvents()
right after the ->setText("sample text") would do the trick in your case.

How can I connect a signal to QWidget::update slot?

I am using Qt5 with new signal/slot syntax.
I don't know why the following code doesn't work:
QWidget *widget = new QWidget();
connect(pipeline, &Pipeline::NewFrame, widget, &QWidget::update);
I get the error:
no matching member function for call to 'connect' why?
Pipeline class inherits from QObject and NewFrame signal has the same signature as QWidget::update
class Pipeline
: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Pipeline)
public:
Pipeline(QObject *parent);
signals:
void NewFrame();
};
I am using QtCreator on Arch Linux with g++.
TL;DR: The pipeline should be signaling an image, and the widget should have a SetImage method:
class Pipeline : public QObject {
Q_OBJECT // important
public:
Q_SIGNAL void NewFrame(const QImage &);
...
};
class Viewer : public QWidget {
Q_OBJECT // important
QImage m_image;
public:
Q_SLOT void SetImage(const QImage &image) {
m_image = image;
update();
}
...
};
This is how you'd be using it - note that Viewer knows nothing about Pipeline, because it shouldn't: it just shows new frames, wherever they come from.
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Pipeline pipeline;
Viewer viewer;
QObject::connect(&pipeline, &Pipeline::NewFrame, &viewer, &Viewer::SetImage);
return app.exec();
}
Connecting anything to QWidget::update directly, especially from external sources, is usually a sign of bad design.
To satisfy your curiosity, you can use a lambda or qOverload to specify what you're connecting to, to fix the very error you're seeing - caused by ambiguity of the the type of the method pointer. Any of the following will work:
connect(…, widget, qOverload<>(&QWidget::update));
or
auto constexpr update = qOverload<>(&QWidget::update));
connect(…, widget, update);
or
connect(…, widget, [widget]{ widget->update(); });

signal points to slot in struct

I'd like a signal to be connected to a slot inside of a struct. My struct looks like:
//Header file
struct someStruct {
public:
int index;
public slots:
void someSlot();
};
QList<someStruct*> mListOfStructs;
and then I create a button that should forward its clicked() signal to the someSlot function.
//Source file
QPushButton *cmd = new QPushButton();
grd->addWidget(cmd, 3, 2, Qt::AlignCenter);
//grd is a QGridLayout somewhere inside the gui. I can see it and also the button.
now connection the clicked() event with the slot inside a specific struct does not work.
connect(cmd, SIGNAL(clicked()), mListOfStructs[3], SLOT(someSlot()));
some sources tell me that I have to add a metaObject or sth. I tried but it didn't work out. Maybe you know better.
I might use How to connect in Qt signal and slot in dynamically added buttons to get in slot index of added button? as workaround though.
your structure need the Q_Object meta attributes in order to emit signals and recieve slot events...
struct testStruct : public QObject
{
Q_OBJECT
public:
int index;
testStruct():index(0){}
public slots:
void someSlot()
{
qDebug() << "slot called!";
}
};
after that you can conected as usual:
testStruct *ts= new testStruct;
connect(this, SIGNAL(someSignal()), ts, SLOT(someSlot()));

How to connect signal and slot in different classes in Qt?

I have two simple classes(class A and class B).
In a.h, I just declared a QPushButton:
QPushButton *testBtn = new QPushButton(this);
In b.h:
class B : public QMainWindow
{
Q_OBJECT
public:
explicit B(QWidget *parent = nullptr);
A testingA;
public slots:
void testing();
};
and b.cpp:
B::B(QWidget *parent) : QMainWindow(parent)
{
connect(testingA.testBtn, &QPushButton::clicked, this, &B::testing);
}
void B::testing()
{
qDebug() << "testing";
}
I am trying to connect the signal in class A to the slot in class B, but from the code I provide, it's not working.
So what is the right way to do it? Thanks
Edit:
According to PRIME's answer, I made a few changes.
In A's constructor, added:
connect(testBtn, &QPushButton::clicked, [this](){OnButtonClicked();});
to emit the own defined OnButtonClicked() signal;
and in B's construtor, changed to this:
connect(&testingA, &A::OnButtonClicked, this, &B::testing);
But when I clicked the button, the testing slot still not triggered.
Edit 2:
After doing some researches and trying a few times, I found that if I created B's object in A's constructor, and then connect A's signal to B's slot in A, it will work.
But I really can not figure out why I can not connect A's signal to B's slot in B.
This is what's in the main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
B b;
A w;
w.show();
return a.exec();
}
Is that because of some reasons that A's object is out of scope in B?
Can someone tell me where I did wrong? Thanks so much.
Don't do it like this, hide your button in the class A, emit your own defined signal from class A lets call it OnButtonClicked.
Cascading code(inside A's c'tor):
connect(testBtn , &QPushButton::clicked, [this](){OnButtonClicked();});
You will also have to declare this new signal in class A now:
So class A must have folowing besides whatever it has right now:
class A
{
Q_OBJECT
signals:
void OnButtonClicked();
};
No special slot is needed since you are using a Lambda as a slot for the signal OnButtonClicked.
Connection in class B( do it in the c'tor ):
connect(testingA, &A::OnButtonClicked, this, &B::testing);
You can connect signal-to-signal in your sender object, for example widget containing the button:
class MyWidget : public QWidget
{
Q_OBJECT
QPushButton *pushButton;
public:
explicit MyWidget(QWidget *parent = nullptr) : QWidget(parent), pushButton(new QPushButton(this)) {
connect(pushButton, &QPushButton::click, this, &MyWidget::buttonClicked);
}
signals:
void buttonClicked();
public slots:
};
By the way you would normally send signals by using emit keyword, e.g.:
emit buttonClicked();
Then the consumer:
class TestObject : public QObject
{
Q_OBJECT
public:
explicit TestObject(QObject *parent = nullptr) : QObject(parent) { }
public slots:
void onButtonClicked() {
qDebug() << "clicked";
}
};
And connect both instances:
MyWidget widget;
TestObject to;
QObject::connect(&widget, &MyWidget::buttonClicked, &to, &TestObject::onButtonClicked);
in your class A you should use the signal testing of the class B, if you clicked on your button the OnButtonClicked function will be activated
A:
public slots:
void OnButtonClicked();
void A::OnButtonClicked()
{
...
emit testing(1);
}
B:
signals:
void testing(int level);
then to connect both you can do this
connect(startButton, &QPushButton::clicked, board, &A::OnButtonClicked);

How to pass data from one form to another in Qt?

How can I pass data from one form to another in Qt?
I have created a QWidgetProgect -> QtGuiApplication, I have two forms currently. Now I want to pass data from one form to another.
How can I achieve that ?
Thanks.
Here are some options that you might want to try:
If one form owns the other, you can just make a method in the other and call it
You can use Qt's Signals and slots mechanism, make a signal in the form with the textbox, and connect it to a slot you make in the other form (you could also connect it with the textbox's textChanged or textEdited signal)
Example with Signals and Slots:
Let's assume that you have two windows: FirstForm and SecondForm. FirstForm has a QLineEdit on its UI, named myTextEdit and SecondForm has a QListWidget on its UI, named myListWidget.
I'm also assuming that you create both of the windows in the main() function of your application.
firstform.h:
class FistForm : public QMainWindow
{
...
private slots:
void onTextBoxReturnPressed();
signals:
void newTextEntered(const QString &text);
};
firstform.cpp
// Constructor:
FistForm::FirstForm()
{
// Connecting the textbox's returnPressed() signal so that
// we can react to it
connect(ui->myTextEdit, SIGNAL(returnPressed),
this, SIGNAL(onTextBoxReturnPressed()));
}
void FirstForm::onTextBoxReturnPressed()
{
// Emitting a signal with the new text
emit this->newTextEntered(ui->myTextEdit->text());
}
secondform.h
class SecondForm : public QMainWindow
{
...
public slots:
void onNewTextEntered(const QString &text);
};
secondform.cpp
void SecondForm::onNewTextEntered(const QString &text)
{
// Adding a new item to the list widget
ui->myListWidget->addItem(text);
}
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Instantiating the forms
FirstForm first;
SecondForm second;
// Connecting the signal we created in the first form
// with the slot created in the second form
QObject::connect(&first, SIGNAL(newTextEntered(const QString&)),
&second, SLOT(onNewTextEntered(const QString&)));
// Showing them
first.show();
second.show();
return app.exec();
}
You could also use pointers to access the QTextEdit (assuming that's what you're using) from the other form.
Following from Venemo's example (where FirstForm has the QTextEdit and SecondForm's the one you need to access the QTextEdit from):
firstform.h:
class FistForm : public QMainWindow
{
...
public:
QTextEdit* textEdit();
};
firstform.cpp:
QTextEdit* FirstForm::textEdit()
{
return ui->myTextEdit;
}
You can then access the QTextEdit's text in SecondForm with something like this (assuming your instance of FirstForm is called firstForm):
void SecondForm::processText()
{
QString text = firstForm->textEdit()->toPlainText();
// do something with the text
}

Resources