how to avoid leading spaces in qtreeview with qfilesystem model header text? - qt

I am using QFileSystem Model with QTreeView, and I am able to change the text by sub classing QFileSystem model like this...
QVariant customFileSystemModel::headerData(int section,
Qt::Orientation orientation,
int role) const
{
switch(role)
{
case(Qt::DisplayRole):
{
return QString("YourText");
}
case(Qt::TextAlignmentRole):
{
return Qt::AlignLeading;
}
default:{}
}
return QFileSystemModel::headerData(section, orientation,role);
}
but I am not able to remove the leading spaces in header text, could any one suggest how to do it.

I don't think it can be easily done. This is definitely not within the scope of your model. This is more a question of the selected style. Maybe something can be done with stylesheets. Or you can change the QHeaderView from your QTreeView. In worst case you have to subclass QHeaderView and override how the label is painted. But I don't think you have to go that far.

Related

Last empty column to fill QTableView

In a QTableView I need the last (rightmost) column to be empty, expandable but not movable. The goal is that the table not to suddenly end (for I use alternate color for rows) or to ugly expand to the right. In QHeaderView there is a setFirstSectionMovable(bool); I need something similar for the last section, letting the rest of them movable. (in other words: Fill the rest of the table with an empty not movable column). Any clue how to acheive this?
I did override mousePressEvent() in a subclass of QHeaderView to skip the last section but it still can be moved by moving other column in its place and I don't know how to prevent this.
AMOQ:
Be HeaderView a subclass of QHeaderView and have
setSectionsMovable(true);
setStretchLastSection(true);
Treat the sectionMoved signal:
connect(this, &QHeaderView::sectionMoved, [this](int, int, int newVisual) {
if(newVisual == count()-1) { moveSection(newVisual, newVisual-1); }
});
Override mousePressEvent:
void HeaderView::mousePressEvent(QMouseEvent* event) {
int logicalIdx = logicalIndexAt(event->pos());
if(logicalIdx != count()-1) { QHeaderView::mousePressEvent(event); }
}

Set different images for different checkboxes in a QTreeView

I subclassed a QTreeView and I have two columns where there are checkboxes. I would like to set two different images: one for the first column, and another one for the second column. I know I can change the image in the stylesheet with:
QTreeView::indicator:checked{
image: url(:/checked);
}
QTreeView::indicator:unchecked{
image: url(:/unchecked);
}
but it will change all the checkboxes in the tree view. Is there a way to do it with the stylesheets, or do I need to use a delegate?
Short answer: Stylesheets can't do that (as far as I know). They are a pretty immature feature in Qt, and there seems to be no development on them either.
What you can do:
Stylesheets
You cannot assign properties to a column or an item and you cannot access the columns by index.
But you can use some of the pseudo-states selectors like :first, :middle and :last:
QTreeView::indicator:first:checked{
background: red;
}
QTreeView::indicator:middle:checked{
background: blue;
}
QTreeView::indicator:unchecked{
background: lightgray;
}
I used colors instead of images for the sake of simplicity.
Note however, that these pseudo-states are actual currently visible states, so if the user is allowed to reorder columns, the style of the column might change. For example if the user drags one of the :middlecolumns and drops it at the end, the box will not be blue anymore.
DecorationRole
You can fake it using Qt::DecorationRole.
To do so, you have to receive the mousePressEvent either by subclassing QTreeView or by installing an event filter. You can then change the icon (via Qt::DecorationRole + emit dataChanged()) when the user clicks in the area of the icon.
This does not work with the keyboard of course.
Custom ItemDelegate
Subclass QStyledItemDelegate and override paint(), just as you suggested.
Custom Style
If you are creating a heavily styled application, you probably have to create a custom Style sooner or later. Stylesheets just don't support some features.
To do so, subclass QProxyStyle, override drawPrimitive and handle the drawing if QStyle::PE_IndicatorViewItemCheck is passed. You will also receive a QStyleOptionViewItem of which has some useful properties like checkState, features (contains QStyleOptionViewItem::HasCheckIndicator if there is a checkbox), and of course index so you can determine what kind of checkbox you want to draw.
Edit: Appendix
Example Using a Custom QStyledItemDelegate
void MyItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyledItemDelegate::paint(painter, option, index);
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = option.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
drawCheckBox(painter, checkRect, opt.checkState, index);
}
void MyItemDelegate::drawCheckBox(QPainter * painter, const QRect & checkRect, Qt::CheckState checkState, const QModelIndex & index) const
{
if (checkState == Qt::Checked)
{
switch (index.column())
{
case 0:
painter->fillRect(checkRect, Qt::red);
break;
default:
painter->fillRect(checkRect, Qt::blue);
}
}
else
{
painter->fillRect(checkRect, Qt::lightGray);
}
}
This example is quick and easy. Simply paint over the checkbox drawn by QStyledItemDelegate. Requires you to fill the whole box however, otherwise the original will be visible.
You can try to use QStyledItemDelegate to draw anything but the checkbox, and draw the checkbox afterwards, but that is a little harder and will leave you with some minor drawing artifacts if you don't want to spend too much time on it.
Example Using a Custom QProxyStyle
void MyStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * w) const
{
if (pe == QStyle::PE_IndicatorViewItemCheck)
{
const QStyleOptionViewItem * o = static_cast<const QStyleOptionViewItem *>(opt);
drawCheckBox(p, opt->rect, o->checkState, o->index);
return;
}
QProxyStyle::drawPrimitive(pe, opt, p, w);
}
The drawCheckBox() function is the same as in the first example.
As you can see, this way is much simpler, cleaner and has none of the drawbacks. You can apply the style globally, or only for a single widget.

