Getting multiple inputs from QInputDialog in Qt - c++

I would like to get a set of four values from four input labels in Qt. I would like to use QInputDialog but it contains only one inputbox as a default one. So, how can I add four labels and four line-edits and get the value from it?

You don't. The documentation is pretty clear:
The QInputDialog class provides a simple convenience dialog to get a
single value from the user.
If you want multiple values, create a QDialog derived class from scratch with 4 input fields.
For example:
QDialog dialog(this);
// Use a layout allowing to have a label next to each field
QFormLayout form(&dialog);
// Add some text above the fields
form.addRow(new QLabel("The question ?"));
// Add the lineEdits with their respective labels
QList<QLineEdit *> fields;
for(int i = 0; i < 4; ++i) {
QLineEdit *lineEdit = new QLineEdit(&dialog);
QString label = QString("Value %1").arg(i + 1);
form.addRow(label, lineEdit);
fields << lineEdit;
}
// Add some standard buttons (Cancel/Ok) at the bottom of the dialog
QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal, &dialog);
form.addRow(&buttonBox);
QObject::connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
QObject::connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
// Show the dialog as modal
if (dialog.exec() == QDialog::Accepted) {
// If the user didn't dismiss the dialog, do something with the fields
foreach(QLineEdit * lineEdit, fields) {
qDebug() << lineEdit->text();
}
}

Following alexisdm's answer, here is one way to implement custom QInputDialog.
"inputdialog.h":
#ifndef INPUTDIALOG_H
#define INPUTDIALOG_H
#include <QDialog>
class QLineEdit;
class QLabel;
class InputDialog : public QDialog
{
Q_OBJECT
public:
explicit InputDialog(QWidget *parent = nullptr);
static QStringList getStrings(QWidget *parent, bool *ok = nullptr);
private:
QList<QLineEdit*> fields;
};
#endif // INPUTDIALOG_H
"inputdialog.cpp":
#include "inputdialog.h"
#include <QLabel>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QFormLayout>
InputDialog::InputDialog(QWidget *parent) : QDialog(parent)
{
QFormLayout *lytMain = new QFormLayout(this);
for (int i = 0; i < 4; ++i)
{
QLabel *tLabel = new QLabel(QString("Text_%1:").arg(i), this);
QLineEdit *tLine = new QLineEdit(this);
lytMain->addRow(tLabel, tLine);
fields << tLine;
}
QDialogButtonBox *buttonBox = new QDialogButtonBox
( QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal, this );
lytMain->addWidget(buttonBox);
bool conn = connect(buttonBox, &QDialogButtonBox::accepted,
this, &InputDialog::accept);
Q_ASSERT(conn);
conn = connect(buttonBox, &QDialogButtonBox::rejected,
this, &InputDialog::reject);
Q_ASSERT(conn);
setLayout(lytMain);
}
QStringList InputDialog::getStrings(QWidget *parent, bool *ok)
{
InputDialog *dialog = new InputDialog(parent);
QStringList list;
const int ret = dialog->exec();
if (ok)
*ok = !!ret;
if (ret) {
foreach (auto field, dialog->fields) {
list << field->text();
}
}
dialog->deleteLater();
return list;
}
Now you can use getStrings() method similar to QInputDialog::getText():
QStringList list = InputDialog::getStrings(this);
if (!list.isEmpty()) {
// use list
}
Or
bool ok;
QStringList list = InputDialog::getStrings(this, &ok);
if (ok) {
// use list
}

Related

How to disable the default copy behavior in QTreeView?

