How to set maximum length for QInputDialog Text - c++

is it possible to restrict the length in a QInputDialog::getText? For example, I want to restrict the length from the user input to 10 characters directly in the InputDialog. Unfortunately, there isn't a function like QInputDialog::setMaximum.
Here's my current code:
QString input = QInputDialog::getText(this, tr("Find"), tr("Enter text:"), QLineEdit::Normal, "", nullptr, Qt::WindowFlags(), Qt::ImhDialableCharactersOnly);
if (input == "")
return;
else if (input.length() > 10)
{
QMessageBox::warning(this, tr("Invalid input", "Note #1"), tr("Input is too long."));
// This is this function name (calls itself again)
on_actionFind_triggered();
}
...

Very easy with a signal/slot mechanism and a signal blocker...
#include <QApplication>
#include <QInputDialog>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QInputDialog w;
QObject::connect(&w, &QInputDialog::textValueChanged,
[&w](QString text){ if (text.length() > 10) { QSignalBlocker s(w); w.setTextValue(text.left(10)); } });
w.show();
return a.exec();
}
Another posibility would be to find QLineEdit child of the dialog using and then assign a certain QValidator to it. I have not tested this but it should work as well. But then you would need to program the maximum length validator.
auto lineEdit = inputDialog->findChild<QLineEdit*>();
lineEdit->setValidator(validator);

Related

Parent function terminates whenever I try to call QTextCharFormat on QTextCursor selection

I recently ran into a weird issue where my QPlainTextEdit::selectionChanged handler function terminates prematurely whenever QTextCursor::mergeCharFormat/setCharFormat/setBlockCharFormat is called. Additionally, after terminating it gets called again and runs into the same issue, leading to an infinite loop.
I'm trying to replicate a feature present in many text editors (such as Notepad++) where upon selecting a word, all similar words in the entire document are highlighted. My TextEditor class is overloaded from QPlainTextEdit.
The minimal reproducible example is as follows:
main.cpp:
#include "mainWindow.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
mainWindow w;
w.show();
return a.exec();
}
MainWindow.h:
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_mainWindow.h"
#include "TextEditor.h"
class mainWindow : public QMainWindow
{
Q_OBJECT
public:
mainWindow(QWidget *parent = Q_NULLPTR) : QMainWindow(parent)
{
ui.setupUi(this);
auto textEdit = new TextEditor(this);
textEdit->setPlainText("test lorem ipsum test\n dolor sit test");
ui.tabWidget->addTab(textEdit, "Editor"); //Or any other way of adding the widget to the window
}
private:
Ui::mainWindowClass ui;
};
TextEditor.h:
The regex highlighter part is based on this SO answer.
#pragma once
#include <QPlainTextEdit>
class TextEditor : public QPlainTextEdit
{
Q_OBJECT
public:
TextEditor(QWidget* parent) : QPlainTextEdit(parent)
{
connect(this, &QPlainTextEdit::selectionChanged, this, &TextEditor::selectChangeHandler);
}
private:
void selectChangeHandler()
{
//Ignore empty selections
if (textCursor().selectionStart() >= textCursor().selectionEnd())
return;
//We only care about fully selected words (nonalphanumerical characters on either side of selection)
auto plaintext = toPlainText();
auto prevChar = plaintext.mid(textCursor().selectionStart() - 1, 1).toStdString()[0];
auto nextChar = plaintext.mid(textCursor().selectionEnd(), 1).toStdString()[0];
if (isalnum(prevChar) || isalnum(nextChar))
return;
auto qselection = textCursor().selectedText();
auto selection = qselection.toStdString();
//We also only care about selections that do not themselves contain nonalphanumerical characters
if (std::find_if(selection.begin(), selection.end(), [](char c) { return !isalnum(c); }) != selection.end())
return;
//Prepare text format
QTextCharFormat format;
format.setBackground(Qt::green);
//Find all words in our document that match the selected word and apply the background format to them
size_t pos = 0;
auto reg = QRegExp(qselection);
auto cur = textCursor();
auto index = reg.indexIn(plaintext, pos);
while (index >= 0)
{
//Select matched text and apply format
cur.setPosition(index);
cur.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor, 1);
cur.mergeCharFormat(format); //This causes the selectChangeHandler function to terminate and then execute again, causing an infinite loop leading to a stack overflow
//Move to next match
pos = index + (size_t)reg.matchedLength();
index = reg.indexIn(plaintext, pos);
}
}
};
I suspect the format fails to apply for some reason, possibly causing an exception that gets caught inside Qt and terminates the parent function. I tried adding my own try-catch handler around the problematic area, but it did nothing (as expected).
I'm not sure whether this is my fault or a bug inside Qt. Does anybody know what I'm doing wrong or how to work around this issue?
An infinite loop is being generated because it seems that getting the text changes also changes the selection. One possible solution is to block the signals using QSignalBlocker:
void selectChangeHandler()
{
const QSignalBlocker blocker(this); // <--- this line
//Ignore empty selections
if (textCursor().selectionStart() >= textCursor().selectionEnd())
return;
// ...

How to use Qt::SystemLocaleShortDate in QDateTimeEdit?

In QDateTimeEdit it is possible to set format by string with the setDisplayFormat(const QString &format), but i see no function which receive Qt::DateFormat enumeration instead of string.
My goal is to have format of QDateEdit depending on user locale.
Maybe it is possible to fetch string format which is used for Qt::SystemLocaleShortDate in fromString and toString but i can't find it.
You can use this code to set display format.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDateTimeEdit w;
QLocale currentLocale = QLocale::system();
w.setDisplayFormat( currentLocale.dateFormat( QLocale::ShortFormat ) + " " + currentLocale.timeFormat( QLocale::ShortFormat ) );
w.setDateTime( QDateTime::currentDateTime() );
w.show();
a.exec();
}
It look like this in Mac