How to set stylesheet for the current item in QTableView

When QTableView edit control is visible for the current item the shylesheet of the edit takes place. When there is no active edit control in the QTableView the current item is styled using the QTableView { selection-background-color: } How to set different style only for the current item?
Qt style sheets support sub-controls and pseudo states, you can use it to improve your customization. see Qt6 docs
In this case you can use the ::item sub-control and the :focus pseudo state (the "current" pseudo state doesn't exist, but the :focus does the same).
This is an example that you can use:
QTableView::item:focus
{
selection-background-color: yellow;
}
See also Qt6 documentation of customizing a qtableview
1. As it IGHOR said you can use data() method in your model and provide a color when role is Qt::BackgroundColor. But there is a stumble here because you don't know whether index is current or not. You'll ought to set a current index in the model when it changes and then make a check like this:
if (index == m_currentIndex and role==Qt::BackgroundRole) return Qt::black;
Actually it's not the best idea to tell the model about currentIndex according to Model/View pattern, because you can have two views for one model.
2. Descendants of QAbstractItemView has method setItemDelegate. A delegate is used to draw a cell.
All you need is to inherit from QStyledItemDelegate, pass a pointer to the view to the delegate and override method initStyleOption.
Then do something like this:
void MyStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
const QModelIndex &index) const
{
QStyledItemDelegate::initStyleOption(option, index);
QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option);
if (index == view()->currentIndex())
{
v4->backgroundBrush = QBrush(Qt::grey);
}
}
3. If you really need to use css (for example you have themes) you can do it this way:
Add something like this in your css file:
QTableView
{
qproperty-currentItemBackground: #cccccc;
}
Modify initStyleOption from the previous example to use the property:
v4->backgroundBrush = view()->property("currentItemBackground").toColor();
With this approach you can set a specific style via css for a column, a row, a single cell or a group of cells.
You need to create a new delegate, that renders itself based on the data model (custom role, for example). You need to base its style on a special control created for the purpose (that can be changed via stylesheet) . I'll post some code when I find time.
One can use variadic templates, and crtp (Coplien) to good effect to layer one's delegates

Can you set a QWidget to take up an entire column of a QTreeWidget?

