How can I access the content of a widget outside the constructor? - qt

I want to display the content I wrote in the QLineEdit widget after clicking on the QPushButton with the function ShowMessage(). How can I access that content outside of the constructor?
Tried putting the QLineEdit object I created in to a private variable.
My CPP file
#include "manualwidget.h"
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QMessageBox>
ManualWidget::ManualWidget(QWidget *parent) : QWidget(parent)
{
QLabel *label = new QLabel(this);
QLineEdit *lineEdit = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
QHBoxLayout *layout = new QHBoxLayout();
label->setText("Enter text:");
pushButton->setText("Ok");
layout->addWidget(label);
layout->addWidget(lineEdit);
layout->addWidget(pushButton);
setLayout(layout);
connect(pushButton,SIGNAL(clicked()),this ,SLOT(showMessage()));
connect(lineEdit, SIGNAL(returnPressed()),this, SLOT(showMessage()));
}
void ManualWidget::showMessage(){
QMessageBox::information(this, "Message", "The text entered in the "
"manual widget window is:\n" + m_lineEdit->text());
}
My header file
#ifndef MANUALWIDGET_H
#define MANUALWIDGET_H
#include <QWidget>
#include <QLineEdit>
class ManualWidget : public QWidget
{
Q_OBJECT
public:
explicit ManualWidget(QWidget *parent = nullptr);
signals:
public slots:
private slots:
void showMessage();
private:
QLineEdit m_lineEdit;
};
#endif // MANUALWIDGET_H

#eyllanesc suggestion might work, but it should not be a prefered approach. Qt has its' own memory model, and usage of it should be prefered. Thus, "QLineEdit m_lineEdit" should be changed to e.g. "QLineEdit* m_lineEdit", and in constructor you should initialize it in the following way:
// Instance of the QLineEdit will be owned by the ManualWidget which is part of Qt memory management now.
m_lineEdit = new QLineEdit(this);
Then, the following line:
layout->addWidget(lineEdit);
Can be changed to:
layout->addWidget(m_lineEdit);
Why is it bad to use "QLineEdit m_lineEdit"? Because Qt might want to destroy this object for some reason (you still can call m_lineEdit.deleteLater()) and you might end up in the "double destruction" situation which would result in app being crashed. You can say that in this way conflicting memory models would interact.

Related

Reparenting a Qt widget

I have a WidgetA widget, which is an owner-drawn widget. It's currently placed in QMainWindow's QVBoxLayout. After clicking a button, I'd like to "detach" WidgetA from this QVBoxLayout, insert QSplitter into this QVBoxLayout and "readd" WidgetA to this QSplitter. All this without destroying WidgetA, so it will preserve its drawing context, etc.
So, currently I have this (only one widget in a window):
I'd like to put a QSplitter between WidgetA and QMainWindow, and create a new widget, WidgetB, so I'd end up with:
Later I'd like it to split even further, so both WidgetA and WidgetB would still allow themselves to be detached and placed in a new QSplitter, so it would be possible to create f.e. this hierarchy:
And, to be complete, one more step:
I'm not very experienced in Qt, so what I'm trying to do may seem pretty obvious, but I couldn't find how to "reparent" widgets. Is this possible in Qt?
Please, see reparent example, may be it helps you:
//MyMainWindow.h
#include <QWidget>
#include <QPainter>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QSplitter>
class MyWidget: public QWidget
{
public:
MyWidget(QWidget* parent, int number)
: QWidget(parent),
m_number(number)
{
}
private:
virtual void paintEvent(QPaintEvent* e)
{
QWidget::paintEvent(e);
QPainter p(this);
p.fillRect( rect(), Qt::red);
p.drawText( rect(), Qt::AlignCenter, QString::number(m_number) );
}
private:
int m_number;
};
class MyMainWindow: public QWidget
{
Q_OBJECT
public:
MyMainWindow()
{
setFixedSize(300, 200);
m_mainLayout = new QVBoxLayout(this);
QHBoxLayout* buttonLayout = new QHBoxLayout;
m_mainLayout->addLayout(buttonLayout);
m_button = new QPushButton("Button", this);
buttonLayout->addWidget(m_button);
connect(m_button, SIGNAL(clicked()), this, SLOT(onButtonClickedOnce()));
m_initWidget = new MyWidget(this, 1);
m_mainLayout->addWidget(m_initWidget);
}
private slots:
void onButtonClickedOnce()
{
m_button->disconnect(this);
m_mainLayout->removeWidget(m_initWidget);
QSplitter* splitter = new QSplitter(Qt::Horizontal, this);
m_mainLayout->addWidget(splitter);
splitter->addWidget(m_initWidget);
MyWidget* newWidget = new MyWidget(splitter, 2);
splitter->addWidget(newWidget);
}
private:
QVBoxLayout* m_mainLayout;
QWidget* m_initWidget;
QPushButton* m_button;
};
//main.cpp
#include <QtWidgets/QApplication>
#include "MyMainWindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyMainWindow mainWindow;
mainWindow.show();
return a.exec();
}
When you operate with widget which is part of layout, then you need to use appropriate methods of QLayout (parent of QVBoxLayout) to detach the item from layout:
QLayout::removeWidget (removeItem if it is not widget, but spacer item or another layout)
QLayout::addWidget (addItem --/--)
Btw: even when widget moves between layouts, its parent may even stay same. I guess you have no need to call QWidget::setParent() as the calls of addWidget/removeWidget will do all work for you.