An issue when creating a pop-up menu w.r.t to a parametr via loop

I am trying to create pop-up menu depending on a variable as follows:
QMenu menu(widget);
for(int i = 1; i <= kmean.getK(); i++)
{
stringstream ss;
ss << i;
string str = ss.str();
string i_str = "Merge with " + str;
QString i_Qstr = QString::fromStdString(i_str);
menu.addAction(i_Qstr, this, SLOT(mergeWith1()));
}
menu.exec(position);
where:
kmean.get(K) returns an int value,
mergeWith1() is some `SLOT()` which works fine
Issue:
The loop creates an action on menu only for i=1 case, and ignores other values of i.
Additional information
When doing the same loop with casual int values (without convert) everything works fine. e.g. if I do in loop only menu.addAction(i, this, SLOT(...))) and my K=4, a menu will be created with four actions in it, named 1, 2, 3, 4 correspondingly.
What can be the problem caused by
I think the issue is in convert part, when I convert i to string using stringstream and after to QString. May be the value is somehow lost. I am not sure.
QESTION:
How to make the loop accept the convert part?
What do I do wrong in convert part?
In Qt code, you shouldn't be using std::stringstream or std::string. It's pointless.
You have a crashing bug by having the menu on the stack and giving it a parent. It'll be double-destructed.
Don't use the synchronous blocking methods like exec(). Show the menu asynchronously using popup().
In order to react to the actions, connect a slot to the menu's triggered(QAction*) signal. That way you can deal with arbitrary number of automatically generated actions.
You can use the Qt property system to mark actions with custom attributes. QAction is a QObject after all, with all the benefits. For example, you can store your index in an "index" property. It's a dynamic property, created on the fly.
Here's a complete example of how to do it.
main.cpp
#include <QApplication>
#include <QAction>
#include <QMenu>
#include <QDebug>
#include <QPushButton>
struct KMean {
int getK() const { return 3; }
};
class Widget : public QPushButton
{
Q_OBJECT
KMean kmean;
Q_SLOT void triggered(QAction* an) {
const QVariant index(an->property("index"));
if (!index.isValid()) return;
const int i = index.toInt();
setText(QString("Clicked %1").arg(i));
}
Q_SLOT void on_clicked() {
QMenu * menu = new QMenu();
int last = kmean.getK();
for(int i = 1; i <= last; i++)
{
QAction * action = new QAction(QString("Merge with %1").arg(i), menu);
action->setProperty("index", i);
menu->addAction(action);
}
connect(menu, SIGNAL(triggered(QAction*)), SLOT(triggered(QAction*)));
menu->popup(mapToGlobal(rect().bottomRight()));
}
public:
Widget(QWidget *parent = 0) : QPushButton("Show Menu ...", parent) {
connect(this, SIGNAL(clicked()), SLOT(on_clicked()));
}
};
int main (int argc, char **argv)
{
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}
#include "main.moc"

QTreeView / QFileSystemModel set header labels

Pretty simple task but I didn't manage to find anything useful in documentation. I want a QTreeView to contain a single column called "Files" with data from QFileSystemView. Here's what I've got:
QFileSystemModel *projectFiles = new QFileSystemModel();
projectFiles->setRootPath(QDir::currentPath());
ui->filesTree->setModel(projectFiles);
ui->filesTree->setRootIndex(projectFiles->index(QDir::currentPath()));
// hide all but first column
for (int i = 3; i > 0; --i)
{
ui->filesTree->hideColumn(i);
}
That gives me a single column with "Name" header. How do I rename this header?
QAbstractItemModel::setHeaderData() should work. If not, you can always inherit from QFileSystemModel and override headerData().
Quick but a little dirty trick (please note w.hideColumn()):
#include <QApplication>
#include <QFileSystemModel>
#include <QTreeView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView w;
QFileSystemModel m;
m.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
m.setRootPath("C:\\");
w.setModel(&m);
w.setRootIndex(m.index(m.rootPath()));
w.hideColumn(3);
w.hideColumn(2);
w.hideColumn(1);
w.show();
return a.exec();
}
You can subclass QFileSystemModel and overide method headerData(). For example, if you want only to change first header label and leave the rest with their original values, you can do:
QVariant MyFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const {
if ((section == 0) && (role == Qt::DisplayRole)) {
return "Folder";
} else {
return QFileSystemModel::headerData(section,orientation,role);
}
}

