How to use Qt::SystemLocaleShortDate in QDateTimeEdit? - c++

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

Related

How to set maximum length for QInputDialog Text

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);

QTreeView doesn't work outside the main function

I'm trying to generate a simple QTreeView inside another widget (QMainWindow). The following code works as expected and displays the tree-view,
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow w;
w.show();
QString rootPath = "C:/";
QFileSystemModel model;
model.setRootPath("");
QTreeView tree;
tree.setModel(&model);
if (!rootPath.isEmpty()) {
const QModelIndex rootIndex = model.index(QDir::cleanPath(rootPath));
if (rootIndex.isValid())
tree.setRootIndex(rootIndex);
}
tree.setParent(&w);
tree.show();
return app.exec();
}
but if I extract the code that generates the tree-view, nothing seems to happen. The extracted function is as follows:
void create_tree(QMainWindow *w) {
QString rootPath = "C:/";
QFileSystemModel model;
model.setRootPath("");
QTreeView tree;
tree.setModel(&model);
if (!rootPath.isEmpty()) {
const QModelIndex rootIndex = model.index(QDir::cleanPath(rootPath));
if (rootIndex.isValid())
tree.setRootIndex(rootIndex);
}
tree.setParent(w);
tree.show();
}
and the corresponding function call in main function is as follows:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow w;
w.show();
create_tree(&w);
return app.exec();
}
How does the extracted function create_tree work and why is it not showing the tree view?
QFileSystemModel model;
and
QTreeView tree;
Are local stack variables, meaning they will be gone once you exit the create_tree function.
You can solve your issue by creating them on the heap by using new, which will keep them alive. Be careful, that you need to think about how you destroy these created objects. The Qt parenting system is a great help there, because the parent will destroy its children when it is destroyed, so your tree view is fine. You should think about good parent for your model to make sure you create no memory leak.
A working version of your function looks like this - be careful that you still need to handle the models deletion:
void create_tree(QMainWindow *w) {
QString rootPath = "C:/";
QFileSystemModel* model = new QFileSystemModel();
model->setRootPath("");
QTreeView* tree = new QTreeView();
tree->setModel(model);
if (!rootPath.isEmpty()) {
const QModelIndex rootIndex = model->index(QDir::cleanPath(rootPath));
if (rootIndex.isValid())
tree->setRootIndex(rootIndex);
}
tree->setParent(w);
tree->show();
}

How to get the QLineEdit text offset when have QAction at leading position

