How to print output data created by QDebug in a widget? - c++

I'm new to programming. Would like to know how to print the output data from the code below (C++) using the Qt. I need an answer to appear in the QTextEdit window.
for (int x=0; x<10; x++);

Here's how you might capture the qDebug messages in a QAbstractItemModel or QTextDocument. Both of those classes are models, their associated views are QListView (or any other view), and QTextEdit (preferably QPlainTextEdit or QTextBrowser), respectively.
// https://github.com/KubaO/stackoverflown/tree/master/questions/qdebug-window-output-52061269
#include <QtWidgets>
struct LogToModelData {
bool installed;
QtMessageHandler previous = {};
QList<QPointer<QObject>> models;
};
Q_GLOBAL_STATIC(LogToModelData, logToModelData)
void logToModelHandler(QtMsgType type, const QMessageLogContext &context,
const QString &msg) {
for (auto m : qAsConst(logToModelData->models)) {
if (auto model = qobject_cast<QAbstractItemModel *>(m)) {
auto row = model->rowCount();
model->insertRow(row);
model->setData(model->index(row, 0), msg);
} else if (auto doc = qobject_cast<QTextDocument *>(m)) {
QTextCursor cur(doc);
cur.movePosition(QTextCursor::End);
if (cur.position() != 0) cur.insertBlock();
cur.insertText(msg);
}
}
if (logToModelData->previous) logToModelData->previous(type, context, msg);
}
void logToModel(QObject *model) {
logToModelData->models.append(QPointer<QObject>(model));
if (!logToModelData->installed) {
logToModelData->previous = qInstallMessageHandler(logToModelHandler);
logToModelData->installed = true;
}
}
void rescrollToBottom(QAbstractScrollArea *view) {
static const char kViewAtBottom[] = "viewAtBottom";
auto *scrollBar = view->verticalScrollBar();
Q_ASSERT(scrollBar);
auto rescroller = [scrollBar]() mutable {
if (scrollBar->property(kViewAtBottom).isNull())
scrollBar->setProperty(kViewAtBottom, true);
auto const atBottom = scrollBar->property(kViewAtBottom).toBool();
if (atBottom) scrollBar->setValue(scrollBar->maximum());
};
QObject::connect(scrollBar, &QAbstractSlider::rangeChanged, view, rescroller,
Qt::QueuedConnection);
QObject::connect(scrollBar, &QAbstractSlider::valueChanged, view, [scrollBar] {
auto const atBottom = scrollBar->value() == scrollBar->maximum();
scrollBar->setProperty(kViewAtBottom, atBottom);
});
}
int main(int argc, char *argv[]) {
QApplication app{argc, argv};
QWidget ui;
QVBoxLayout layout{&ui};
QListView view;
QTextBrowser browser;
layout.addWidget(new QLabel(QLatin1String(view.metaObject()->className())));
layout.addWidget(&view);
layout.addWidget(new QLabel(QLatin1String(browser.metaObject()->className())));
layout.addWidget(&browser);
QStringListModel model;
view.setModel(&model);
logToModel(view.model());
logToModel(browser.document());
rescrollToBottom(&view);
rescrollToBottom(&browser);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [] {
static int n;
qDebug() << "Tick" << ++n;
});
timer.start(500);
ui.show();
return app.exec();
}
Also note that the separate declaration of the loop induction variable i is a very much obsolete C-ism - the last time you had to write code like that was two decades ago.

Related

How to lock an item in QListWidget Qt