Connecting buttons to mainwindows slot

I tried to do a bunch of research on how to solve this problem, and everything is slightly different than my situation, or didn't work to fix my problem. 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). The actual buttons are being placed there, but they only work when pressed from my mainwindow.cpp....when I press them from a different class nothing happens.
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:
Ui::MainWindow *ui;
QLineEdit *tempValueBox;
QLineEdit *humidityValueBox;
QLineEdit *c02ValueBox;
...
public slots:
void ECSgeneralScreen();
void homeScreen();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ecsgeneralcommand.h"
#include <QtWidgets>
#include <QtCore>
MainWindow::MainWindow(QWidget *parent) : QDialog(parent)
{
QGridLayout *layout = new QGridLayout;
...
setLayout(layout);
}
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 home 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()));
}
ecsgeneralcommand.h
#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:
...
public slots:
};
#endif // ECSGENERALCOMMAND_H
ecsgeneralcommand.cpp
#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(550);
...
MainWindow setupButtons;
//Setup Subsystem Buttons
setupButtons.setupSubsystemButtons(layout);
setLayout(layout);
};
MainWindow setupButtons;
//Setup Subsystem Buttons
setupButtons.setupSubsystemButtons(layout);
This will create the buttons and connect their signals to slots of setupButtons, which will get deleted as soon as it's out of scope (the end of the ECSgeneralCommand constructor). So your buttons will be left connected to nothing.
You need to connect the button signals to an object that will exist at the time the button is pressed, such as the ECSgeneralCommand itself. Then it could close itself and spawn the correct window.
Or, possibly a much better solution, if applicable for your application: Use a single main window, with a QStackedWidget that switches widgets when a button is pressed. That's what's typically done.

SLOT rejected(), accepted() in QMainWindow

I am a newbie in Qt-programming. I have read a book about GUI-programming with Qt. I have an trouble in creating a dialog. Here is sample code:
// gotocell.h
#ifndef GOTOCELL_H
#define GOTOCELL_H
#include <QDialog>
#include <QtWidgets>
#include "ui_gotocell.h"
class GoToCellDialog : public QDialog, public Ui::GoToCellDialog
{
Q_OBJECT
public:
GoToCellDialog (QWidget *parent = 0);
private slots:
void on_lineEdit_textChanged();
};
#endif // GOTOCELL_H
// gotocell.cpp
#include <QtWidgets>
#include "gotocell.h"
#include <QtWidgets>
GoToCellDialog::GoToCellDialog (QWidget *parent):
QDialog (parent)
{
setupUi(this);
QRegExp regExp ("[A-Za-z][1-9][0-9]{0,2}");
lineEdit->setValidator(new QRegExpValidator(regExp, this));
connect (okButton, SIGNAL(clicked()), this, SLOT(accept()));
connect (cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
}
void GoToCellDialog::on_lineEdit_textChanged()
{
okButton->setEnabled(lineEdit->hasAcceptableInput());
}
// main.cpp
#include "gotocell.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GoToCellDialog *dialog = new GoToCellDialog;
dialog->show();
return a.exec();
}
but when I compiled, there is an error: no known conversion for argument 1 from 'GoToCellDialog* const' to 'QMainWindow*'at setupUi() function. I think because the designer in Qt Creator created a QMainWindow, not a QDialog. So I changed GoToCellDialog class to QMainWindow. But there is no slots whose name is "accepted", "rejected" in QMainWindow. Can anyone help me?
If you want to display a Dialog as main window you have two choices:
1. make the whole main window QDialog based
2. design the Dialog separately and set it as the main windows central Widget (QMainWindow->setCentralWidget()).
In both cases you still have the problem what semantics you give to the OK and Cancel buttons. Generally it may be a better idea to consider what the main window of the application should contain, and design the dialogs later.

