Do I need to implement my own QAbstractTableModel? - c++

I found this question: How to change the background color for a QTreeView Header (aka QHeaderView)?
I want to be able to set the color for each header section. So the question seen above seems to be my solution!
The solution says "the easiest way to do that is probably to derive a new model from QAbstractItemModel or another model class and reimplement the headerData()". I went and looked at the Qt source tree for QTableWidget, QTableModel, QTableWidgetItem... these classes are supposedly "default models" so I thought they would be a good example and then I would go implement my own QAbstractTableModel.
The 3 files I saw are a whopping 3300 lines of code. That is definitely NOT "the easiest way" IMO!!!
I would like the functionality of QTableWidget but then I want to add the following ability:
horizontalHeader.setSectionColor(index,color)
verticalHeader.setSectionColor(index,color)
Do I really need to inherit/implement QAbstractTableModel if all I want is to change the color of a section header?
Update:
I am not using my own custom view and model classes. I am using the convenience class QTableWidget right now (it is called a convenience class b/c it implements the view and model). The function headerData() is part of the model. The model class, QTableModel, is not accessible via Qt lib/headers so I can't inherit from it.
Update:
I tried creating a new item with background brush QBrush(QColor(Qt::red)) and then setting the table's header with the new item (using QTableWidget::setHorizontalHeaderItem(int column, QTableWidgetItem *item). I also tried inheriting QTableWidgetItem and overriding the virtual data() method with:
QVariant HeaderItem::data(int role) const
{
if(role==Qt::BackgroundRole) {
return QVariant(QBrush(QColor(Qt::red)));
} else if(role==Qt::ForegroundRole) {
return QVariant(QBrush(QColor(Qt::green)));
} else {
return QTableWidgetItem::data(role);
}
}
I can change the header sections foreground. But when I try to change the header's background brush... nothing happens... it's like the QTableWidgetItem's background brush that I set for the header section is ignored.

Instead of creating model with custom headerData() from scratch create subclass of QTableWidgetItem with desired implementation of QTableWidgetItem::data() and use the instances of this class for QTableWidget::setHorizontalHeaderItem.

Related

C++ QT tableView not displaying header labels after setting a custom header [duplicate]

This question already has answers here:
QLabel does not display in QWidget
(2 answers)
Closed 1 year ago.
I am working with a tableView and trying to implement some size styling.
The code:
// Setup table header items
Views::TeamTableHeaderView *header = new Views::TeamTableHeaderView(Qt::Horizontal);
ui->tableView->setHorizontalHeader(header);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->tableView->horizontalHeader()->setStretchLastSection(true);
ui->tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter | (Qt::Alignment)Qt::TextWordWrap);
for(auto& tableModel : entitlementLicenseModelMap)
{
tableModel->setHorizontalHeaderLabels({"PRODUCT", "TOTAL SEATS", "VALID UNTIL"});
}
This does not display any header on the table at all. When the call to setHorizontalHeader(header) is removed, the header displays fine. However, the header is too thin to render the text without cutting it off, hence I require a custom header (TeamTableHeaderView) to implement the sizeHint() to make it taller.
I have tried moving the setting of the header labels before and after, but the issue is that the header is just mot displaying at all, not even with the default "1" "2" "3" labels.
Some information:
This code executes after all of the data has been (successfully) populated in the tableModel.
The tableView is a bog standard QTableView
The TeamTableHeaderView is a custom type inherited from QHeaderView which only implements sizeHint(). Currently it just returns QHeaderView::sizeHint(). There is nothing special about this class yet.
The entitlementLicenseModelMap is a bit strange, but irrelevent to this question I believe. That whole for loop you can treat as just setting a table model. That table model is a QStandardItemModel.
Earlier in the code, a custom delegate is set to the model. However this delegate is not yet implemented. It is only set in order to later do some painting stuff. It inherits from QStyledItemDelegate, and only implements the paint function, which currently only saves the painter, calls QStyledItemDelegate::paint(...) and then restores the painter. I don't believe this delegate is relevant, but happy to be corrected.
To anyone else encountering this you just need to manually call:
header->setVisible(true)
Why this is the case when the header is normally visible by default when not otherwise specified.