is it possible to lock on an item in QListWidget, so when I press for example: a button, the item stays selected?
I have tried to look it up but I failed
I really hope you can provide me with an example, since I am just a beginner
Yes, I can. Here we go:
testQListWidgetLockSelection.cc:
#include <set>
#include <QtWidgets>
class ListWidget: public QListWidget {
public:
using QListWidget::QListWidget;
virtual ~ListWidget() = default;
void lockSelection(bool lock);
virtual void selectionChanged(
const QItemSelection &selected,
const QItemSelection &deselected) override;
private:
bool _lockSel = false;
std::set<int> _selLocked;
bool _lockResel = false;
struct LockGuard {
bool &lock;
LockGuard(bool &lock): lock(lock) { lock = true; }
~LockGuard() { lock = false; }
};
};
void ListWidget::lockSelection(bool lock)
{
_lockSel = lock;
if (_lockSel) {
// store selected indices
for (const QModelIndex &qMI : selectedIndexes()) _selLocked.insert(qMI.row());
} else _selLocked.clear();
}
void ListWidget::selectionChanged(
const QItemSelection& selected, const QItemSelection& deselected)
{
if (_lockSel && !_lockResel) {
const LockGuard lock(_lockResel);
QItemSelection reselect;
for (int row : _selLocked) {
const QModelIndex qMI = model()->index(row, 0);
reselect.select(qMI, qMI);
}
selectionModel()->select(reselect, QItemSelectionModel::Select);
}
}
void populate(QListWidget& qLstView)
{
for (int i = 1; i <= 20; ++i) {
qLstView.addItem(QString("Item %1").arg(i));
}
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QWidget qWinMain;
qWinMain.resize(640, 480);
qWinMain.setWindowTitle("Lock Selection Demo");
QVBoxLayout qVBox;
QCheckBox qTglLockSel("Lock Selection");
qVBox.addWidget(&qTglLockSel, false);
ListWidget qLstView;
qLstView.setSelectionMode(ListWidget::ExtendedSelection);
qVBox.addWidget(&qLstView, true);
qWinMain.setLayout(&qVBox);
qWinMain.show();
// install signal handlers
QObject::connect(&qTglLockSel, &QCheckBox::toggled,
&qLstView, &ListWidget::lockSelection);
// fill GUI with sample data
populate(qLstView);
// runtime loop
return app.exec();
}
Demo:
Note:
This sample has a weakness: It doesn't consider that items may be inserted or removed after the selection has been locked.
To enhance this, the member functions rowsInserted() and rowsAboutToBeRemoved() had to be overridden as well to correct the indices in ListWidget::_selLocked respectively.

QtConcurrent - keeping GUI responsive amid thousands of results posted to UI thread