I have a QTreeView with a QStandardItemModel and I would like to be able to prevent the user from copying the text of the items.
#include <QMainWindow>
#include <QStandardItemModel>
#include <QTreeView>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr) :
QMainWindow(parent)
{
auto *treeView = new QTreeView(this);
auto *model = new QStandardItemModel(this);
for (int n = 0; n < 5; n++)
model->appendRow(createItem(QString::number(n)));
treeView->setModel(model);
treeView->setContextMenuPolicy(Qt::NoContextMenu);
setCentralWidget(treeView);
}
private:
QStandardItem *createItem(const QString &name)
{
auto *item = new QStandardItem(name);
item->setFlags(Qt::ItemIsEnabled);
return item;
}
};
I have already made the items not editable and disabled the context menu. However, it is still possible for the user to click on an item and copy the text by pressing Ctrl+C. I can use Qt::NoItemFlags, but I want the items to be enabled.
How to accomplish that?
To disable the default copy behavior of QTreeView reimplement QTreeView::keyPressEvent in a subclass, e.g. TreeView, like that:
void TreeView::keyPressEvent(QKeyEvent *event)
{
if (!(event == QKeySequence::Copy))
QTreeView::keyPressEvent(event);
}
Then in your code instead of QTreeView:
auto *treeView = new QTreeView(this);
instantiate TreeView:
auto *treeView = new TreeView(this);
Alternatively, you can use installEventFilter to trap the keystroke events with having to subclass.

Qt signals and slots passing data

I'm pretty new to c++ and qt. I'm not sure if i use the right terminology describe what I want to achieve. But here it goes.
My application spawns and removes widgets in a gridlayout when the user pushes buttons. Managed to do this successfully. However when the user uses the spawned widgets I want the widgets to interact with each other.
QList<QLineEdit*> m_ptrLEPathList;
QList<QPushButton*> m_ptrPBList;
qint8 m_noFields;
void MainWindow::on_pbIncFields_clicked()
{
//create widgets and place on a new row in a gridLayout
QLineEdit *lineEditPath = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
//storing pointers in lists to be able to delete them later.
m_ptrLEPathList.append(lineEditPath);
m_ptrPBList.append(pushButton);
ui->gridLayout->addWidget(m_ptrLEPathList.last(),m_noFields,0);
ui->gridLayout->addWidget(m_ptrPBList.last(),m_noFields,1);
connect(m_ptrPBList.last(), SIGNAL(clicked(bool), this, SLOT(on_addPath()));
m_noFields++;
}
void MainWindow::on_pbDecFields()
{
//delete last spawned widgets
}
void MainWindow::on_addPath()
{
QFileDialog getPath();
getPath.exec();
//somehow set the text of the line edit spawned on the same row as the pushbutton
}
So my slot is executed when I push any spawned button but I have no idea how to store the data from the file dialog in the related lineEdit.
Is the basic idea of what I'm trying to do ok or is there any other solution to achieve the fuctionality I'm looking for?
In on_addPath slot you can use QObject::sender method to get the clicked button, and, assuming m_ptrLEPathList and m_ptrPBList lists are equal, you can easily get the corresponding QLineEdit:
void MainWindow::on_addPath()
{
QFileDialog dialog;
if (!dialog.exec())
{
return;
}
QStringList fileNames = dialog.selectedFiles();
if (fileNames.isEmpty())
{
return;
}
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn)
{
return;
}
Q_ASSERT(m_ptrPBList.size() == m_ptrLEPathList.size());
int index = m_ptrPBList.indexOf(btn);
if (index == -1)
{
return;
}
QLineEdit *edit = m_ptrLEPathList.at(index);
edit->setText(fileNames.first());
}
You are including 'on_addPath' function out of the scope of the 'MainWindow' class, so when the slot is called you have not access to member elements in the class.
Try to include the slot function into the class and check if you have direct access to the member elements. Also, the 'lineEditPath' element must be a member object, so it must be included into the class definition.
Something like this:
void MainWindow::on_addPath()
{
QFileDialog getPath();
getPath.exec();
QStringList fileNames = dialog.selectedFiles();
if (fileNames.isEmpty())
{
return;
}
m_lineEditPath->setText(fileNames.first());
}
First off, void on_addPath() must be void MainWindow::on_addPath()
As for linking the data from QFileDialog it is simple. Try this:
void MainWindow::on_addPath() {
/* Get the push button you clicked */
QPushButon *btn = qobject_cast<QPushButton*>( sender() );
/* Make sure both the lists have the same size */
Q_ASSERT(m_ptrPBList.size() == m_ptrLEPathList.size());
/* If the sender is a button in your list */
if ( m_ptrPBList.contains( btn ) ) {
/* Get the index of your push button */
int idx = m_ptrPBList.indexOf( btn );
/* Get the corresponding line edit */
QLineEdit *le = m_ptrLEPathList.at( idx );
/* Get your path */
QString path = QFileDialog::getOpenFileName( this, "Caption", "Default Location" );
/* If you path is not empty, store it */
if ( not path.isEmpty() )
le->setText( path );
}
}
Add a map to private section
QMap<QPushButton*, QLineEdit*> map;
Then
QLineEdit *lineEditPath = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
map.insert(pushButton, lineEditPath);
You can use sender() method like follow:
void on_addPath()
{
QFileDialog getPath();
getPath.exec();
QObject* obj = sender();
QPushButton *pb = 0;
if((pb = qobject_cast<QPushButton *>(obj)) != 0) {
QLineEdit* lineEdit = map->value(pb, 0);
if( lineEdit != 0 )
lineEdit->setText( getPath.<some function to get selected file name> );
}
}
I think the cleanest solution would be to contain the QLineEdit and QPushButton in a custom widget class, if it suits your project. That way you could use the file dialog inside this class, and you won't have to store the widgets in lists. It is hard to give you all the information, as you didn't really provide any details what your application is supposed to do. But in any case, the custom widget class would look something like this (you should define all the functions inside a .cpp file):
#ifndef WIDGETCONTAINER_H
#define WIDGETCONTAINER_H
#include <QWidget>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QFileDialog>
class WidgetContainer : public QWidget
{
Q_OBJECT
public:
WidgetContainer(QWidget *parent = 0) : QWidget(parent)
{
setLayout(new QHBoxLayout);
button.setText("BUTTON");
layout()->addWidget(&lineEdit);
layout()->addWidget(&button);
connect(&button, SIGNAL(clicked()), this, SLOT(buttonPressed()));
}
private:
QLineEdit lineEdit;
QPushButton button;
private slots:
void buttonPressed()
{
QString filename = QFileDialog::getSaveFileName();
lineEdit.setText(filename);
}
};
#endif // WIDGETCONTAINER_H

