I currently have a problem where my QMessageBox has to be closed twice before it finally goes away. I was wondering if the local event loop QMessageBox::exec() is being triggered twice by the QApplication::exec() but I am not sure if that's right assumption to make. I then decided to switch the QMessageBox::exec() to .show() and the QMessageBox instantly disappears, not sure why I either. An explanation and a solution would be great.
#include "texteditor.h"
#include "./ui_texteditor.h"
TextEditor::TextEditor(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::TextEditor)
, m_texteditor(new QTextEdit(this))
{
ui->setupUi(this);
connect(ui->actionAbout, &QAction::triggered, this, &TextEditor::on_actionAbout_triggered);
connect(ui->actionExit, &QAction::triggered, [](){ QApplication::quit();});
}
TextEditor::~TextEditor()
{
delete ui;
}
void TextEditor::on_actionAbout_triggered()
{
const QString projectName = QString("Project Name: %1").arg(PROJECT_NAME);
const QString version = QString("Version: %1").arg(TextEditor_VERSION);
QMessageBox msgBox(this);
msgBox.setText(projectName);
msgBox.setInformativeText(version);
msgBox.exec();
msgBox.show();
}
As is turned out during the discussion in the comments:
Either on_actionAbout_triggered should be renamed, possibly into something not matching the on_childObject_signal pattern or the manual connection connect(ui->actionAbout, &QAction::triggered, this, &TextEditor::on_actionAbout_triggered); should be removed.
The reason for the dual call is QMetaObject::connectSlotsByName, which is usually (and also in this case) called by setupUi. It automatically connects the methods matching on_childObject_signal naming convention to their name-designated signals.
Related
I am developing a GUI app. I have a main window(QMainWindow) which pops up when the app is executed.
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
This window has some buttons. When I click a button here, another window which is of QDialog is popped up. The logic of the button is
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
lib.setModal(false);
lib.exec();
}
Question: I have a socket in the QMainWindow class, whenever I receive a message there I need to send it to the QDialogue window and display it in a QLineEdit. With the above logic, even after I set QDialogue to nonmodal, I am not able to interact with the QMainWindow when the QDialogue window is open. I tried lib.show() instead of lib.exec(), but with this, when I click the button the QMainWindow class the QDialogue window does not pop up.
Please advice me what is the best method to communicate from the background window the the foreground window?
Your code can looks like
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( letsChangeLineEdit( QString ) ) ) );
lib.setModal(false);
lib.exec();
}
Of course you should implement your signal and slot. QString in signal slot arguments is target string to show in dialog.
Hope that will clear for you.
In your MainWindow::on_InsertButton_pressed()slot you are creating a local variable libinsert lib which gets destroyed when it gets out of scope, i.e. when the slot execution finishes. If you use lib.exec() the execution waits for the dialog execution to return so you see the dialog, but can't interact with the main window until you close it. When you use lib.show(), your dialog is shown, execution continues and exits the slot right away, at which point the lib gets destroyed and you don't see it any more (depending on your configuration this may be fast enough that it looks like it newer gets shown in the first place).
One solution is to have a pointer to your dialog as a member of MainWindow class (add libinsert* lib; in MainWindow.h), and initialize it in your Mainwindow constructor like this:
`MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
lib = new libinsert(this);
lib->setModal(false);
}`
Then in MainWindow::on_InsertButton_pressed() you only need to call QWidget::show() method, i.e. like this:
void MainWindow::on_InsertButton_pressed()
{
lib->show();
}
Now, if what you are trying to achieve is to have a dialog pop-up every time the message is received , and display the message, first you will need to have a public method(or slot) in your libinsert class which updates the text in your QLineEdit, for example
void libinsert::onMessageReceived(QString message)
{
// Update the text and show the dialog to avoid having to call show()
// explicitly from MainWindow.
ui->lineEdit->setText(message);
show();
}
and then either call this method directly, when you receive the message, with:
lib->onMessageReceived(message);
or connect a signal, emitted when message is received, to it with something like this (in MainWindow constructor):
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( onMessageReceived( QString ) ) ) );
In my qt c++ application a QStringList is sent from one cpp file(MainWIndow) to another cpp file(Dialog) via signal and slots mechanism! I want to display the elements in the qtringList on a combo box in the Dialog.ui when the interface gets loaded(no button click)!
following is my code
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
for(int i=0;i<subMessages.size();i++){
ui->comboBox->addItem(subMessages[i]);
}
}
//slot which is used to get the qstringList
void Dialog::receiveSubMessages(QStringList List){
subMessages=List;
}
The QStringList is received successfully through the slot(already verified).Though I used a for loop and tried display (as in the code) nothing was displayed on the combo box! How can I fix this issue?
In order to get a working code you need to place your for llop inside you slot:
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
//slot which is used to get the qstringList
void Dialog::receiveSubMessages(QStringList List){
ui->comboBox->addItems (List);
}
If you want to fill the comboBox with the contents of some QStringList upon Dialog construction then you should either pass this list as constructor argument:
Dialog::Dialog(QStringList List, QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->comboBox->addItems (List);
}
or call Dialog::receiveSubMessages() right after Dialog object construction:
// somewhere in code, where you construct Dialog object
// ...
auto parent = SomeQWidgetDerivedClass (...);
QStringList someStringList {
"string 1",
"string 2"
};
// ...
auto dialog {new Dialog ()};
dialog->receiveSubMessages (someStringList);
// ...
The code that you have provided will never allow you to achieve the desired result because your for loop that is supposed to fill the QComboBox is executed on your Dialog object creation. At that point your subMessages is empty. The slot that you have created is not called before the constructor - the slot is just a member function that can only be called after the object is created. You can only call a member function without an object if the function itself is static, in which case it is definitely not a slot.
I did this answer rather to show you how to solve your problem. (I've the feeling that I even didn't understand what your actual problem is.)
When asking a question the chances to get a helpful answer increase if an MCVE is provided. (Please, follow this link. It teachs you really basic skills every S/W developer shouldmust have. I would also recommend to follow-up to How to debug small programs.)
As I did understand your problem I made such an MCVE. This is the code testQComboBox:
#include <QtWidgets>
int main(int argc, char **argv)
{
// build appl.
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// a QStringList with items
QStringList items;
items
<< QString::fromUtf8("first item")
<< QString::fromUtf8("second item")
<< QString::fromUtf8("third item");
// build GUI
QDialog dlg;
QVBoxLayout vBox;
QComboBox cmbBox;
cmbBox.addItems(items);
vBox.addWidget(&cmbBox);
dlg.setLayout(&vBox);
dlg.show();
app.exec();
// done
return 0;
}
I compiled it in VS2013 with Qt 5.9.2 on Windows 10 (64 bit). This is how it looks:
As you see, the usage of combobox is rather easy – no secret trap doors to use it. The actual code which is directly related to QComboBox is exactly 4 lines of code:
QVBoxLayout vBox;
QComboBox cmbBox;
cmbBox.addItems(items);
vBox.addWidget(&cmbBox);
And there is exactly one line of code where items are added to the QComboBox:
cmbBox.addItems(items);
Note:
I used QComboBox::addItems() instead of QComboBox::addItem() as the former has already a loop built-in to add a complete QStringList. It doesn't make any difference to the loop you used in your code Dialog::Dialog().
So, finally I dare to do the following statement:
If your combobox doesn't show items then:
You added items from an empty list.
Or, you forgot to add the items from list.
Or, something very weird happend.
I would always bet for 1. or 2. reason – the 3. reason is for real emergency cases only (e.g. broken Qt installation).
Concerning 3. reason:
I saw many questions where some lines of innocent looking code were presented which looked exactly as they should but were claimed to fail. And finally almost everytimes it showed that these lines worked fine when isolated in an MCVE but they didn't in the original code. How can this happen? Either there is some context which changes the behavior of the code in your original program or there is UB – undefined behavior. Something else does bad things but instead of crashing your process immediately (which would mean you're lucky) it goes on for a while corrupting the data more and more until finally everything breaks completely. Looking into the core-dump doesn't help at all. Therefore my recommendation of How to debug small programs.
I'm trying to update a QPlainTextEdit() from either a button click, thread, etc. Somewhere from outside the MainThread and in Qt documentation it says it must used signals but I can't figure out how. If I try to do a CreateThread() or use a std::thread to update the
class MainWindow : public QMainWindow
{
Q_OBJECT
...
private slots:
handleButtonClick();
Thread();
...
private:
QPlainTextEdit *TextView;
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
...
TextView = new QPlainTextEdit();
TextView->setReadOnly(true);
TextView->setCursorWidth(0);
QPUshButton *UpdateButton = new QPushButton();
connect(UpdateButton, SIGNAL(released()), this, SLOT(handleButtonClick()));
....
}
MainWindow::handleButtonClick()
{
// eventually this will cause a crash, usually not imemdiately
TextView->insertPlainText("test");
}
MainWindow::Thread()
{
TextView->insertPlainText("test");
}
So as you can see, I want a way to update this QPlainTextEdit from outside the main thread without crashes. How can I do this? Thanks.
You are not allowed to do UI opeations like updating the text of a QTextEdit from outside the main thread. The solution to this is emitting a signal from the non-main thread, and have it connected to a slot in the main thread which does the UI work.
Overflowers!
I'm getting crazy trying using scrollTo() in qTreeView (or QListView) widget. To make my question simple I've reduced my code to a simple scrollToBottom() which I can't manage to use as well. Here is the mainWindow code:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <qfilesystemmodel.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFileSystemModel *model = new QFileSystemModel(this);
QModelIndex modelRootIndex = model->setRootPath(QDir::rootPath());
ui->treeView->setModel(model);
ui->treeView->setRootIndex(modelRootIndex);
ui->treeView->scrollToBottom();
if(modelRootIndex.isValid()) std::cout << "validIndex" << std::endl;
}
MainWindow::~MainWindow()
{
delete ui;
}
As far as I know it's all ok (I get the "ValidIndex" string on standard output), but the widget doesn't scroll to bottom at all.
I'm using Desktop QT5.0.2 msvc2010 32bit.
Any Idea? Thanks. L
QFileSystemModel and the QFileSystemWatcher are kept up-to-date in a separate thread. Thus, simply setting the model on the tree view does not ensure that the model will be fully populated by the time you make the call to scrollToBottom. Use a single shot timer with a small delay to give the model time to populate.
QTimer::singleShot(1000, ui->treeView, SLOT(scrollToBottom()));
Additionally, (and I don't know your application, so this may or may not be true) it may be confusing to your users that the data they need to see is at the bottom of the view anyway. You may think about whether you can sort the view items in reverse order (thus having the data you need at the top) to avoid scrolling and potentially make the usage more intuitive.
This is because of the asynchronous way that QFileSystemModel works, and what appears to be a bug in Qt that was never fixed: https://bugreports.qt.io/browse/QTBUG-9326
You can work around it by doing QApplication::sendPostedEvents() immediately before the call to scrollTo(), but you must call them in a function that is connected to the directoryLoaded signal:
MyFileBrowser::MyFileBrowser(QWidget *parent) : QWidget(parent), ui(new Ui::MyFileBrowser) {
//...
connect(model, SIGNAL(directoryLoaded(QString)), this, SLOT(dirLoaded(QString)));
QModelIndex folderIndex = model->index("path/to/dir");
files->setCurrentIndex(folderIndex);
files->expand(folderIndex);
}
void WFileBrowser::dirLoaded(QString dir) {
if (dir == model->filePath(files->currentIndex())) {
QApplication::sendPostedEvents(); // booyah!!
files->scrollTo(files->currentIndex(), QAbstractItemView::PositionAtTop);
}
}
A am trying to make a program that takes a signal from one class and with activation of that signal I want to activate a slot in second class.
In my case the first class is the mainWindow class, this class is subClass of QMainWindow, and in this class is the slot that I want to activate.
This is mainWindow.cpp:
mainWindow::mainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::mainWindow)
{
ui->setupUi(this);
}
mainWindow::~mainWindow()
{
delete ui;
}
void mainWindow::slotForStatusBarMessage(QString string)
{
statusBar()->showMessage(string);
}
The second class is the mainWidget class and it is a subclass of QWidget.
This is mainWidget.cpp:
mainWidget::mainWidget(QWidget *parent) :
QWidget(parent)
{
buttonAddNewRecord=new QPushButton("Add new record", this);
layoutButton=new QHBoxLayout();
layoutButton->addWidget(buttonAddNewRecord);
layoutMain=new QVBoxLayout();
layoutMain->addLayout(layoutButton);
functionDatabaseOpen();
setLayout(layoutMain);
}
The signal is emited from functionDatabaseOpen() function:
if (sqlDatabase.open())
{
emit signalForShowMessageInStatusBar("true");
}
else
{
emit signalForShowMessageInStatusBar("false");
}
I have made all the settings to the database but i didnt copy here because of space.
I have tried to make connection inside main.cpp but it seems it dosent work.
QObject::connect(mw, SIGNAL(signalForShowMessageInStatusBar(QString)), w, SLOT(slotForStatusBarMessage(QString)));
I cant make this signal/slot connection between classes to work. Can you give me any help.
If you have any question about the code please ask. Sorry for the bad english,I am not a native english speaker.
Thank you very much for your help.
You are emitting the signal from the constructor of mainWidget, and since the connection is only done after you return from that constructor, the signal doesn't go anywhere.
The easiest fix, not knowing what the rest of the code looks like, would be to move the call to functionDatabaseOpen() in main() after the signal/slot connection is made.