Including a button in a WTableView

I'm working on a Wt-based gui, and struggling with something that feels like it should be very simple:
The gui displays 'events'. In our project we have a model (ActiveEventsModel, based on WStandardItemModel) and a view, (ActiveEventsView, based on WTableView). Currently the view simply shows some text corresponding to various properties of the 'events'. The aim is to add an additional column to the table, contain a button (for example, a WPushButton) which allows the user to "acknowledge" the event shown in that row (in context, this essentially translates to dismissing the event - i.e. each event has a button, and clicking the button will remove that event from the model).
I searched online and found this discussion on the Wt-forum, which contains some guidance on putting a button in (derive from WAbstractItemDelegate and use setItemDelegateForColumn) and a code example for creating a button in an item delegate. After some minor changes for Wt-4.0 compatibility I now have my button in the table.
I've defined a very simple class, EventDelegate, derived from WAbstractItemDelegate:
header:
#include <Ui/WebToolkit.h> //convenience header containing all of the Wt elements we use
class EventDelegate : public Wt::WAbstractItemDelegate
{
public:
EventDelegate();
std::unique_ptr<Wt::WWidget> update(Wt::WWidget *widget, const Wt::WModelIndex &index,
Wt::WFlags<Wt::ViewItemRenderFlag> flags) override;
void acknowledge(Wt::WModelIndex &index);
};
source:
#include <Ui/EventDelegate.h>
EventDelegate::EventDelegate()
: WAbstractItemDelegate()
{
}
std::unique_ptr<Wt::WWidget> EventDelegate::update(Wt::WWidget *widget, const Wt::WModelIndex &index,
Wt::WFlags<Wt::ViewItemRenderFlag> flags)
{
std::unique_ptr<Wt::WWidget> w = std::make_unique<Wt::WPushButton>("acknowledge");
Wt::WPushButton *button = dynamic_cast<Wt::WPushButton*>(w.get());
button->clicked().connect(std::bind(&EventDelegate::acknowledge,this,index));
return w;
}
void EventDelegate::acknowledge(Wt::WModelIndex &index) {
//how do I interact with the model?
}
This seemed promising at first, but I just can't figure out how to actually make the button do anything to the model. I thought maybe I need to override WAbstractItemDelegate::setModelData, but for that I need the model, which the ItemDelegate doesn't seem to know anything about.
I do have the ModelIndex, and the ModelIndex knows which Model it belongs to - but will only give me it as const, which is no use to me because setModelData needs to alter it.
Should I just be giving the Model to the Delegate directly, for example as a constructor parameter? That seems an easy solution but feels very inelegant. Or am I just barking up the wrong tree entirely with how I'm trying to do this? I'm extremely new to Wt (and my colleagues only a little less so - this is a relatively new project and the first in our company that's used Wt) so that's very possible.
(I've not included a whole mcve because I'm pretty sure that what I'm missing here is conceptual. I can add one later if people feel it's really necessary to answer the question)

QT add widgets to UI anywhere

The application that I'm building is supposed to create, destroy, and manipluate widgets that I've created
The problem is I'm not making a simple program with nice buttons where everything is symmetrical and needs to be evenly spaced and handled via a layout that will automatically move everything around and space it.
And yet, the only way I know of is to manually instance a layout and add the widgets to it, but then I can't set the coordinates of them
How can I simply instance my widget, and add it to the project generated frame?
This is how I'm instantiating my class, in which case I then set my own parameters:
Tile *tile = new Tile;
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
To reiterate, I want to add my own widgets to a frame outside of the editor (only by code), and be able to manually move them around the frame by code.
I don't see an ".addWidget() as a method accessible from the QFrame, and yet they can be children within the designer, why can't I do this by code? Whenever I try to do it manually and add them to any layout, any attempt I make to manually set the widgets location doesn't do anything. I haven't overridden the setGeometry
I fixed my problem
After 2 hours of continual searching I finally came across my answer
I never thought that you could set the parent of a widget by code, as I thought you strictly had to add it in as a child of something else, not the reverse and declare that it should have a parent
So, by simply adding the line:
tile->setParent(ui->frame);
completely fixed my problem.
I will change this post back and submit the answer tomorrow when I'm allowed to by this site.
Thank you to those who actually came though. I'm just glad I managed to fix it before that.
All you need is to pass the parent to the widget's constructor:
Tile *tile = new Tile(ui->frame); // <-- here
tile->setImg("://Images/placeholderTile.png");
tile->setCol(true);
tile->setGeometry(retX(line),retY(line),50,50);
Since Tile is your own class, you should definitely have a Qt-style, parent-taking explicit constructor for it:
class Tile : public QWidget {
...
public:
explicit Tile(QWidget * parent = 0) : QWidget(parent) { ... }
};
Another approach is to write your own layout that would know about the relationships that are to be held between your objects. After you do it once, writing custom layouts isn't that hard.

Change C++ model with QML

I want to extend the example called "Object ListModel Example" from Qt documentation
(you can get it on http://qt-project.org/doc/qt-4.8/declarative-modelviews-objectlistmodel.html).
I am trying to add a simple GUI functionality: a menu item that changes the content
(i.e. name) of the first data item in the model. Something like this:
MenuItem {
text: "Item 123"
onClicked: {
myModel.setProperty(0,"name","Item 123") //this gives me error
}
}
I am able to create a menu in QML but I cannot find the correct way to make changes in the model.
Btw, what is a difference between setContextProperty and qmlRegisterType (only the first one is used in this example but many other examples include the second one).
That kind of model is really not suitable for modification. There is no way for the view to be notified of changes. A better option is to use a QAbstractItemModel: http://qt-project.org/doc/qt-4.8/declarative-modelviews-abstractitemmodel.html
A simpler way to use a QAbstractItemModel is via QStandardItemModel: http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html
setContextProperty() adds a single named property to the context. qmlRegisterType() registers a QObject-derived type with the QML engine, allowing it to instantiate that type. For example, the QDeclarativeItem type is registered with the engine as "Item", which is how the engine knows what to create when Item {} appears in QML code.

How to implement delegate in QHeaderView

I have created one table by using QTableview and QAbstractTableModel . i have added some vertical header by using QHeaderView . In one of the header cell i want to use delegate ..
I am using the delegate but it does not have any impact ..
Is anywhere i am doing wrong ?
Had this issue myself. The answer from the Qt documentation is simple and annoying:
Note: Each header renders the data for each section itself, and does
not rely on a delegate. As a result, calling a header's
setItemDelegate() function will have no effect.
In other words you cannot use delegates with QHeaderView.
For the record, if you want to style a QHeaderView section, you'll have to do it either via the header data model (changing Qt::FontRole, etc.) or derive your own QHeaderView (don't forget to pass it to your table with "setVerticalHeader()") and overwrite the its paintSection()-function.
i.e.:
void YourCustomHeaderView::paintSection(QPainter* in_p_painter, const QRect& in_rect, int in_section) const
{
if (nullptr == in_p_painter)
return;
// Paint default sections
in_p_painter->save();
QHeaderView::paintSection(in_p_painter, in_rect, in_section);
in_p_painter->restore();
// Paint your custom section content OVER a specific, finished
// default section (identified by index in this case)
if (m_your_custom_section_index == in_section)
{
QPen pen = in_p_painter->pen();
pen.setWidthF(5.5);
pen.setColor(QColor(m_separator_color));
in_p_painter->setPen(pen);
in_p_painter->drawLine(in_rect.right(), in_rect.top(), in_rect.right(), in_rect.bottom());
}
}
This simplified example could of course easily be done via a stylesheet instead, but you could theoretically draw whatever you like using this method.