How to get a Widget as a QListWidget with 2 columns (with some constraints)?

Looking for : To display log messages, with date-time (grey color) and message (red for errors, orange for warnings ... -> number of colors not etablished and have to be flexible). Sometimes we will need to open a link from the message, the act to open not etablished (context menu, simple click -> nevermind)
History : 1- I tried QTextBrowser with html text style -> after 500 lines, it begin to slow the app, after a lot, it crashes the app
2- I Tried QListWidget with only the message with color (not the date-time), works very well !
So now you understand that I need a second column, the date-time in grey color.
My Question is: what is the easiest, more efficient way to do it, by keeping the QListWidget style, that I like.
I heard about QTreeWidget and QTableWidget to do that, but what is the best and what properties whould I change for those widgets? (to sum up : 2 columns with 2 different text colors, the ability to open links, the QListWidget style, and scrolling bar always at bottom)
Windows, C++, Qt 5
MainWindow.h
#include <QMainWindow>
#include <QTreeWidget>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void contextMenuRequest(const QPoint& pos);
void openLink();
private:
QTreeWidget* treeWidget;
};
MainWindow.cpp
#include "mainwindow.h"
#include <QTreeWidgetItem>
#include <QStringList>
#include <QBrush>
#include <QAction>
#include <QMenu>
#include <QTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
treeWidget = new QTreeWidget(this);
treeWidget->setGeometry(0, 0, 500, 500);
treeWidget->setColumnCount(2);
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));
QStringList headers;
headers << "Date" << "Message";
treeWidget->setHeaderLabels(headers);
QTreeWidgetItem* errorItem = new QTreeWidgetItem();
errorItem->setForeground(1, QBrush(QColor(255, 0, 0)));
errorItem->setText(0, QTime::currentTime().toString());
errorItem->setText(1, "error message");
QTreeWidgetItem* warningItem = new QTreeWidgetItem();
warningItem->setForeground(1, QBrush(QColor(255, 165, 0)));
warningItem->setText(0, QTime::currentTime().toString());
warningItem->setText(1, "warning message");
treeWidget->addTopLevelItem(errorItem);
treeWidget->addTopLevelItem(warningItem);
for (int i = 0; i < 500; ++i)
{
QTreeWidgetItem* item;
if (i % 2)
{
item = errorItem->clone();
treeWidget->addTopLevelItem(item);
}
else
{
item = warningItem->clone();
treeWidget->addTopLevelItem(item);
}
treeWidget->scrollToItem(item); //call after adding new item
}
resize(500, 500);
}
MainWindow::~MainWindow()
{
}
void MainWindow::contextMenuRequest(const QPoint& pos)
{
QAction* openAction = new QAction("Open link", this);
connect(openAction, SIGNAL(triggered()), this, SLOT(openLink()));
QMenu menu;
menu.addAction(openAction);
menu.exec(mapToGlobal(pos));
}
void MainWindow::openLink()
{
}

