I want to make a QLabel clickable and followed this "how-to". I was not sure how to get this piece of code into my GUI (I am quite newbie to qt). What I did was:
I created a new class (just copy/paste of ClickableLabel from the link, but I changed the signal to clicked(QMouseEvent* event))
I added a QLabel to my GUI and "promoted" it to a ClickableLable
I connected the signal to a slot of my main window where I std::cout some stuff:
connect(this->ui->label,SIGNAL(clicked(QMouseEvent*)),
this,SLOT(on_label_clicked(QMouseEvent*)));
It seems to work. The problem is that each time I click on the label the mousePressedEvent gets called twice. I also tried mouseReleasedEvent but its the same.
Any ideas what could go wrong?
EDIT: Here is my modified ClickableLable:
class MyClickableLabel : public QLabel {
Q_OBJECT
public:
MyClickableLabel(QWidget* parent=0);
~GBoardLabel();
signals:
void clicked(QMouseEvent* event);
protected:
void mouseReleaseEvent(QMouseEvent* event);
};
MyClickableLabel::MyClickableLabel(QWidget* parent) : QLabel(parent) {this->setText("");}
MyClickableLabel::~MyClickableLabel() {}
void MyClickableLabel::mouseReleaseEvent(QMouseEvent *event){
std::cout << "CLICKED R" << std::endl;
std::cout << event->type() << std::endl;
std::cout << event->pos().x() << std::endl;
std::cout << event->pos().y() << std::endl;
emit clicked(event);
}
The couts in the in the above method I added only later and realized that the mouseReleaseEvent is actually only called once. But when I connect the clicked to a slot of my mainwindow, this slot recieves the event twice.
Then I removed the connect statement and to my surprise the signal is still emited and recieved (only once). I am a bit puzzled how this works, because I am pretty sure that I do not mistakenly have a connect anywhere in the code.
My label is working, but I would like to understand what is going on. Actually I am not 100% sure anymore that I didn't use some Qt creator feature make the connection. However, I have no idea where to find such connections. For example, I have a QButton on the same main window. In the gui editor I right clicked it and then "show slots"->"clicked()"->"OK" and automatically a method called on_pushButton_clicked() is created, but I have no idea, where this is called / how the button's signal is connected to this method. On the other hand, I do not get the MyClickableLabel::clicked(QMouseEvent*) listed in the list of slots for my label, thus I don't think I created the connection this way...
I could fix it, but I do not really understand what is going on...
It was not the mousePressEvent that was fired twice, but my on_label_clicked slot recieved the event twice.
I removed the connect statement and now the on_label_clicked is called only once per click. It seems like, there is something going on under the hood. Maybe when the slot is called on_label_clicked it gets automatically ("qtmatically") connected to the mouse events emmited from the child called label ?
EDIT: Still didnt find the official docs, but this blog explains it quite nicely. In summary, one just needs to use the naming convention for the slot
void on_<widget name="">_<signal name="">(<signal parameters="">);
to make use of the auto connect feature.
Related
I have a simple executable file (english.exe) that changes Windows' input language to English. I want to run this executable when the user interacts with various components of the GUI.
If I run the executable in the focusInEvent of QLineEdit, I get the expected behavior: the program runs, and there is no lag.
void MyLineEdit::focusInEvent(QFocusEvent *e)
{
QLineEdit::focusInEvent(e);
QProcess * switchInput = new QProcess(this);
switchInput->start( "c:/english.exe");
}
I want to run analogous code when a particular widget is activated, for which see this answer. (In fact, I would like to use focusInEvent for a QWebEngineView, but this resolved bug report notwithstanding, that event is never called; I am using Qt 5.12.6.) But with the following code, the GUI hangs.
bool MyWidget::event(QEvent *event)
{
bool result = QWidget::event(event);
if( event->type() == QEvent::WindowActivate )
{
// qDebug() << "QEvent::WindowActivate";
QProcess * switchInput = new QProcess(this);
switchInput->start( "c:/english.exe" );
}
return result;
}
Using the qDebug() code, I'm only getting one "QEvent::WindowActivate" message.
What is the difference between the two calls? Is there an alternative solution?
Edit: Whatever solution I find may not be of general interest. The program I'm running is a command-line AutoHotKey script (with contents PostMessage, 0x50, 0, 0x0409,, A) (no output or user interaction). For comparison I tried running ipconfig.exe (to take a command line program at random) and the problem goes away. So there must be something about the AutoHotKey script, or about AutoHotKey itself.
I am currently having trouble with a QStateMachine in QT. Basically I want to use it to control the flow between my screens, e.g. to switch from a master page to a detail page.
What I basically have is this:
// Overview
QState* overviewState = new QState();
overviewState->assignProperty(ui->stackedWidget, "currentIndex", 0);
stateMachine->addState(overviewState);
// Editor
QState* editorState = new QState();
editorState ->assignProperty(ui->stackedWidget, "currentIndex", 1);
stateMachine->addState(editorState);
// Overview -> Editor
overviewState->addTransition(overview, SIGNAL(onEditor()), editorState);
So far this is working as expected. The overview emits onEditor and the editor is shown. However, now I want to forward a value from the signal. The editor can be called with a selected value (meaning the user wants to edit an entry) or without a value (meaning the user starts with an empty editor). The overview would then have two signals: onEditor and onEditor(long). The editor would have two slots: performInitialize and performInitialize(long).
connect(editorState, SIGNAL(entered()), editor, SLOT(performInitialize()));
This is the point where I am stuck. (How) can I forward the signal from onEditor(long) to performInitialize(long)? The signal from the QState surely has no knowledge of this additional parameter. How can I solve this?
Thanks a lot in advance.
You can split the initialization into setup and performance stages:
void Editor::performInitialize(long val); // current implementation - keep it
void Editor::setupInitialize(long val);
m_initVal = val;
}
void Editor::performInitialize() {
performInitialize(m_initVal);
}
connect(overview, SIGNAL(onEditor(long)), editor, SLOT(setupInitialize(long)));
overviewState->addTransition(overview, SIGNAL(onEditor(long)), editorState);
addTransition sets up an internal connection to the signal. Qt guarantees that slots are executed in the order of connection. Thus the state transition is guaranteed never to precede setupInitialize call.
I have written a QT code which starts a new process on a button click, that process have to execute a shell script and append the Std output/error on text Browser dynamically based upon the script result. The code gets failed in the custom Slot. This is what my window.h is
class Window : public QWidget
{
Q_OBJECT
public:
explicit Window(QWidget *parent = 0);
QPushButton *goButton;
QTextBrowser *statusWindow;
QProcess *process;
private slots:
void go_Button_Clicked();
void updateOutput();
};
And this is how my window.cpp is
Window::Window(QWidget *parent) : QWidget(parent)
{
// Text Browser
statusWindow = new QTextBrowser(this);
// Create the Go button, make "this" the parent
goButton = new QPushButton(this);
connect(goButton, SIGNAL (clicked()), this, SLOT (go_Button_Clicked()));
}
void Window::go_Button_Clicked()
{
// Step1 - Create a new process
QProcess *process = new QProcess(this);
// Step2 - Connect the Signals and slot
qDebug() << connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(updateOutput()));
// Step3 - Start the Script process
process->start("/home/root/script.sh");
qDebug() << "Process in main";
}
void Window::updateOutput()
{
qDebug() << "Process in update Output";
//statusWindow->append(process->readAllStandardOutput());
}
So whenever i uncommented lines in update Output the GUI crashes as soon as i hit the button. using qdebug i managed to find that the GUI crashes bcoz of the line "statusWindow->append(process->readAllStandardOutput());" .
If the line is commented the debug message keeps on printing on console, but with the un-commented line i get the debug message once and then GUI crashes.Here is the debug output.
true
true
Process in main
Process in update Output
Process killed by signal
Any idea whats going on here, and i am familiar with Debugging in QT
Replace:
QProcess *process = new QProcess(this);
by
process = new QProcess(this);
You are using uninitialized member variable process, because in Window::go_Button_Clicked() you are creating a local variable.
Edit
Actually, the code is very error prone. What happens, when the user presses the button multiple times. Then in the slot you may read the the output from wrong process.
One workaround could be: You don't define QProcess as member at all, but do it like now - as a local variable. Then in the slot, you can cast the sender to QProcess*, and unless it fails use this instance. It will be always the right one. Afterwards, don't forget to delete the sender. Use deleteLater() for this.
Inside your void Window::go_Button_Clicked() slote you hide QProcess *process declared at .h file with new variable
QProcess *process = new QProcess(this);
Replace this line with
process = new QProcess(this);
But when you click second time you'll get memory leak, and can not get more data from first process. So you chould change you design somehow.
I don't under stand how to use the ProcessState enums. According to documentation, the ProcessState enum can have the following values:
QProcess::NotRunning- 0 - The process is not running.
QProcess::Starting- -1-The process is starting, but the program has not yet been invoked.
QProcess::Running -2 -The process is running and is ready for reading and writing.
How would I use them?
What you refer to are not functions, simply values. You could assign them to an integer and output its value:
int val = QProcess::Starting;
qDebug() << "the value of QProcess::Starting is" << val;
To check the state of a process, you could do:
QProcess *process;
....
if (process->state() == QProcess::Running) // do something with a running process
Of course, when it comes to a QProcess, you really need to be handling signals that the process emits as it changes state. You do not want to do any sort of busy-waiting, and I should discourage the use of any Qt function called waitFor.... Those functions cause the event loop to be re-entered, and potentially to re-enter your code that you never realized could be re-entered. It's a Pandora's box you do not want to open. About the only valid use of wait-style functions is to wait for QThreads that have been quit() to finish before you return from the main() function.
You can have states for the processes to be run. You can then connect your slot to the state changed signal, even in QML if needed, and act accordingly. Also, not that there is no such a thing as "enum function". It is just a simple enumeration that basically the state "property" holds. You can query and set it the usual way. You can see the documentation for those methods below.
http://qt-project.org/doc/qt-5.0/qtcore/qprocess.html#state
http://qt-project.org/doc/qt-5.0/qtcore/qprocess.html#setProcessState
This looks like a generic Qt examples as your question is, but here you go:
myclass.h
class MyClass : QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent);
public Q_SLOTS:
void handleProcessStateChanged(QProcess::ProcessState newProcessState);
....
}
myclass.cpp
...
MyClass::MyClass(QObject *parent)
: QObject(parent)
{
}
MyClass::myProcessInvokeMethod()
{
connect(myprocess, SIGNAL(stateChanged(QProcess::ProcessState), this, SLOT(handleStateChange(QProcess::ProcessState)));
myprocess.start(myprogram, myarguments);
....
}
void MyClass::handleProcessStateChange(QProcess::ProcessState newProcessState)
{
switch (newProcessState) {
case QProcess::NotRunning:
qDebug() << "Here goes the handler code when the process is not yet running";
break;
case QProcess::Starting:
qDebug() << "Here goes the handler code when the process is starting";
break;
case QProcess::Running:
qDebug() << "Here goes the handler code when the process is running";
break;
}
}
...
I have problem with one class in my project, after click appears new window with QTableWidget and QPushButton, after clicking the button I should have "test" on stdout, but nothing shows, here are parts of this code:
Header:
class ClientsSelector : public QWidget {
Q_OBJECT
public:
ClientsSelector(InvoiceTab* parent);
QWidget *window;
private:
QPushButton *accept;
public slots:
void loadData();
Constructor:
window = new QWidget();
layout = new QGridLayout();
layout->addWidget(table, 0, 0);
/*code*/
accept = new QPushButton(QString::fromUtf8("Load data"));
connect(accept, SIGNAL(clicked()), this, SLOT(loadData()));
layout->addWidget(accept, 0, 1);
/*code*/
window->setLayout(layout);
window->show();
Method:
void ClientsSelector::loadData() {
QTextStream std(stdout);
std << "test" << endl;
}
I have not even one warning nor error. I have nothing on stdout, it looks like button was connected to wrong object(?)
How do you instantiate ClientsSelector? Isn't it a singleton or global variable by chance? Try moving the connect call to a separate init function which is called after the ClientsSelector constructor. It helped me in similar WTF situations. It has something to do with the fact that each QObject inheritor has a static metadata member and you can't be sure about when it is fully initialized until the constructor finishes. connect won't work without that metadata.
See for example here: http://www.qtcentre.org/threads/9479-connect-in-constructor
If still lost, go through this checklist. Qt signals are so easy to use, everybody sometimes forgets it also has some requirements.
Seems like you forgot a closing " on the test printout.
Try using
qDebug() << "test";
instead of QTextstream
You can do following to make sure that the connection was made and slot is called.
connect function returns status of connection. Check if the connection was made properly.
put a breakpoint and see if the loadData() is called when button is pressed.
This might help to find the cause.