Qt window wont close using "this->close()" from other class - c++

I will start off by explaining my main goal. I have a main window with 7 buttons on it(amongst other things), when you hit each button, it closes out the current window and opens up a new window. All the windows will have the same 7 buttons, so you can go between each window. With all windows having the exact same 7 buttons, I wanted to set up a function that each class can call to set up each button and connect to a slot() in my mainwindow.cpp(called setupSubsystemButtons in example below). However, I can't seem to get the window to close using the standard "this->close()"...it works when I go from the main window to another window(the main window closes) but when I go from a different window to say the home window, the different window doesn't close. Suggestions would be greatly appreciated. My guess is that my understanding of "this" when it comes to calling slots in another class is wrong.
mainwindow.cpp( the parts that are relevant)
void MainWindow::ECSgeneralScreen()
{
ECSgeneralCommand *ECSgeneral = new ECSgeneralCommand;
this->close();
ECSgeneral->show();
//opens up the ECS screen
}
void MainWindow::homeScreen()
{
MainWindow *home = new MainWindow;
this->close();
home->show();
//opens up the ECS screen
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setupSubsystemButtons(QGridLayout *layout)
{
//Push Button Layout
homeScreenButton = new QPushButton("Home");
layout->addWidget(homeScreenButton, 3, 11);
connect(homeScreenButton, SIGNAL(clicked()), this, SLOT(homeScreen()));
ECSgeneralScreenButton = new QPushButton("General");
layout->addWidget(ECSgeneralScreenButton,5,11);
connect(ECSgeneralScreenButton, SIGNAL(clicked()), this, SLOT(ECSgeneralScreen()));
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtWidgets>
#include <QDialog>
namespace Ui {
class MainWindow;
}
class MainWindow : public QDialog
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
QWidget *window;
void setupSubsystemButtons(QGridLayout *layout);
~MainWindow();
private slots:
public slots:
void ECSgeneralScreen();
void homeScreen();
};
#endif // MAINWINDOW_H
ecsgeneralcommandWindow
include "ecsgeneralcommand.h"
#include "mainwindow.h"
#include <QtWidgets>
#include <QtCore>
ECSgeneralCommand::ECSgeneralCommand(MainWindow *parent) : QDialog(parent)
{
QGridLayout *layout = new QGridLayout;
QWidget::setFixedHeight(600);
QWidget::setFixedWidth(650);
...
//Setup Subsystem Buttons
test.setupSubsystemButtons(layout);
setLayout(layout);
}
ecsgeneralcommandWindow header
#ifndef ECSGENERALCOMMAND_H
#define ECSGENERALCOMMAND_H
#include <QDialog>
#include <QMainWindow>
#include <QtWidgets>
#include <QObject>
#include "mainwindow.h"
class ECSgeneralCommand : public QDialog
{
Q_OBJECT
public:
explicit ECSgeneralCommand(MainWindow *parent = 0);
private:
MainWindow test;
public slots:
};
#endif // ECSGENERALCOMMAND_H

Slots are just normal functions. When Qt invokes a slot, it ends up calling the appropriate receiver's method. In other words, this equals to the value of the 3rd argument of your connect statements. You passed this there, so the receiver is MainWindow object. E.g. MainWindow::homeScreen method always tries to close MainWindow. If it is already hidden, this action takes no effect.
You should either have a slot in each window class and connect buttons to appropriate receivers, or use a pointer to the currently active window instead of this when calling close(). But your architecture is strange in the first place. Why would you need to create these buttons for each window? It is reasonable to create them once and use in all windows. Also hiding and showing windows is not necessary. You can create one main window with buttons and a QStackedWidget that will contain the content of all other windows. Maybe you can even use QTabWidget instead of these buttons.

Related

Qt connect signals and slots of different windows/ mirror a lineEdit text on two windows

I'm new to any form of programming but have to do a project with Qt for my "programming for engineers" course where we simultaneously learn the basics of c++.
I have to display a text from one lineEdit to a lineEdit in another window.
I have a userWindow that opens from the mainWindow and in this userWindow I have a lineEdit widget that displays the current selected user as a QString (from a QDir object with .dirName() ). But now I have to display the same String in a lineEdit in the mainWindow as well.
From what I've read I have to do this with "connect(...)" which I have done before with widgets inside a single .cpp file but now I need to connect a ui object and signal from one window to another and I'm struggling.
My idea /what I could find in the internet was this:
userWindow.cpp
#include "userwindow.h"
#include "ui_userwindow.h"
#include "mainwindow.h"
#include <QDir>
#include <QMessageBox>
#include <QFileDialog>
#include <QFileInfo>
QDir workingUser; //this is the current selected user. I tried defining it in userWindow.h but that wouldn't work how I needed it to but that's a different issue
userWindow::userWindow(QWidget *parent) : //konstruktor
QDialog(parent),
ui(new Ui::userWindow)
{
ui->setupUi(this);
QObject::connect(ui->outLineEdit, SIGNAL(textChanged()), mainWindow, SLOT(changeText(workingUser) //I get the error " 'mainWIndow' does not refer to a value "
}
[...]
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QDir>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void changeText(QDir user); //this is the declaration of my custom SLOT (so the relevant bit)
private slots:
void on_userButton_clicked();
void on_settingsButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "userwindow.h"
#include "settingswindow.h"
#include "click_test_target.h"
#include "random_number_generator.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
[...]
}
[...]
//here I define the slot
void MainWindow::changeText(QDir user)
{
QString current = user.dirName();
ui->userLine->insert("The current working directory is: "); //"userLine" is the lineEdit I want to write the text to
ui->userLine->insert(current);
}
I know I'm doing something wrong with the object for the SLOT but can't figure out how to do it correctly.
If anyone could help me I would be very grateful.
Alternatively: perhaps there is another way to mirror the text from one lineEdit to another over multiple windows. If anybody could share a way to do this I would be equally grateful. Is there maybe a way to somehow define the variable "QDir workingUser;" in such a way as to be able to access and overwrite it in all of my .cpp files?
Thank you in advance for any help. Regards,
Alexander M.
"I get the error 'mainWindow' does not refer to a value"
I don't see you having any "mainWindow" named variable anywhere,
but you also mentioned that the MainWindow is the parent, which means you could get reference anytime, like:
MainWindow *mainWindow = qobject_cast<MainWindow *>(this->parent());
Also, your signal-handler (changeText(...) slot) should take QString as parameter (instead of QDir), this way you handle how exactly the conversion is handled, in case users type some random text in input-field (text-edit).
void changeText(const QString &input);
Finally, you either need to specify type:
QObject::connect(ui->outLineEdit, SIGNAL(textChanged(QString)), mainWindow, SLOT(changeText(QString));
Or, use the new Qt-5 syntax:
connect(ui->outLineEdit, &QLineEdit::textChanged,
mainWindow, &MainWindow::changeText);
You can create new signal (same params as lineEdit's textChanged) to userWindow which is connected to the textChanged signal of the lineEdit. Then connect that signal userWindow to mainWindow's slot.
//In userWindow.h
signals:
void textChanged(const QString&);
//In userWindow.cpp
connect(ui.lineEdit, &QLineEdit::textChanged, this, &userWindow::textChanged);
//In mainWindow.cpp
connect(userWindow, &userWindow::textChanged, this, &mainWindow::onTextChanged
Then in onTextChanged write the same text to mainWindow's lineEdit

Using a Slot to Create a Dialog

I need to create a new window over top of my current program (I'm attempting a dialog in this case) when a button is clicked. I have this working, but I am having trouble editing the content of the Dialog. As far as I'm aware, no .ui file is created for this and it is only being created when I connect the button to the function.
I have tried using a QMessageBox but was unable to resize the window how I needed. And still ran into the issue of adding the other elements.
void MyNameSpace::openInfoDialog()
{
QDialog* Dialog = new QDialog(this);
Dialog->setWindowTitle("View Stuff");
Dialog->setMinimumSize(500,250);
Dialog->adjustSize();
DialogRunner* msgRunner = new DialogRunner(Dialog, this);
msgRunner->safeExec();
}
This is my connect
connect(_Widget.InfoBtn, SIGNAL(clicked(bool)), this, SLOT(openInfoDialog()));
This code does produce a dialog on click, but I need to be able to add things like labels etc to it. I also use QT Designer as my WYSIWYG.
What can I do to create the new window from a button click and have it filled with other text etc ?
I have this working, but I am having trouble editing the content of
the Dialog
You can add ui files in your project:
Using that option Qt Creator will make a class with cpp and h files and a ui file in which you can add other widgets as you're used to.
use the import directives to use your class where you need it like you did in the code above and you will have your ui file available.
Then connect the clicked signal of your button with the slot of your dialog class. You can do that in the constructor of the class that holds the button.
You can read this approach more in detail in the book C++ GUI Programming
with Qt 4
on chapter 2 : Creating Dialogs.
It is available for free on the web. It uses Qt4, but in Qt5 it works the same way around.
EDIT: Here is a minimal example that shows you where you can use the needed parts: a mainwindow with a button on it (in the ui file). a Dialog class which also has a ui file (with several widgets on it). When clicking the button on the mainwindow, the dialog form is shown. Thjs is what I asked you for before. It makes it easier to communicate / test.
pro file
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = test
TEMPLATE = app
SOURCES += \
dialog1.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
dialog1.h \
mainwindow.h
FORMS += \
dialog1.ui \
mainwindow.ui
**main.cpp**
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
**mainwindow.cpp**
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog1.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
Dialog1* dialog = new Dialog1(this);
connect( ui->pushButton_1, SIGNAL(clicked()), dialog, SLOT(show()));
}
MainWindow::~MainWindow()
{
delete ui;
}
dialog1.h
namespace Ui {
class Dialog1;
}
class Dialog1 : public QDialog
{
Q_OBJECT
public:
explicit Dialog1(QWidget *parent = nullptr);
~Dialog1();
private:
Ui::Dialog1 *ui;
};
#endif // DIALOG1_H
dialog1.cpp
#include "dialog1.h"
#include "ui_dialog1.h"
Dialog1::Dialog1(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog1)
{
ui->setupUi(this);
}
Dialog1::~Dialog1()
{
delete ui;
}

Qt custom SLOT of subclass not recognised by CONNECT

Background
I am writing a media player application in Qt. I have subclassed QMediaPlayer and created a new SLOT which is capable of interpreting an int and passing it as qint64 to QMediaPlayer::SetPosition(qint64).
QMediaPlayer::PositionChanged fires a signal to the cslider slider_playback (a horizontal slider subclass). This makes the slider move as a song is playedback. There are also some subclassed labels (clabel) which receive signals regarding the song duration and song playback position.
Problem
The problem i have occurs when I build & run, I receive the following error:
Starting /home/daniel/DeveloperProjects/build-Player-Desktop_Qt_5_9_1_GCC_64bit-Debug/Player...
QObject::connect: No such slot QMediaPlayer::set_playback_position(int) in ../Player/mainwindow.cpp:23
QObject::connect: (sender name: 'slider_playback')
The slider should be able to control the position of the playback. The offending line of code is preceeded by a '// Player seek' comment in the file.
I think the error indicates that the base class is being referred to for the SLOT, whereas the slots is actually part ofthe derived class.
Why is this error occuring and what action would resolve the issue? The slots in my clabel and cslider classes work without issue. The difference is that those classes have constructors and destructors. I have not implemented a constructor in the QMediaPlayer subclass as I do not want to override the base class constructor.
cmediaplayer.h (full file)
#ifndef CMEDIAPLAYER_H
#define CMEDIAPLAYER_H
#include <QMediaPlayer>
//#include <QObject>
class cmediaplayer : public QMediaPlayer
{
Q_OBJECT
public slots:
void set_playback_position(int);
};
#endif // CMEDIAPLAYER_H
cmediaplayer.cpp (full file)
#include "cmediaplayer.h"
void cmediaplayer::set_playback_position(int v) {
this->setPosition( (qint64)v );
}
mainwindow.h (full file)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMediaPlayer>
#include "cmediaplayer.h"
#include "clabel.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QPalette m_pal;
QString media_file_str="/usr/share/example-content/Ubuntu_Free_Culture_Showcase/Jenyfa Duncan - Australia.ogg";
//QMediaPlayer * player ;
cmediaplayer * player; // My custom type
private slots:
void on_pushButton_pressed();
void on_pushButton_released();
void on_button_playback_clicked();
};
mainwindow.cpp (full file)
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
//player = new QMediaPlayer;
player = new cmediaplayer; // My custom type
// set max time on playback slider
connect(player, SIGNAL(durationChanged(qint64)), ui->slider_playback, SLOT(set_qint64_max(qint64)));
// st max time on playback label
connect(player, SIGNAL(durationChanged(qint64)), ui->label_track_length, SLOT(setIntText(qint64)));
// set slider playback position
connect(player, SIGNAL(positionChanged(qint64)), ui->label_track_position, SLOT(setIntText(qint64)));
// Player seek
connect(ui->slider_playback,
SIGNAL(valueChanged(int)),
player,
SLOT(set_playback_position(int)));
// Player volume
connect(ui->slider_volume,SIGNAL(valueChanged(int)),player,SLOT(setVolume(int)));
ui->slider_volume->setValue(50); // set player initial value
}
MainWindow::~MainWindow() {
delete ui;
delete player;
//delete playlist;
}
void MainWindow::on_pushButton_pressed() {
m_pal=this->palette().color(QPalette::Background);
QPalette pal=palette();
pal.setColor(QPalette::Background,Qt::gray);
this->setAutoFillBackground(true);
this->setPalette(pal);
player->setMedia(QUrl::fromLocalFile(media_file_str));
player->setPlaybackRate(1);
player->play();
}
void MainWindow::on_pushButton_released() {
QPalette pal=m_pal;
this->setAutoFillBackground(true);
this->setPalette(pal);
//player->stop();
}
void MainWindow::on_button_playback_clicked()
{
//player->play();
}
Qt creates a new class that implements the actual connection between the slots and the signals but many times this one is not updated. The classes they refer to have a name similar to moc_xxx.cpp and these are created in the build folder.
To force them to update we must click on the make clean submenu that is located in the Menu Build of QtCreator, and then run qmake in the same menu.
Or you can manually remove the build folder and compile it back
Perhaps, it could help somebody. I had a similar issue when trying to use a SLOT function in the connect call. Even though the slot was declared and defined in the subclass, the connect function not recognize it, issuing a message which says that the slot function is not part of the base class.
In my case, the problem was that I didn't place the Q_OBJECT in the beginning of the class declaration. After that, I had to clean (otherwise, various errors occur in compilation step) and build the project again to had the slot function working properly.

Initializing a Ui pointer From a QMainWindow class to a QDialog Class

I'm really stuck on one problem that I want to solve. the problem is that I have a Class for QMainWindow which holds the Ui variable for that form. Now I want to be able to edit that Form using the Ui variable in that class on a QDialog cpp file. I probably sound really stupid and I really have no idea how I should explain this, but I have code which maybe can help.
MainWindow.h:
#include "ui_mainwindow.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
Ui::MainWindow *ui;
}
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog.h"
Dialog *dialog;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
dialog = new Dialog(this);
dialog->show();
}
QDialog.cpp:
#include "ui_mainwindow.h"
#include "mainwindow.h"
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
Ui::MainWindow *mainui;
void Dialog::on_pushbutton_clicked(){
mainui->label->setText("test");
}
So as you can see from the above code, it shows that I have a pointer to the Ui variable however its uninitialised, therefore it would lead to a SIGSEGV error, so how to do Initialize this pointer? any help here is highly appreciated, and even though this is probably really simple I just don't know what to do. (I have looked at other questions but I couldn't quite grasp what to do, so please explain what I am to do before linking me to a similar question. Also, I have left out the Dialog.h file as I didn't think it was needed, please tell me if I need to show it, thanks!).
Generally in C++ you should practice what is called encapsulation - keep data inside a class hidden from others that don't need to know about it. It's not good to have multiple pointers to the UI object as now all those other objects have to know how the main window UI is implemented.
In this case, what I would recommend is to use Qt's signals and slots mechanism to allow the dialog to tell the main window what you need it to do. That has the advantage that if you add more dialogs, or change how things are implemented in the main window, you don't need to alter the signal slot mechanism, and the details are hidden cleanly.
So - for your dialog, add a signal like this in the header file
class Dialog : QDialog
{
Q_OBJECT
signals:
void setTextSignal(QString text);
}
and in your main window header, add a slot.
class MainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void setTextSlot(const QString &text);
}
now in your method where the button is pressed,
void Dialog::on_pushbutton_clicked()
{
emit setTextSignal("test");
}
and in your main window
void MainWindow::setTextSlot(const QString &text)
{
mainUi->label->setText(text);
}
The final part is to connect the signal and slot together, which you would do in your main window function where you create the dialog:
void MainWindow::on_pushButton_clicked()
{
dialog = new Dialog(this);
connect(dialog, SIGNAL(setTextSignal(QString)), this, SLOT(setTextSlot(QString)));
dialog->show();
}
You can see there are many advantages to this; the Dialog no longer needs a pointer to the main window UI, and it makes your code much more flexible (you can have other objects connected to the signals and slots as well).
Short answere - your can't! If you want to create a new instance of the ui, you would have to do:
MainWindow::Ui *ui = new MainWindow::UI();
ui->setupUi(this);
However, the this-pointer for a UI created for a QMainWindow based class must inherit QMainWindow - thus, you can't.
In general, it is possible if you create your Ui based on a QWidget instead of a QMainWindow, since both inherit QWidget.
Alternativly, you could try the following:
QMainWindow *subWindow = new QMainWindow(this);
subWindow->setWindowFlags(Qt::Widget);
MainWindow::Ui *ui = new MainWindow::UI();
ui->setupUi(subWindow );
//... add the mainwindow as a widget to some layout
But I would guess the result will look weird and may not even work in the first place.