Yes/No message box using QMessageBox

How do I show a message box with Yes/No buttons in Qt, and how do I check which of them was pressed?
I.e. a message box that looks like this:
You would use QMessageBox::question for that.
Example in a hypothetical widget's slot:
#include <QApplication>
#include <QMessageBox>
#include <QDebug>
// ...
void MyWidget::someSlot() {
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Test", "Quit?",
QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes) {
qDebug() << "Yes was clicked";
QApplication::quit();
} else {
qDebug() << "Yes was *not* clicked";
}
}
Should work on Qt 4 and 5, requires QT += widgets on Qt 5, and CONFIG += console on Win32 to see qDebug() output.
See the StandardButton enum to get a list of buttons you can use; the function returns the button that was clicked. You can set a default button with an extra argument (Qt "chooses a suitable default automatically" if you don't or specify QMessageBox::NoButton).
You can use the QMessage object to create a Message Box then add buttons :
QMessageBox msgBox;
msgBox.setWindowTitle("title");
msgBox.setText("Question");
msgBox.setStandardButtons(QMessageBox::Yes);
msgBox.addButton(QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
if(msgBox.exec() == QMessageBox::Yes){
// do something
}else {
// do something else
}
QT can be as simple as that of Windows. The equivalent code is
if (QMessageBox::Yes == QMessageBox(QMessageBox::Information, "title", "Question", QMessageBox::Yes|QMessageBox::No).exec())
{
}
I'm missing the translation call tr in the answers.
One of the simplest solutions, which allows for later internationalization:
if (QMessageBox::Yes == QMessageBox::question(this,
tr("title"),
tr("Message/Question")))
{
// do stuff
}
It is generally a good Qt habit to put code-level Strings within a tr("Your String") call.
(QMessagebox as above works within any QWidget method)
EDIT:
you can use QMesssageBox outside a QWidget context, see #TobySpeight's answer.
If you're even outside a QObject context, replace tr with qApp->translate("context", "String") - you'll need to #include <QApplication>
QMessageBox includes static methods to quickly ask such questions:
#include <QApplication>
#include <QMessageBox>
int main(int argc, char **argv)
{
QApplication app{argc, argv};
while (QMessageBox::question(nullptr,
qApp->translate("my_app", "Test"),
qApp->translate("my_app", "Are you sure you want to quit?"),
QMessageBox::Yes|QMessageBox::No)
!= QMessageBox::Yes)
// ask again
;
}
If your needs are more complex than provided for by the static methods, you should construct a new QMessageBox object, and call its exec() method to show it in its own event loop and obtain the pressed button identifier. For example, we might want to make "No" be the default answer:
#include <QApplication>
#include <QMessageBox>
int main(int argc, char **argv)
{
QApplication app{argc, argv};
auto question = new QMessageBox(QMessageBox::Question,
qApp->translate("my_app", "Test"),
qApp->translate("my_app", "Are you sure you want to quit?"),
QMessageBox::Yes|QMessageBox::No,
nullptr);
question->setDefaultButton(QMessageBox::No);
while (question->exec() != QMessageBox::Yes)
// ask again
;
}
If you need asynchronous call you should use open and result methods instead of question or exec. Sample code inside a QWidget method:
QMessageBox* const message = new QMessageBox(QMessageBox::Icon::Question, tr("Test"),
tr("Quit?"), QMessageBox::Button::Yes | QMessageBox::Button::No, this);
message->setDefaultButton(QMessageBox::Button::No);
message->open();
connect(message, &QDialog::finished, this, [message] {
message->deleteLater();
if (message->result() == QMessageBox::Button::Yes) {
QApplication::quit();
}
});
It should not be usefull just for a quit dialog but for other confirmation dialogs where parent widget might be destroyed by external events it is the main way to avoid a crash.
Python equivalent code for a QMessageBox which consist of a question in it and Yes and No button. When Yes Button is clicked it will pop up another message box saying yes is clicked and same for No button also. You can push your own code after if block.
button_reply = QMessageBox.question(self,"Test", "Are you sure want to quit??", QMessageBox.Yes,QMessageBox.No,)
if button_reply == QMessageBox.Yes:
QMessageBox.information(self, "Test", "Yes Button Was Clicked")
else :
QMessageBox.information(self, "Test", "No Button Was Clicked")
If you want to make it in python you need check this code in your workbench.
also write like this.
we created a popup box with python.
msgBox = QMessageBox()
msgBox.setText("The document has been modified.")
msgBox.setInformativeText("Do you want to save your changes?")
msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
msgBox.setDefaultButton(QMessageBox.Save)
ret = msgBox.exec_()