Exit Application in Qt

I have built an app in Qt that contains two buttons: an exit button and an import button. When the import button is pushed, a list of buttons is shown in a scrollarea on the screen (the file loggers.csv contains the data 1;2;3;4;5;).
It all works fine, but when I push the exit button (which of course should close everything), the app is not stopped properly (the stop button of Qt is still active, and the play button isn't). When I run the debugger and push the exit button it gives an error: Invalid address specified to RtlFreeHeap( 0ADF0000, 0028FE40 ). Can anybody help me?
main
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.showFullScreen();
return a.exec();
}
Mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtGui>
#include "logger.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
QPushButton exit_btn;
QPushButton import_btn;
private slots:
void createMenus();
void exit();
void import();
private:
int window_width;
int window_height;
int numLoggers;
int numSelected;
QVector<Logger*> loggers;
QScrollArea * scroll_area;
QVBoxLayout scrollLayout;
QWidget viewport;
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QtGui"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
window_width = QApplication::desktop()->width();
window_height = QApplication::desktop()->height();
createMenus();
connect(&exit_btn,SIGNAL(clicked()),this,SLOT(exit()));
connect(&import_btn,SIGNAL(clicked()),this,SLOT(import()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::createMenus()
{
import_btn.setParent(ui->centralWidget);
import_btn.setGeometry(400,300,100,100);
import_btn.setText("IMPORT");
exit_btn.setText("EXIT");
exit_btn.setParent(ui->centralWidget);
exit_btn.setGeometry(window_width-50,12,32,32);
viewport.setLayout(&scrollLayout);
viewport.resize(0,0);
scroll_area = new QScrollArea(ui->centralWidget);
scroll_area->setGeometry(0,66,317,window_height-116);
scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setWidget(&viewport);
scroll_area->setGeometry(0,97,317,window_height-228);
scrollLayout.setMargin(0);
scrollLayout.setSpacing(0);
}
void MainWindow::exit()
{
close();
qApp->quit();
}
void MainWindow::import()
{
numSelected=0;
QFile f("Loggers3.csv");
if (f.open(QIODevice::ReadOnly))
{
numLoggers=0;
QString data;
data = f.readAll();
QStringList vals = data.split(';');
while(vals.size()>=1)
{
Logger * logger = new Logger;
logger->setNumber(vals[0].toInt());
vals.removeAt(0);
loggers<<logger;
numLoggers++;
}
f.close();
for(int i=0; i<numLoggers;i++)
{
loggers[i]->createButtons();
scrollLayout.addWidget(loggers[i]->button);
}
viewport.resize(367,numLoggers*60);
}
}
logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <QtGui>
class Logger : public QWidget
{
Q_OBJECT
public:
explicit Logger(QWidget *parent = 0);
~Logger();
int number;
QLabel num;
QToolButton * button;
bool checked;
signals:
public slots:
void setNumber(int number);
void createButtons();
};
#endif // LOGGER_H
logger.cpp
#include "logger.h"
#include <QtGui>
Logger::Logger(QWidget *parent) :
QWidget(parent)
{
button = new QToolButton;
button->setCheckable(true);
button->setMinimumSize(317,60);
button->setStyleSheet("QToolButton{background-image: url(images/btn_bg); border:none}");
}
Logger::~Logger()
{
}
void Logger::setNumber(int logNumber)
{
number=logNumber;
}
void Logger::createButtons()
{
QLayout * layout = new QHBoxLayout;
QSpacerItem *spacer = new QSpacerItem(120, 31, QSizePolicy::Maximum, SizePolicy::Maximum);
num.setStyleSheet("color: white; font: bold 16px");
num.setText(QString::number(number));
layout->addWidget(&num);
layout->addItem(spacer);
button->setLayout(layout);
}
I'm not entirely certain about what you are trying to achieve... but your problem lies with these two lines:
viewport.setLayout(&scrollLayout);
viewport.resize(0,0);
In the documentation for the QWidget class it states that:
If there already is a layout manager installed on this widget, QWidget
won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.
This is where your problem lies. Don't believe me, add this check before those two lines of code.
if(layout()){
qDebug() << "Another layout exists";
}
Source: QVBoxLayout Class Reference
The QVBoxLayout class lines up widgets vertically.
This class is used to construct vertical box layout objects. See QBoxLayout for details.
The simplest use of the class is like this:
QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
window->show();
First, we create the widgets we want in the layout. Then, we create the QVBoxLayout object and add the widgets into the layout. Finally, we call QWidget::setLayout() to install the QVBoxLayout object onto the widget. At that point, the widgets in the layout are reparented to have window as their parent.
Critical source of error in your project:
Widgets should be constructed on the heap because they will be deleted automatically when their parents are deleted. You have a custom widget class that you instantiate on the heap. The members should also go on the heap. Also, you should consider using the parent /child hierarchy in your GUI code to ensure proper memory management and proper deletion.
In my experience, if your program stops in RtlFreeHeap it is a good sign of memory corruption.
When calling
import_btn.setParent(ui->centralWidget);
centralWidget takes ownership of import_btn. That means, when centralWidget is deleted (which happens as part of delete ui;in your MainWindow's destructor), it will call delete on your member variable!
This leads to the reported memory corruption.
You need to allocate your QPushButton's dynamically, not as a plain member variable. So make them QPushButton*.
Here's how I did it from mainwindow.cpp, thanks to and this question: How to create a correct exit button in qt
QPushButton * quit_btn = new QPushButton(this);
quit_btn->setGeometry(540,440,93,27);
quit_btn->setText("Exit");
QObject::connect(quit_btn,SIGNAL(clicked()),qApp,SLOT(quit()));
Works flawlessly :D

How to Fit ScrollArea into Grid in QT?

How to Fit ScrollArea into Grid in QT ?
I want to apply QScrollArea to this part of the grid which contains first name, lastname...
Here is the sample. The size of QScrollArea is changed inside QGridLayout.
header file
#ifndef WIDGET_H
#define WIDGET_H
#include <QtGui/QWidget>
#include <QScrollArea>
#include <QTextEdit>
#include <QPushButton>
#include <QGridLayout>
class Widget: public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private:
QScrollArea *sca;
QTextEdit *txt;
QPushButton *btn;
private slots:
void onClicked();
};
#endif // WIDGET_H
source file
#include "widget.h"
#include <QScrollBar>
Mediator::Widget(QWidget *parent)
: QWidget(parent)
{
sca = new QScrollArea(this);
txt = new QTextEdit;
txt->setFixedSize(1000,500);
btn = new QPushButton("Button",this);
btn->setFixedSize(75,30);
QGridLayout *layout = new QGridLayout;
layout->addWidget(sca);
layout->addWidget(btn);
sca->setWidget(txt);
setLayout(layout);
connect(btn,SIGNAL(clicked()),this,SLOT(onClicked()));
}
Widget::~Widget()
{
}
void Widget::onClicked()
{
txt->setText(txt->toPlainText() + "abcd ");
}
QScrollArea is an ordinary widget and its size is controlled by the layout that contains it. You can change it size by calling its resize method.
However if you need the object contaied in QScrollArea to be resized as well than you should use QScrollArea::widgetResizable property. Take a look at Image Viewer example to see how the QLabel is resized along with QScrollArea.

Resources