I have an application that has potentially long-running tasks and also possibly thousands or millions or results.
This specific application (code below) isn't of any worth, but it is aimed to provide a general use case of the need to maintain a responsive UI amid 'thousands' of results.
To be clear, I am aware that one should reduce the number of times the UI is polled. My question is regarding design principles that can be applied to this (and other similar) scenarios in keeping a responsive UI.
My first thought is to use a QTimer and process all 'results' every e.g. 200ms, an example which can be found here but needs adation.
What methods are available and which are preferred to keep a responsive UI?
A simple example of I am trying to explain is as follows. I have a UI that:
generates a list of integers,
passes it into a mapped function to pow(x,2) the value, and
measure the progress
When running this app, click the 'start' button will run the application, but due to the frequency of results being processed by the QueuedConnection: QFutureWatcher::resultReadyAt, the UI cannot respond to any user clicks, thus attempting to 'pause' or 'stop' (cancel) is futile.
Wrapper for QtConcurrent::mapped() function passing in lambda (for a member function)
#include <functional>
template <typename ResultType>
class MappedFutureWrapper
{
public:
using result_type = ResultType;
MappedFutureWrapper<ResultType>(){}
MappedFutureWrapper<ResultType>(std::function<ResultType (ResultType)> function): function(function){ }
MappedFutureWrapper& operator =(const MappedFutureWrapper &wrapper) {
function = wrapper.function;
return *this;
}
ResultType operator()(ResultType i) {
return function(i);
}
private:
std::function<ResultType(ResultType)> function;
};
MainWindow.h UI
class MainWindow : public QMainWindow {
Q_OBJECT
public:
struct IntStream {
int value;
};
MappedFutureWrapper<IntStream> wrapper;
QVector<IntStream> intList;
int count = 0;
int entries = 50000000;
MainWindow(QWidget* parent = nullptr);
static IntStream doubleValue(IntStream &i);
~MainWindow();
private:
Ui::MainWindow* ui;
QFutureWatcher<IntStream> futureWatcher;
QFuture<IntStream> future;
//...
}
MainWindow implementation
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() << "Launching";
intList = QVector<IntStream>();
for (int i = 0; i < entries; i++) {
int localQrand = qrand();
IntStream s;
s.value = localQrand;
intList.append(s);
}
ui->progressBar->setValue(0);
}
MainWindow::IntStream MainWindow::doubleValue(MainWindow::IntStream &i)
{
i.value *= i.value;
return i;
}
void MainWindow::on_thread1Start_clicked()
{
qDebug() << "Starting";
// Create wrapper with member function
wrapper = MappedFutureWrapper<IntStream>([this](IntStream i){
return this->doubleValue(i);
});
// Process 'result', need to acquire manually
connect(&futureWatcher, &QFutureWatcher<IntStream>::resultReadyAt, [this](int index){
auto p = ((++count * 1.0) / entries * 1.0) * 100;
int progress = static_cast<int>(p);
if(this->ui->progressBar->value() != progress) {
qDebug() << "Progress = " << progress;
this->ui->progressBar->setValue(progress);
}
});
// On future finished
connect(&futureWatcher, &QFutureWatcher<IntStream>::finished, this, [](){
qDebug() << "done";
});
// Start mapped function
future = QtConcurrent::mapped(intList, wrapper);
futureWatcher.setFuture(future);
}
void MainWindow::on_thread1PauseResume_clicked()
{
future.togglePaused();
if(future.isPaused()) {
qDebug() << "Paused";
} else {
qDebug() << "Running";
}
}
void MainWindow::on_thread1Stop_clicked()
{
future.cancel();
qDebug() << "Canceled";
if(future.isFinished()){
qDebug() << "Finished";
} else {
qDebug() << "Not finished";
}
}
MainWindow::~MainWindow()
{
delete ui;
}
Explanation of why the UI is 'not responding'.
The UI loads w/o performing any action other than printing "Launching". When the method on_thread1Start_clicked() is invoked, it started the future, in addition to adding the following connection:
connect(&futureWatcher, &QFutureWatcher<IntStream>::resultReadyAt, [this](int index){
auto p = ((++count * 1.0) / entries * 1.0) * 100;
int progress = static_cast<int>(p);
if(this->ui->progressBar->value() != progress) {
qDebug() << "Progress = " << progress;
this->ui->progressBar->setValue(progress);
}
});
This connection listens for a result from the future, and acts upon it (this connect function runs on the UI thread). Since I am emulating a massive amount of 'ui updates', shown by int entries = 50000000;, each time a result is processed, the QFutureWatcher<IntStream>::resultReadyAt is invoked.
While this is running for +/- 2s, the UI does not respond to the 'pause' or 'stop' clicks linked to on_thread1PauseResume_clicked() and on_thread1Stop_clicked respectively.
Your approach of using QtConcurrent::mapped makes perfect sense, and I think that in theory it could be a good way of solving such a problem. The problem here is that the number of events that are added to the event queue are just too much to keep the UI responsive.
The reason why the UI is not responding is that you have only one event queue in the GUI thread. As a consequence your button clicked events are queued together with the resultReadyAt events. But the queue is just that, a queue, so if your button event enter the queue after say 30'000'000 of resultReadyAt event, it will be processed only when it comes its turn. The same holds for resize and move events. As a consequence the UI feels sluggish and not responsive.
One possibility would be to modify your mapping function so that instead of a single data point receives a chunk of the data. For example I'm splitting the 50'000'000 data in 1000 batch of 50'000 data. You can see that in this case the UI is responsive during all the execution. I have also added a 20ms delay in each function otherwise the execution is so fast that I cannot even press the stop/pause button.
There are also a couple of minor comments to your code:
In principle you don't need a wrapper class since you can pass the member function directly (again see my first example below). If you have problem maybe it's related to the Qt version or compiler that you are using.
You are actually changing the value you pass to doubleValue. That actually makes useless returning a value from the function.
#include <QApplication>
#include <QMainWindow>
#include <QProgressBar>
#include <QPushButton>
#include <QRandomGenerator>
#include <QtConcurrent>
#include <QVBoxLayout>
class Widget : public QWidget {
Q_OBJECT
public:
struct IntStream {
int value;
};
Widget(QWidget* parent = nullptr);
static QVector<IntStream> doubleValue(const QVector<IntStream>& v);
public slots:
void startThread();
void pauseResumeThread();
void stopThread();
private:
static constexpr int BATCH_SIZE {50000};
static constexpr int TOTAL_BATCHES {1000};
QFutureWatcher<QVector<IntStream>> m_futureWatcher;
QFuture<QVector<IntStream>> m_future;
QProgressBar m_progressBar;
QVector<QVector<IntStream>> m_intList;
int m_count {0};
};
Widget::Widget(QWidget* parent) : QWidget(parent)
{
auto layout {new QVBoxLayout {}};
auto pushButton_startThread {new QPushButton {"Start Thread"}};
layout->addWidget(pushButton_startThread);
connect(pushButton_startThread, &QPushButton::clicked,
this, &Widget::startThread);
auto pushButton_pauseResumeThread {new QPushButton {"Pause/Resume Thread"}};
layout->addWidget(pushButton_pauseResumeThread);
connect(pushButton_pauseResumeThread, &QPushButton::clicked,
this, &Widget::pauseResumeThread);
auto pushButton_stopThread {new QPushButton {"Stop Thread"}};
layout->addWidget(pushButton_stopThread);
connect(pushButton_stopThread, &QPushButton::clicked,
this, &Widget::stopThread);
layout->addWidget(&m_progressBar);
setLayout(layout);
qDebug() << "Launching";
for (auto i {0}; i < TOTAL_BATCHES; i++) {
QVector<IntStream> v;
for (auto j {0}; j < BATCH_SIZE; ++j)
v.append(IntStream {static_cast<int>(QRandomGenerator::global()->generate())});
m_intList.append(v);
}
}
QVector<Widget::IntStream> Widget::doubleValue(const QVector<IntStream>& v)
{
QThread::msleep(20);
QVector<IntStream> out;
for (const auto& x: v) {
out.append(IntStream {x.value * x.value});
}
return out;
}
void Widget::startThread()
{
if (m_future.isRunning())
return;
qDebug() << "Starting";
m_count = 0;
connect(&m_futureWatcher, &QFutureWatcher<IntStream>::resultReadyAt, [=](int){
auto progress {static_cast<int>(++m_count * 100.0 / TOTAL_BATCHES)};
if (m_progressBar.value() != progress && progress <= m_progressBar.maximum()) {
m_progressBar.setValue(progress);
}
});
connect(&m_futureWatcher, &QFutureWatcher<IntStream>::finished,
[](){
qDebug() << "Done";
});
m_future = QtConcurrent::mapped(m_intList, &Widget::doubleValue);
m_futureWatcher.setFuture(m_future);
}
void Widget::pauseResumeThread()
{
m_future.togglePaused();
if (m_future.isPaused())
qDebug() << "Paused";
else
qDebug() << "Running";
}
void Widget::stopThread()
{
m_future.cancel();
qDebug() << "Canceled";
if (m_future.isFinished())
qDebug() << "Finished";
else
qDebug() << "Not finished";
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
Another really good alternative could be using a separate working thread as suggested by Jeremy Friesner. If you want we can elaborate on that too =)

