i have an UI with two buttons. My first button read an xml file. The second button should create an window an show a circle.
I have a mainwindow.h and a circle.h. Now i want to start my circle function with a pushbutton click.
My circle function:
void circle::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(QBrush("#888"), 1));
painter.setBrush(QBrush(QColor("#888")));
qDebug() << "\r\nxarray : " <<"test";
for(int z = 0; z < 50; z++)
{
painter.drawEllipse(10, 10, 100, 100);
}
}
Atm i start it with:
circle::circle(QWidget *parent)
: QWidget(parent)
{}
But i want to start it with:
void MainWindow::on_pushButton_clicked(
{}
How can i start my function with the pushButton?
[I just started to learn qt/c++ so im a beginner]
greetings
In the slot on_pushButton_clicked() create an instance of the class circle and call the required method through that instance.
EDIT:
I do not know what method you intend to call in the circle class. But assuming you have a function called myFunc(), the code would be something like:
void MainWindow::on_pushButton_clicked()
{
circle* circleObj = new circle(this); // instance created
circleObj->myFunct(); // the necessary actions are done in this function
// connect the signals from the circle class if any to the required slots in MainWindow
connect(circle, SIGNAL(mySignal()), this, SLOT(mySlot());
}
Since you seem to be completely new to Qt looking at your comments, I highly recommend you go through the documentation and the awesome tutorials at the VoidRealms YouTube channel before proceeding further.
Qt uses signal and slot system for specifying actions triggered by the user. So if you specify function on_pushButton_clicked you should do the following:
Write Q_OBJECT macro in the beginning of your MainWindow definition (class containing a slot)
Write on_pushButton_clicked in private slots: section of your class (instead of normal private: section)
Write call to QObject::connect somewhere (possibly in constructor) with the following syntax: connect (button, SIGNAL(clicked ()), this, SLOT(on_pushButton_clicked ()));, read Qt manual for which signals are available.
Rerun qmake if you're using Visual Studio.
Specifically to do what you want in your case, you probably need to create some flag which will be set in on_pushButton_clicked function and check for this flag in paintEvent.
Additional info about signal and slots from Qt documentation: http://doc.qt.io/qt-5/signalsandslots.html
Related
I have two classes that are interacting with each other, let's call them for the sake of simplicity ClassA and ClassB. In ClassA, I would like to emit a signal when a specific QGroupBox is shown that triggers a slot in ClassB to set the given channel's checkbox.
I have a code that consists of several thousand lines, so I included only the most relevant parts.
ClassA header
private:
QGroupbox* m_Channel1GroupBox;
void setup();
signals:
void setCheckBox(int, bool);
ClassA source
void ClassA::setup()
{
m_Channel1GroupBox = new QGroupBox("Channel 1", this);
connect(m_Channel1GroupBox, &QGroupBox::show, [this]()
{
emit setCheckBox(1, true);
});
}
ClassB header
private:
QCheckBox* m_Channel1CheckBox;
public slots:
void setCheckBox(int, bool);
ClassB source
void ClassB::setCheckBox(int channel, bool check)
{
if (channel == 1)
{
m_Channel1CheckBox->setChecked(check);
}
}
Main.cpp
connect(m_ClassA, &ClassA::setCheckBox, m_ClassB, &ClassB::setCheckBox);
The lambda expression compiles, but unfortunately it doesn't do what it is expected to do, which is whenever m_Channel1GroupBox->show() is called, then tick the checkbox of Channel1. Actually this block of code never executes, which would emit setCheckBox.
Please note that the setCheckBox function works perfectly when called in another function of ClassA and it checks or unchecks the desired channel in ClassB. This is possible using another connect in the Main.cpp file where the object instances are created.
You will need to subclass QGroupBox to achieve the functionality you require.
Override QWidget::showEvent(QShowEvent*) in your QGroupBox subclass and do the work there.
Please see the documentation for QWidget.
run the code and you will see that the
QGroupBox has no signal to emit called show...
show is actually a slot
QObject::connect: signal not found in QGroupBox
But...
if you are calling show per hand, then emit the signal right after that...
I've made a class named MyWindow which inherits from QWidget to create a window. Here is the content of mywindow.h:
class MyWindow: public QWidget{
public:
MyWindow(QString title,QString icon,int w = 600,int h = 400);
int getWidth() const;
int getHeight() const;
public slots:
void openDialogBox(QString title,QString message);
private:
int m_width;
int m_height;
};
There is a openDialogBox slot which takes the title and the message of the dialog box as arguments.
I've made a menu bar which basically looks like this:
MyWindow myWindow("Example window",QCoreApplication::applicationDirPath() + "/icon.png");
QMenuBar menuBar(&myWindow);
menuBar.setGeometry(0,0,myWindow.getWidth(),menuBar.geometry().height());
QMenu *fileMenu = new QMenu("&File");
QAction *fileMenu_open = fileMenu->addAction("&Open");
MyWindow::connect(fileMenu_open,&QAction::triggered,&myWindow,&MyWindow::openDialogBox);
In the last line, I would like to send arguments to the slot &MyWindow::openDialogBox. I tried to do:
MyWindow::connect(fileMenu_open,&QAction::triggered,&myWindow,&MyWindow::openDialogBox("Title","Hello, this is a message"));
but it didn't work (I don't need you to explain why it didn't work, I already know why). How to do this properly so that it works?
Since you are using the New Signal Slot Syntax, I would suggest using a c++11 lambda instead of a slot, and call the desired function inside your slot, here is how your connect call would look like:
QObject::connect(fileMenu_open, &QAction::triggered, &myWindow, [&myWindow](){
myWindow.openDialogBox("Title","Hello, this is a message");
});
Note that openDialogBox is not required to be a slot this way, it can be any normal function.
If your compiler does not support C++11 lambda expression, you might have to declare a slot that does not take any argument, and connect to that slot. And inside that slot call your function with the desired arguments. . .
Use lambdas
QObject::connect(fileMenu_open, &QAction::triggered, &myWindow, [QWeakPointer<MyWindow> weakWindow = myWindow]()
{
weakWindow->openDialogBox("Title","Hello, this is a message");
});
QWeakPointer is used in case your class is moved, so the "old" myWindow is a dangling pointer
If your class won't be moved, just capture myWindow.
Note that my code needs C++14 to declare a variable in the lambda capture
Consider the following scenario:
I have integrated QT in my c++ app. I wish to enter Data from a GUI rather than terminal. For this purpose, i created a function for QT. My dialog window consists of three text lines and a button, upon the click of which i want to call a particular method of some other class. I am having trouble with SINGAL and SLOTS.
Consider the following files:
main.cpp has
a.h -> a.cpp
a.cpp has
a.h
myslots.h
and the QT app method inside a.cpp as:
int A::inputData(){
...
A a
myslots ms;
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(&a)));
....
}
myslots.h has:
a.h and inherited from A as:
class myslots : public QObject, public A {
Q_OBJECT
public slots:
void clickButton(A &a);
signals:
void buttonClicked();
};
myslots.cpp has:
myslots.h and the following method
void myslots::clickButton(A &a) {
cout <<"I am called successfully"<<endl;
a.perform_action(-2.3, 4.5, 4.4);
emit this->buttonClicked();
}
I get the following error:
QObject::connect: No such slot myslots::clickButton(&a)
Actually i want to pass three double values from three textlines say: 1.3, 2.4, 4.5 to a function by clicking the button, where the function to be called is in another class that is inherited by myslots.h and accepts three parameters.
Currently am just testing whether i am able to call the function properly or not but am not.
Thanks
This will not work. In SIGNAL or SLOT must be a type, not an object or its reference. I mean this
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(&a)));
must be
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(A)));
But then you must catch another error - where is A in your SIGNAL? Why do you use A in the signal at all?
Some issues:
1 - When tou derive from QObject, always put the Q_OBJECT macro in the private section of your class, do something like
class MyClass: public QObject {
Q_OBJECT
public:
...
this Q_OBJECT macro is important for classes that have signals and slots.
2 - In the connect statement you have to tell Qt the type of the parameters, not the names you use, that is, if you slot function is
void myslots::clickButton(A &a);
The you connect to it with
connect(emiterObject, SIGNAL(someSignal(A)), targetObject, SLOT(clickButton(A)));
Since Qt 5.x you are able to use a lambda expression as slot.
So your connect statement should look like:
QObject::connect(button, &QPushButton::clicked, [a, ms](){ms.clickButton(&a);});
But, using a and ms as local variables is not realy a good thing, as stated by Karsten Koop.
The error is in your :
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(&a)));
it has to be:
QObject::connect(button, SIGNAL(clicked(A)), &ms, SLOT(clickButton(A)));
the type that you send in the SIGNAL must be in SLOT that receives that type.
I'm fighing since last week with problem caused by update of QPlainTextEdit. I'm trying to create separate from QMainWindow Dialog window with QPlainTextEdit inside. The problem begins when I try to use appendHtml signal (also tried with appendText), text that is placed is not visible unless marked by by mouse. Repainting or updating cause in program crash or no visible action.
Simplified code of QDialog with QPlainTextEdit header:
namespace Ui {
class LogWindow;
}
class LogWriter: public QDialog
{
Q_OBJECT
QMutex print_lock;
public:
class Log{
Q_OBJECT
const static int MAX_SIZE = 100;
bool to_terminal;
QString color;
QMutex *print_lock;
QPlainTextEdit *text_place;
QVector< QPair<QString,time_t> > history;
LogWriter * obj;
public:
bool print;
Log(bool _print,QString _color,LogWriter *obj_ = NULL)
{print = _print; color = _color; obj = obj_;}
void setLock(QMutex *print_lock_){print_lock = print_lock_;}
void setTextField(QPlainTextEdit *_text) {text_place = _text;}
Log& operator<<(QString &a);
Log& operator<<(const char* a);
};
static LogWriter* getInstance()
{
static LogWriter instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return &instance;
}
~LogWriter();
Log LOW,MEDIUM,HIGH;
Ui::LogWindow *ui;
signals:
void signalLogAppend(QString);
};
Simplified code of methods definitions:
LogWriter::LogWriter(QWidget * parent): QDialog(parent) {
ui = new Ui::LogWindow;
ui->setupUi(this);
LOW.setLock(&print_lock);
MEDIUM.setLock(&print_lock);
HIGH.setLock(&print_lock);
connect(this,SIGNAL(signalLogAppend(QString)),ui->plainTextEdit,
SLOT(appendHtml(QString)),Qt::DirectConnection);
}
LogWriter::Log& LogWriter::Log::operator<< (QString &s){
history.push_front(qMakePair(s,time(NULL)));
if(history.size() > MAX_SIZE) history.pop_back();
if(print){
//print_lock->lock();
QString text = "<font color=\"";
text += color + "\">";
text += s + "</font>";
//cout << text.toStdString() << endl;
//text_place->appendHtml(text);
//text_place->repaint();
emit (obj)->signalLogAppend(text);
//print_lock->unlock();
}
return *this;
}
I have two separate ui files (first for main window, second for log window).
I have to use log window all across my program (something about 10 threads), and I stucked on this issue. My question is - is it possible to force GUI update without using main thread and if not - what else possibilities I have. If possible I would rather avoid reconstructing all my code - it would take me some time to do it. Right now logging is super easy - I ony need:
LogWindow *log = LogWindow::getInstance();
log->MEDIUM << "something";
As additional info I add QTCreator warning:
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
If I understand your code correctly, you're trying to log from a background thread and are using a direct connection to pass the signal to the GUI thread? That's not going to work, you have to send the signal via the default connection so Qt can figure out that it's a cross-thread signal and pass it across threads accordingly (ie, via the message loop on the foreground thread).
In Qt, any GUI interaction has to happen in the Main/foreground thread otherwise bad things happen as you discovered. You can certainly send a signal from a background thread to trigger a GUI update - I do this all the time - but you need to ensure that you're using the correct connection for it. The direct connection results in a direct function call and is not going to work for you in this case.
In your code, the problem is the call to connect() - you explicitly specify the connection mode for the signal to slot connection when you should just use the default setting. If you set the connection explicitly to Qt::DirectConnection, the underlying code will execute a direct call to the specified slot, which means that you end up calling the slot in the thread context of the signal. You don't want that because the signal is triggered in a background thread.
You can't pass arbitrary types/classes to signals and slots. The list is limited and not all Qt classes are in the list. To add types/classes to the list of those that can be passed to a signal/slot, you must call qRegisterMetaType for that class. I recommend calling it in the constructor of the class you're trying to pass to a signal like this:
MyClass::MyClass() : MyParentClass()
{
static int reg = qRegisterMetaType<MyClass>("MyClass");
}
The static int ensures the registration is only called once and before any instance of MyClass could ever be used.
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 ;)