I have a custom QTreeWidget subclass that I'm using to display track names/etc. in my MIDI editor project (https://github.com/waddlesplash/ragingmidi). I'd like to add another column to this tree widget, but with one widget taking up the whole column and not per-item widgets.
Is this possible or will I have to figure out some other solution?
EDIT: I'm trying to accomplish something like this: http://www.anvilstudio.com/compose.jpg - see the last "column" in the header view (3rd after "L/R Balance") showing all the lines/notes (which is entirely custom, and written in VB.NET and closed-source anyway).
EDIT 2: You can't see it, but the last column scrolls without the other columns scrolling in the above picture. In their method, you have to scroll using the mouse. I want a scrollbar.
Looking at the Qt documentation, there seems to be a few options to accomplish this, however there are a few important factors to address before you can decide what approach best suits your needs.
Is the content being displayed in this custom tree column static or dynamic?
Is there a one to one mapping of rows from your QTreeWidget to your custom tree column?
If your custom tree column content IS static and there IS a one to one mapping of rows , use of the QTreeWidget::setItemWidget ( QTreeWidgetItem * item, int column, QWidget * widget ) function should suffice.
However, if the content of your custom tree column is dynamic OR there is not a one to one mapping of rows, this will require a more complex approach.
As described in the documentation for QTreeWidget; "If you want to display custom dynamic content or implement a custom editor widget, use QTreeView and subclass QItemDelegate. "
QItemDelegate, and its sub classes, perform all drawing facilities for items inserted into Qt item views (like QTreeView, QListView, QTableView, etc..). This essentially allows you to control ALL drawing operations for any item inserted into a QTreeView class, letting you draw dynamic content in addition to being able to extend content across multiple rows.
Having implemented a similar approach for a QListWidget, I recommend using QStyledItemDelegate in lieu of QItemDelegate as it allows you to more easily integrate this widget with your application's style layout. As you did not detail the exact use of this custom QWidget, you also might need the additional facilities provided by QItemEditorCreator, QItemEditorCreatorBase and QItemEditorFactory. I would post the similar widget I developed here if I could, but sadly it is part of a proprietary software suite.
This is not completely pretty, because it got it's problems when the custom widget is in the right-most column and the column is made narrow, but it's a start:
#include <QtGui>
class TreeWidget : public QTreeWidget
{
Q_OBJECT
public:
TreeWidget();
QRect columnRect(int column) const;
private slots:
void repositionColumnWidget();
private:
QPushButton * mColumnWidget;
};
TreeWidget::TreeWidget()
: mColumnWidget(new QPushButton("Custom Column Button", viewport()))
{
const int COLUMN_COUNT = 6;
setColumnCount(COLUMN_COUNT);
for (int row = 0; row < 400; ++row)
{
QStringList columns;
for (int column = 0; column < COLUMN_COUNT; ++column)
{
columns << QString("row %1, column %2").arg(row + 1).arg(column + 1);
}
addTopLevelItem(new QTreeWidgetItem(columns));
}
for (int column = 0; column < COLUMN_COUNT; ++column)
{
resizeColumnToContents(column);
}
repositionColumnWidget();
mColumnWidget->show();
connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(repositionColumnWidget()));
connect(header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(repositionColumnWidget()));
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(repositionColumnWidget()));
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(repositionColumnWidget()));
}
QRect TreeWidget::columnRect(int column) const
{
int itemCount = topLevelItemCount();
if (!itemCount)
{
return QRect();
}
int columnX = header()->sectionViewportPosition(column);
int columnY = visualItemRect(topLevelItem(0)).top();
int columnWidth = header()->sectionSize(column);
int columnHeight = visualItemRect(topLevelItem(itemCount-1)).bottom() - columnY + 1;
return QRect(columnX, columnY, columnWidth, columnHeight);
}
void TreeWidget::repositionColumnWidget()
{
mColumnWidget->setGeometry(columnRect(3));
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
TreeWidget treeWidget;
treeWidget.resize(800, 600);
treeWidget.show();
return a.exec();
}
#include "main.moc"
The though that has come to my mind after a week is to hijack the H-scrollbar for the QTreeWidget, and then make that scrollbar scroll only the final column. Because right now, all the columns fit when the window is 620x670px, and who has a screen that small anymore?
Unless anyone has a better solution or objections as to why this is a bad idea, this is the way I'm going to do it.