Implementations of buttons in Qt / C++

Im looking to implement a sudoku gui in Qt. First things first im trying to generate a grid of QToolButtons, which when pressed will increment their value by one.
Here is my header for window.h
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = nullptr);
private slots:
void digitClicked();
private:
Button *createButton(const QString &text, const char *member);
QString *readFileChar(const QString file_name, QString digit[] );
QGridLayout *previewLayout;
void setSudokuChar(const int digit, const char *member);
enum { NumDigitButtons = 81 };
Button *digitButtons[NumDigitButtons];
QString digit[NumDigitButtons];
protected:
void paintEvent(QPaintEvent *event) override;
void mouseReleaseEvent(QMouseEvent * event) override;
};
And the corresponding source file window.cpp
Window::Window(QWidget *parent) : QWidget(parent) //constrcutor
{
//button creation
QString* current_digit = readFileChar("/home/pi/Documents/ELEC1204/P6/sudoku.txt", digit);
for(int i = 0; i < NumDigitButtons; i++){
digitButtons[i] = createButton(*(current_digit++), SLOT(digitClicked())); //initialize sudoku
}
// set form size
QGridLayout *mainLayout = new QGridLayout;
setMaximumSize(600,600);
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
setWindowTitle(tr("button game"));
// place each digit button
for(int i = 1; i < NumDigitButtons; i++){
int row = (81 - i) / 9 ; // y position for button
int col = (i - 1) % 9 ; // x position for button
mainLayout->addWidget(digitButtons[i], row, col);
}
}
void Window::digitClicked()
{
Button *clickedButton = qobject_cast<Button*>(sender()); //clicked button = object button that was pressed
int val = ++(clickedButton->text().toInt()); //incrememt digit value of the button which was pressed
setSudokuChar(val, clickedButton); //updates sudoku with new value
}
QString* Window::readFileChar(const QString file_name, QString digit[]) //reads in file to initialize buttons
{
QFile file(file_name);
int i = 0;
if(file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream in(&file);
while(!in.atEnd()){
digit[i] = in.read(1); //read each character into an array of QStrings
i++ ;
}
}
else{
QTextStream(stdout) << "could not open file, program terminating" << endl;
exit(1);
}
return digit;
}
Button *Window::createButton(const QString &text, const char *member)
{
Button *button = new Button(text);
connect(button, SIGNAL(clicked()), this, member);
return button;
}
void setSudokuChar(const int digit, Button* clickedButton) //takes in a digit, and assigns its value to the char that was pass
{
}
Please note that the function to update the button's value when pressed 'setSudokuChar' is yet to be implemented, but that pretty irrelevant for now.
Here is the button class header button.h, it's only a small modification of the QToolButton class to aid in resizing.
class Button : public QToolButton
{
Q_OBJECT
public:
explicit Button(const QString &text, QWidget *parent = nullptr);
QSize sizeHint() const override;
};
And finally, here is the button.cpp source file
Button::Button(const QString &text, QWidget *parent) : QToolButton(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
setText(text);
}
QSize Button::sizeHint() const
{
QSize size = QToolButton::sizeHint();
size.rheight() += 20;
size.rwidth() = qMax(size.width(), size.height());
return size;
}
Hopefully thats not too much to digest, I'm still a beginner!
**My problem is ... ** when I run the code, a window is produced but its blank, which means the compilation is fine, but the program is not working as intended. Could anyone by chance spot what is going wrong? All that i do in the main function is create a window as follows :
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
Any and all help is thoroughly appreciated.