Connect not working properly while using QDialog as a popup

I am having a problem with connect in the program I am currently writing. I first create a "main window" dialog that contains buttons, line edits, etc. (which all work perfectly fine with my custom slots). One of the buttons (the "Add Class" button) should create a new pop up dialog that is a child of the mainWindow dialog. I wrote a new .h and .cpp for this new dialog (addClass.h and addClass.cpp). When I click the button, the dialog modality is set to ApplicationModal and up to this point, the code works; when I click "Add Class" and new dialog shows up as a pop up with all the labels, line edits, and buttons that I want. The problem comes in when I try and use the connect using this new class. Upon clicking the ok button the connect is not executed. The program compiles properly (using qmake and then make) and gives no errors during run time. I also took the .h and .cpp files from the pop up dialog and tested them with their own main.cpp and the connect worked perfectly. I am stumped as to what the problem could be, so any help would be awesome!
Here are some snipets of code that might be helpful:
the custom slot that initiates the pop up dialog in mainWindow.cpp (works and I include "addClass.h" in mainWindow.cpp):
void mainWindow::addClassCombo(){
addClass aC(win);
}
addClass.h:
#ifndef ADDCLASS_H
#define ADDCLASS_H
#include <QDialog>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QString>
class addClass : public QDialog{
Q_OBJECT
public:
addClass(QWidget *parent = 0);
private slots:
void addToTxt();
private:
QDialog *addMathClass;
QVBoxLayout *mainLayout;
QHBoxLayout *layoutOkCanc;
QLabel *nameL; //label for name of math class to be added
QLineEdit *name; //line edit for name
QPushButton *ok; //ok button
QPushButton *canc; //cancel button
};
#endif
addClass.cpp (works with its own main.cpp but not the one with my mainWindow.cpp):
#include <QtGui>
#include <QTextStream>
#include "addClass.h"
#include <iostream>
addClass::addClass(QWidget *parent):QDialog(parent){
addMathClass = new QDialog(parent);
mainLayout = new QVBoxLayout(addMathClass);
layoutOkCanc = new QHBoxLayout();
nameL = new QLabel("Math Class Name:");
name = new QLineEdit;
nameL->setBuddy(name);
ok = new QPushButton("Ok");
canc = new QPushButton("Cancel");
QObject::connect(canc, SIGNAL(clicked()), addMathClass, SLOT(close()) ); //<-works
QObject::connect(ok, SIGNAL(clicked()), this, SLOT(addToTxt()) ); //<-doesn't work
QObject::connect(ok, SIGNAL(clicked()), addMathClass, SLOT(close()) ); //<-works
layoutOkCanc->addStretch();
layoutOkCanc->addWidget(ok);
layoutOkCanc->addWidget(canc);
mainLayout->addWidget(nameL);
mainLayout->addWidget(name);
mainLayout->addLayout(layoutOkCanc);
addMathClass->setWindowModality(Qt::ApplicationModal);
addMathClass->setWindowTitle("Add Class");
addMathClass->show();
}
void addClass::addToTxt(){
std::cout<<"testing"<<std::endl;
}
Your addClass aC(win); goes out of scope and is being destroyed. Connection works, but after object destruction is disconnected. That's why you're not getting slot called