Resetting Qt Style Sheet

I've managed to style my QLineEdit to something like this:
alt text http://www.kimag.es/share/54278758.png
void Utilities::setFormErrorStyle(QLineEdit *lineEdit)
{
lineEdit->setStyleSheet(
"background-color: #FF8A8A;"
"background-image: url(:/resources/warning.png);"
"background-position: right center;"
"background-repeat: no-repeat;"
"");
}
I called the function using
Utilities *util = new Utilities;
util->setFormErrorStyle(lineNoStaf);
The flow should be something like this:
User open form
User fill data
User submit data
Got error
Use setFormErrorStyle()
User edit the text in the QLineEdit and the style disappear
This function should be reusable over and over again, but how can I connect QLineEdit signal such as textChanged() to a function in other class that will reset the Style Sheet and then disconnect the signal so that it won't be running continuously every time the text changed ?
Qt also allows dynamic properties in its stylesheet, that means you don't need to code your own class for every widget type in your form.
From http://qt-project.org/doc/qt-4.8/stylesheet-examples.html
Customizing Using Dynamic Properties
There are many situations where we need to present a form that has mandatory fields. To indicate to the user that the field is mandatory, one effective (albeit esthetically dubious) solution is to use yellow as the background color for those fields. It turns out this is very easy to implement using Qt Style Sheets. First, we would use the following application-wide style sheet:
*[mandatoryField="true"] { background-color: yellow }
This means that every widget whose mandatoryField Qt property is set to true would have a yellow background.
Then, for each mandatory field widget, we would simply create a mandatoryField property on the fly and set it to true. For example:
QLineEdit *nameEdit = new QLineEdit(this);
nameEdit->setProperty("mandatoryField", true);
QLineEdit *emailEdit = new QLineEdit(this);
emailEdit->setProperty("mandatoryField", true);
QSpinBox *ageSpinBox = new QSpinBox(this);
ageSpinBox->setProperty("mandatoryField", true);
Works also in Qt 4.3!
Allright, this is not compile but should work in principle, you should be able to change the look by calling editWidget->setProperty('isError',true) or editWidget->setError(false)
class ErrorTextEdit : QLineEdit
{
Q_OBJECT
QPROPERTY(bool isError, READ isError, WRITE setError);
public:
ErrorTextEdit(QWidget* parent) : QLineEdit(parent), m_isError(false)
{
m_styleSheet = "" // see below
setStyleSheet(m_styleSheet);
}
void setError(bool val)
{
if (val != m_isError)
{
m_isError = val;
setStyleSheet(m_styleSheet);
}
}
bool isError() {return m_isError;}
private:
QString m_styleSheet;
bool m_isError;
}
for the stylesheet
ErrorTextEdit[isError="false"]
{
optional ...
Style for textedit that is NOT an error
}
ErrorTextEdit[isError="true"]
{
background-color: #FF8A8A;
background-image: url(:/resources/warning.png);
background-position: right center;
background-repeat: no-repeat;
}
the term
[<property>="<value>"]
restricts the application of the stylesheet to instances of the class whose <property> has the appropriate <value> the only caveat is that the style is not changed when the property changes its' value, so the stylesheet has to be reapplied for the look of the widget to actually change, see Stylesheet Documentation -> Property Selector
This construction moves the stylesheet into the widget that uses it and makes switch internal to the widget, the widget changes in accordance to its state.
In general you have a couple of ways to handle invalid inputs in your form
a) observe every change and update the style appropriately, you should be able to use QValidator for that too, but that is a separate topic, using QValidator you will probably be able to completely internalize the state of a single QTextEdit and not have to deal with its validity from the outside
b) Do it in the submit loop that you have described above, whenever the user clicks on submit change the state of the correct and incorrect fields
it all depends the structure of your app and the view
See, the other idea is you need to override the paint evet of line edit and then set the background image and color.
here the implimentation is presetn here button, follow up the same to your line edit

Resources