I have a QLineEdit with a QAction at leading position. I want to know the start position of the text but I don't find how to do:
QLineEdit *le = new QLineEdit(parent);
le->addAction(QIcon(":/myicon"), QLineEdit::LeadingPosition);
// Now I want to get the text start position
// but both return "QMargins(0, 0, 0, 0) QMargins(0, 0, 0, 0)"
qDebug() << le->textMargins() << le->contentsMargins();
I searched in the qt's github sources to find if the addAction() method does something on the contents or text margins but without success.
I must admit that (before reading OPs question) I was not aware about QLineEdit::addAction(). Thus, I wrote a little sample testQLineEditAction.cc:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
QLineEdit qEdit;
qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
qEdit.show();
// runtime loop
return app.exec();
}
and this is how it looks (compiled in cygwin64):
Afterwards, I digged a bit through woboq.org to find out how it is implemented.
I started in QLineEdit::paintEvent():
void QLineEdit::paintEvent(QPaintEvent *)
{
...
QStyleOptionFrame panel;
initStyleOption(&panel);
...
QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
r.setX(r.x() + d->effectiveLeftTextMargin());
r.setY(r.y() + d->topTextMargin);
r.setRight(r.right() - d->effectiveRightTextMargin());
r.setBottom(r.bottom() - d->bottomTextMargin);
This is interesting: The rectangle for contents is retrieved and then corrected by inner offsets.
QFontMetrics fm = fontMetrics();
...
QRect lineRect(r.x() + d->horizontalMargin, d->vscroll, r.width() - 2*d->horizontalMargin, fm.height());
About the d->horizontalMargin, I'm not quite sure but I ignored it for now and followed instead d->effectiveLeftTextMargin():
int QLineEditPrivate::effectiveLeftTextMargin() const
{
return effectiveTextMargin(leftTextMargin, leftSideWidgetList(), sideWidgetParameters());
}
...
static int effectiveTextMargin(int defaultMargin, const QLineEditPrivate::SideWidgetEntryList &widgets,
const QLineEditPrivate::SideWidgetParameters &parameters)
{
if (widgets.empty())
return defaultMargin;
return defaultMargin + (parameters.margin + parameters.widgetWidth) *
int(std::count_if(widgets.begin(), widgets.end(),
[](const QLineEditPrivate::SideWidgetEntry &e) {
return e.widget->isVisibleTo(e.widget->parentWidget()); }));
}
So, I came to the conclusion that QLineEditPrivate::effectiveLeftTextMargin() considers the space for action icons when effective size of text rectangle is determined.
It's a pity that all these functions are private and thus not accessable from outside. After thinking a while how to get access to these from outside and looking into doc. whether I haven't overseen something, I got the idea to use the QActions directly for this:
#include <QtWidgets>
void inspect(const QString &cmd, QAction &qCmd)
{
qDebug() << (cmd + "->associatedWidgets().size():")
<< qCmd.associatedWidgets().size();
int i = 0;
for (QWidget *const pQWidget : qCmd.associatedWidgets()) {
qDebug() << '[' << i++ << "]:"
<< typeid(*pQWidget).name()
<< "geometry:" << pQWidget->geometry();
}
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
QLineEdit qEdit;
qEdit.setText("012345678901234567890123456789");
QAction *const pQCmd1
= qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
QAction *const pQCmd2
= qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
qEdit.show();
qDebug() << "qEdit.geometry():" << qEdit.geometry();
inspect("pQCmd1", *pQCmd1);
inspect("pQCmd2", *pQCmd2);
// runtime loop
return app.exec();
}
Console output:
Qt Version: 5.9.4
qEdit.geometry(): QRect(0,0 200x23)
"pQCmd1->associatedWidgets().size():" 2
[ 0 ]: 9QLineEdit geometry: QRect(0,0 200x23)
[ 1 ]: 19QLineEditIconButton geometry: QRect(4,2 22x18)
"pQCmd2->associatedWidgets().size():" 2
[ 0 ]: 9QLineEdit geometry: QRect(0,0 200x23)
[ 1 ]: 19QLineEditIconButton geometry: QRect(174,2 22x18)
To compare the values, another snapshot with modified icons (frame drawn in SVGs to show icon size) which has been magnified (factor 5):
Left QLineEditIconButton reported position (4, 2) but the left frame of icon is 8 pixels away from left border of QLineEdit. There is surely a frame around the QLineEditIconButton which has to be considered as well (and I didn't investigate how to retrieve it). The width of frame might be subject of style engine and thus vary between platforms. To make such attempt robust and portable, the respective values should be retrieved from the widgets or from style. This starts to become a tedious fiddling with more or less chance for success.
I ended up once in a similar situation when trying to answer SO: How to automatically increase/decrease text size in label in Qt.
Concerning QLineEdit::cursorRect():
I believe that using QLineEdit::cursorRect() is (as well) at best fragile.
I modified my above example to check this out:
#include <QtWidgets>
class LineEdit: public QLineEdit {
public:
QRect cursorRect() const { return QLineEdit::cursorRect(); }
};
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
LineEdit qEdit;
qEdit.setText("012345678901234567890123456789");
qEdit.addAction(QIcon("./document-properties.svg"), QLineEdit::LeadingPosition);
qEdit.addAction(QIcon("./document-save.svg"), QLineEdit::TrailingPosition);
qEdit.show();
qDebug() << "qEdit.cursorRect():" << qEdit.cursorRect();
// runtime loop
return app.exec();
}
Console output:
Qt Version: 5.9.4
qEdit.geometry(): QRect(0,0 200x23)
qEdit.cursorRect(): QRect(253,0 9x16)
Funny, that the cursor x-position is not only quite high – it's even higher than the width of qEdit. How comes? The initial text "012345678901234567890123456789" I put into qEdit causes the cursor to be close to the right whereby horizontal scrolling happens. The cursor position seems to be related to the virtual text width (including the clipped range on the left side).
After having to deal with this problem myself recently, I found a very simple solution:
QLineEdit.setTextMargins(24, 0, 0, 0)
Where the parameters refer to left, top, right, and bottom text margins.
I am using PyQt5, but the idea is the same for Qt as well.

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);
}
}

Displaying just the files with correct extension

I'm trying in QFileSystemModel display just files with extention *.txt and the other types shaded/grayed out:
proxy_ is of type QSortFilterProxyModel
model_ is of type QFileSystemModel
Here's my code:
proxy_->setFilterWildcard("*.txt");
proxy_->setSourceModel(model_);
model_->setNameFilters(QStringList(proxy_->filterRegExp().pattern()));
model_->setNameFilterDisables(true);
sel_model_ = (new QItemSelectionModel(proxy_));
treeView->setModel(proxy_);
treeView->setSelectionModel(sel_model_);
...but by doing so nothing is shown in my view. Anyone knows what I'm doing wrong?
You can set a file name filter with QFileSystemModel::setNameFilters.
In the example program below .txt and folders are displayed normally, and other files are disabled (greyed out).
The nameFilterDisables property allows you to choose between filtered out files being disabled or hidden.
#include <QtGui>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QFileSystemModel model;
model.setRootPath(QDir::rootPath());
QStringList filters;
filters << "*.txt";
model.setNameFilters(filters);
QTreeView view;
view.setModel(&model);
view.show();
return app.exec();
}