Qtoolbar toggle show hide on Qmenu

How to add slot to toggle show and hide the toolbar in qmenu? This my code:
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setMinimumSize(800, 600);
CreateAct();
CreateMenus();
createToolBars();
}
void MainWindow::CreateAct()
{
undoAct = new QAction(QIcon::fromTheme("edit-undo", QIcon(":/images/undo.png")), tr("&Undo"), this);
redoAct = new QAction(QIcon::fromTheme("edit-redo", QIcon(":/images/redo.png")), tr("&Redo"), this);
cutAct = new QAction(QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png")), tr("Cu&t"), this);
copyAct = new QAction(QIcon::fromTheme("edit-copy", QIcon(":/images/copy.png")), tr("&Copy"), this);
pasteAct = new QAction(QIcon::fromTheme("edit-paste", QIcon(":/images/paste.png")), tr("&Paste"), this);
editToolBarAct = new QAction(tr("Show edit toolbar"), this);
editToolBarAct->setCheckable(true);
editToolBarAct->setChecked(true);
// connect(editToolBarAct, SIGNAL(toggled(bool)), editToolBar, SLOT());
fileToolBarAct = new QAction(tr("Show file toolbar"), this);
fileToolBarAct->setCheckable(true);
fileToolBarAct->setChecked(true);
// connect(fileToolBarAct, SIGNAL(toggled(bool)), fileToolBar, SLOT());
}
void MainWindow::CreateMenus()
{
windowMenu = menuBar()->addMenu(tr("&Window"));
windowMenu->addAction(fileToolBarAct);
windowMenu->addAction(editToolBarAct);
}
void MainWindow::createToolBars()
{
fileToolBar = addToolBar("file");
fileToolBar->addAction(undoAct);
fileToolBar->addAction(redoAct);
fileToolBar->toggleViewAction();
fileToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::LeftToolBarArea);
editToolBar = addToolBar(tr("Edit"));
editToolBar->addAction(cutAct);
editToolBar->addAction(copyAct);
editToolBar->addAction(pasteAct);
editToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::LeftToolBarArea);
}
MainWindow::~MainWindow() {}
I am know using toggleViewAction, but how to use that code (I am really new in qt programming), can you give me sample code? I've tried googling, but did not find examples of its use.
The following small example demonstrates how to use the QToolBar::toggleViewAction():
class MainWindow : public QMainWindow
{
public:
MainWindow()
{
// Create a tool bar
QToolBar *tb = addToolBar("My Tool Bar");
[..]
// Create a menu and add toggle action for the tool bar.
QAction *tba = tb->toggleViewAction();
QMenu *m = menuBar()->addMenu("&Window");
m->addAction(tba);
}
};

QT - QInputDialog How to Validate?