Get response from QProcess in real time

I am new in c++ programming , so i need help with the logic (code would be awesome). I want to make QTextEdit to work as a terminal like widget.
I am trying to achieve that with QProcess like:
void QPConsole::command(QString cmd){
QProcess* process = new QProcess();
connect(process, &QProcess::readyReadStandardOutput, [=](){ print(process->readAllStandardOutput()); });
connect(process, &QProcess::readyReadStandardError, [=](){ print(process->readAllStandardError()); });
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),[=](int exitCode, QProcess::ExitStatus exitStatus){ prepareCommandLine(); return; });
process->setProgram("/bin/bash");
process->start();
process->write(cmd.toStdString().c_str());
// process->write("\n\r");
process->closeWriteChannel();
}
The problem is next:
If i don't close write channel (process->closeWriteChannel) than i am stuked in infinite loop. And if i do close it, than i cannot remember state (kinda makes new session) for example if i do "pwd" i get result, but if i do next "cd .." and then "pwd" the result is same as the first output of "pwd"
So, my question is is it possible to achieve some kind of real time output + having previous state remembered (like in real terminal) with QProcess or i have to do in some other way. In python i did it with subprocess calls with pipes, but i don't know what is the equilavent for c++.
An code example would be great. I have QTextEdit part (which is sending QString param to function), i need part with interaction with console.
The idea is simple: Keep an instance of QProcess and write commands to it followed by \n!
Here is a full example, with a QMainWindow and a Bash class, put it in main.cpp:
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class Bash : public QObject
{
Q_OBJECT
public:
explicit Bash(QObject *parent = nullptr) : QObject(parent)
{
}
~Bash()
{
closePrivate();
}
signals:
void readyRead(QString);
public slots:
bool open(int timeout = -1)
{
if (m_bash_process)
return false;
m_timeout = timeout;
return openPrivate();
}
void close()
{
closePrivate();
}
bool write(const QString &cmd)
{
return writePrivate(cmd);
}
void SIGINT()
{
SIGINTPrivate();
}
private:
//Functions
bool openPrivate()
{
if (m_bash_process)
return false;
m_bash_process = new QProcess();
m_bash_process->setProgram("/bin/bash");
//Merge stdout and stderr in stdout channel
m_bash_process->setProcessChannelMode(QProcess::MergedChannels);
connect(m_bash_process, &QProcess::readyRead, this, &Bash::readyReadPrivate);
connect(m_bash_process, QOverload<int>::of(&QProcess::finished), this, &Bash::closePrivate);
m_bash_process->start();
bool started = m_bash_process->waitForStarted(m_timeout);
if (!started)
m_bash_process->deleteLater();
return started;
}
void closePrivate()
{
if (!m_bash_process)
return;
m_bash_process->closeWriteChannel();
m_bash_process->waitForFinished(m_timeout);
m_bash_process->terminate();
m_bash_process->deleteLater();
}
void readyReadPrivate()
{
if (!m_bash_process)
return;
while (m_bash_process->bytesAvailable() > 0)
{
QString str = QLatin1String(m_bash_process->readAll());
emit readyRead(str);
}
}
bool writePrivate(const QString &cmd)
{
if (!m_bash_process)
return false;
if (runningPrivate())
return false;
m_bash_process->write(cmd.toLatin1());
m_bash_process->write("\n");
return m_bash_process->waitForBytesWritten(m_timeout);
}
void SIGINTPrivate()
{
if (!m_bash_process)
return;
QProcess::startDetached("pkill", QStringList() << "-SIGINT" << "-P" << QString::number(m_bash_process->processId()));
}
bool runningPrivate()
{
if (!m_bash_process)
return false;
QProcess process;
process.setProgram("pgrep");
process.setArguments(QStringList() << "-P" << QString::number(m_bash_process->processId()));
process.setProcessChannelMode(QProcess::MergedChannels);
process.start();
process.waitForFinished(m_timeout);
QString pids = QLatin1String(process.readAll());
QStringList pid_list = pids.split(QRegularExpression("\n"), QString::SkipEmptyParts);
return (pid_list.size() > 0);
}
//Variables
QPointer<QProcess> m_bash_process;
int m_timeout;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
{
connect(&m_bash, &Bash::readyRead, this, &MainWindow::readyRead);
QWidget *central_widget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(central_widget);
layout->addWidget(&m_message_board);
layout->addWidget(&m_cmd_edit);
layout->addWidget(&m_sigint_btn);
m_message_board.setReadOnly(true);
m_cmd_edit.setEnabled(false);
m_sigint_btn.setText("Send SIGINT(CTRL+C)");
connect(&m_cmd_edit, &QLineEdit::returnPressed, this, &MainWindow::writeToBash);
connect(&m_sigint_btn, &QPushButton::clicked, this, &MainWindow::SIGINT);
setCentralWidget(central_widget);
resize(640, 480);
QTimer::singleShot(0, this, &MainWindow::startBash);
}
~MainWindow()
{
m_bash.close(); //Not mandatory, called by destructor
}
private slots:
void startBash()
{
//Open and give to each operation a maximum of 1 second to complete, use -1 to unlimited
if (m_bash.open(1000))
{
m_cmd_edit.setEnabled(true);
m_cmd_edit.setFocus();
}
else
{
QMessageBox::critical(this, "Error", "Failed to open bash");
}
}
void writeToBash()
{
QString cmd = m_cmd_edit.text();
m_cmd_edit.clear();
if (!m_bash.write(cmd))
{
QMessageBox::critical(this, "Error", "Failed to write to bash");
}
}
void readyRead(const QString &str)
{
m_message_board.appendPlainText(str);
}
void SIGINT()
{
m_bash.SIGINT();
}
private:
Bash m_bash;
QPlainTextEdit m_message_board;
QLineEdit m_cmd_edit;
QPushButton m_sigint_btn;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"

Signals and Slots

I have written some code, where if I log in as a customer it should hide column customer (value of that column is 1). I need to pass parameter to the signals. I have used my customerLogin is isSa. How to pass these parameters to signals and slots?
Main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
initialize();
SafeOTPWindow w;
Login login;
login.connect(&login, SIGNAL(loggedIn()), &w, SLOT(show()) );
login.connect(&login, SIGNAL(loginCancelled() ), &w, SLOT(close()) );
login.show();
a.exec();
}
Login.h:
#ifndef LOGIN_H
#define LOGIN_H
#include <QDialog>
namespace Ui {
class Login;
}
class Login : public QDialog
{
Q_OBJECT
public:
explicit Login(QWidget *parent = 0);
~Login();
bool isSa;
signals:
void loggedIn();
void loginCancelled();
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
private:
Ui::Login *ui;
};
#endif // LOGIN_H
And here is my another file where I need to hide the column (Customer), OtpWindow.cpp:
void SafeOTPWindow::initLogTable()
{
QList<OtpLog> logs;
int ret = otpRepo.fetchOtpLogs(logs);
if( ret != errorCodes::SUCCESS )
{
QMessageBox msgBox(QMessageBox::Critical, QString("SafeOTP"),
QString("OTPLogs could not be fetched"),QMessageBox::Ok, this);
msgBox.exec();
QLOG_ERROR() << "fetchLogs error " << ret;
return;
}
QStandardItemModel *model = new QStandardItemModel(0,5,this); //5 columns
model->setHorizontalHeaderItem(0, new QStandardItem(QString("Date")));
model->setHorizontalHeaderItem(1, new QStandardItem(QString("Customer")));
model->setHorizontalHeaderItem(2, new QStandardItem(QString("Server")));
model->setHorizontalHeaderItem(3, new QStandardItem(QString("Authorized by")));
model->setHorizontalHeaderItem(4, new QStandardItem(QString("Description")));
for(QList<OtpLog>::Iterator lIt = logs.begin(); lIt != logs.end(); lIt++)
{
OtpLog& log = *lIt;
QList<QStandardItem*> row;
row.push_back(new QStandardItem(log.when.toString("dd MMM yyyy, hh:mm")));
row.push_back(new QStandardItem(QString(log.customer)));
row.push_back(new QStandardItem(QString(log.server)));
row.push_back(new QStandardItem(QString(log.author)));
row.push_back(new QStandardItem(QString(log.reason)));
model->appendRow(row);
}
// set the data model
ui->tblLog->setModel(model);
//Set the column to hide
ui->tblLog->setColumnHidden(1,true);
// set the column widths
int tw = ui->tblLog->width() - 5;
int w = tw / 6;
for(int i=0; i<4;i++)
{
ui->tblLog->setColumnWidth(i,w);
tw -= w;
}
ui->tblLog->setColumnWidth(4,tw);
}
For the slot/signal example, you need to connect two objects having the same parameters. so let me modify a bit your example:
Let's say you have:
signals:
void loginStatusChanged(bool isLoggedIn);
In your Login method you will have something like:
bool status = checkLogin();
emit loginStatusChanged(status);
And then you should have a dedicated slot in SafeOTPWindow doing something like that:
void SafeOTPWindow::UpdateTableAfterLogin(bool isLoggedIn)
{
ui->tblLog->setColumnHidden(1,isLoggedIn);
}
and you connect as usual:
connect(&login, SIGNAL(loginStatusChanged()), &w, SLOT(UpdateTableAfterLogin(bool)) );