I would like to add some type of validation to my QInputDialog. I use the input of the dialog to create a file system path. So I would like to exclude characters such as #$#%^&*() but keep - and _. I was thinking of applying a regexp pattern but I'm not sure of the workflow.
If its not possible or it makes sense to use something different I'm open to that as well.
This is what I'm currently using:
QString defaultText("whatever");
bool ok;
QString caseInput = QInputDialog::getText(this, tr("Input Text"), tr("New Text:"), QLineEdit::Normal, defaultText, &ok);
if (ok && !caseInput.isEmpty())
{
// do stuff
}
So if you want full control of it, you will want to make your own QDialog, add in a QLabel for the text, and add in a line edit, setup a QValidator, and access the return value afterwards.
Like so:
mydialog.h
#include <QDialog>
#include <QLineEdit>
class MyDialog : public QDialog
{
Q_OBJECT
public:
MyDialog(QWidget *parent = 0);
~MyDialog();
QString getNewValue();
signals:
//void rejected();
//void accepted();
public slots:
private:
QLineEdit * le;
};
mydialog.cpp
#include "mydialog.h"
#include <QDialogButtonBox>
#include <QRegExpValidator>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QLabel>
MyDialog::MyDialog(QWidget *parent)
: QDialog(parent)
{
le = 0;
this->setAttribute(Qt::WA_QuitOnClose, false);
QVBoxLayout * vbox = new QVBoxLayout;
vbox->addWidget(new QLabel(tr("Type in your text:")));
le = new QLineEdit();
// le->setText(tr("Profile"));
// le->selectAll();
le->setPlaceholderText(tr("Profile"));
vbox->addWidget(le);
QRegExpValidator * v = new QRegExpValidator(QRegExp("[\\w\\d_ \\.]{24}"));
le->setValidator(v);
QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel);
vbox->addWidget(buttonBox);
this->setLayout(vbox);
// connect(buttonBox, SIGNAL(accepted()), this, SIGNAL(accepted()));
// connect(buttonBox, SIGNAL(rejected()), this, SIGNAL(rejected()));
}
MyDialog::~MyDialog()
{
}
QString MyDialog::getNewValue()
{
return le->text();
}
Example usage:
MyDialog dialog;
if(dialog.exec() == QDialog::Accepted)
{
QString retVal = dialog.getNewValue();
qDebug() << "Dialog value:" << retVal;
}
Another way to achieve almost the same thing:
http://qt-project.org/doc/qt-4.8/qlineedit.html#inputMask-prop
http://qt-project.org/doc/qt-4.8/widgets-lineedits.html
If you want to use the stock getText QInputDialog you can set the field for InputMethodHint:
http://qt-project.org/doc/qt-4.8/qinputdialog.html#getText
http://qt-project.org/doc/qt-4.8/qt.html#InputMethodHint-enum
But the QRegExp is the most powerful in my opinion.
Here are some good examples of QRegExp in this class:
http://qt-project.org/doc/qt-4.8/richtext-syntaxhighlighter-highlighter-cpp.html
classFormat.setFontWeight(QFont::Bold);
classFormat.setForeground(Qt::darkMagenta);
rule.pattern = QRegExp("\\bQ[A-Za-z]+\\b");
rule.format = classFormat;
highlightingRules.append(rule);
singleLineCommentFormat.setForeground(Qt::red);
rule.pattern = QRegExp("//[^\n]*");
rule.format = singleLineCommentFormat;
highlightingRules.append(rule);
multiLineCommentFormat.setForeground(Qt::red);
quotationFormat.setForeground(Qt::darkGreen);
rule.pattern = QRegExp("\".*\"");
rule.format = quotationFormat;
highlightingRules.append(rule);
functionFormat.setFontItalic(true);
functionFormat.setForeground(Qt::blue);
rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()");
rule.format = functionFormat;
highlightingRules.append(rule);
commentStartExpression = QRegExp("/\\*");
commentEndExpression = QRegExp("\\*/");
Hope that helps.