I am displaying a TreeView with a custom sortfilterproxymodel (which takes another custom model as source) and a custom delegate (overwritten paint) to affect the display of each item.
However, I cannot get the header of the TreeView to show. I had a look at both the proxy and the normal model and both have their headerData() called and return the correct values. I do not explicitly hide the header. In fact, I explicitly show() the header of the TreeView and setHeaderHidden() to false.
What could cause the header not being shown?
Here is the paint() function of the delegate, as I suspect the mistake somewhere there:
//---------------------------------------------------------------------------------
void
MyDelegate::paint(QPainter* p_painter, const QStyleOptionViewItem& p_option, const QModelIndex& p_index) const
{
// Get a custom text
QString text = "";
// Code that changes the text variable, nothing fancy, no pre-mature return
// Left out for convenience
// Call painter methods for drawing
p_painter->save();
p_painter->setClipRect(p_option.rect);
drawBackground(p_painter, p_option, p_index);
drawDisplay(p_painter, p_option, p_option.rect, text);
drawFocus(p_painter, p_option, p_option.rect);
p_painter->restore();
}
If you wonder why I do all the painter stuff (save(), drawBackground, etc.) manually, it is because it seems to be the only way to change the displayed text inside the paint() function. At least the only one I could figure out. But I do not know if this has anything to do with the header not being shown in the view.
Edit: By now I tried to replace my own paint with the default one. The header is still not shown, so the paint() method seems to be innocent ;)
The problem was that I "forgot" to add the following to the beginning of the headerData() function:
if (role != Qt::DisplayRole)
return QVariant();
Though I have to say it is a bit weird that you have to have those lines in order to display anything at all. If they are required like that, why not do that check before headerData() is even called?
Anyway, I hope that may help some people with the same problem :)
Related
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)
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.
In my application, I have my re-implemented QGraphicsView checking for a mouseReleaseEvent(), and then telling the item at the position the mouse is at to handle the event.
The QGraphicsItem for my view is made up of two other QGraphicsItems, and I check which one of the two is being clicked on (or rather having the button released on), and handle the respective events.
In my Widget's constructor, I set one of the items as selected by default, using the same methods I used when the items detect a release.
When I debugged, I found that for the LabelItem, select is called without a problem from the constructor (and the result is clear when I first start the application). But, when I click on the items, the application terminates. I saw that I was getting into the select function, but not leaving it. So the problem is here.
Which is very weird, because the select function is just a single line setter.
void LabelItem::select()
{
selected = true;
}
This is the mouseReleaseEvent;
void LayerView::mouseReleaseEvent(QMouseEvent *event)
{
LayerItem *l;
if(event->button() == Qt::LeftButton)
{
l = (LayerItem *) itemAt(event->pos());
if(l->inLabel(event->pos()))
{ //No problem upto this point, if label is clicked on
l->setSelection(true); //in setSelection, I call select() or unselect() of LabelItem,
//which is a child of LayerItem, and the problem is there.
//In the constructor for my main widget, I use setSelection
//for the bottom most LayerItem, and have no issues.
emit selected(l->getId());
}
else if(l->inCheckBox(event->pos()))
{
bool t = l->toggleCheckState();
emit toggled(l->getId(), t);
}
}
}
When I commented the line out in the function, I had no errors. I have not debugged for the other QGraphicsItem, CheckBoxItem, but the application terminates for its events as well. I think the problem might be related, so I'm concentrating on select, for now.
I have absolutely no clue as to what could have caused this and why this is happening. From my past experience, I'm pretty sure it's something simple which I'm stupidly not thinking of, but I can't figure out what.
Help would really be appreciated.
If the LabelItem is on top of the LayerItem, itemAt will most likely return the LabelItem because it is the topmost item under the mouse. Unless the LabelItem is set to not accept any mouse button with l->setAcceptedMouseButtons(0).
Try to use qgraphicsitem_cast to test the type of the item. Each derived class must redefine QGraphicsItem::type() to return a distinct value for the cast function to be able to identify the type.
You also could handle the clicks in the items themselves by redefining their QGraphicsItem::mouseReleaseEvent() method, it would remove the need for the evil cast, but you have to remove the function LayerView::mouseReleaseEvent() or at least recall the base class implementation, QGraphicsView::mouseReleaseEvent(), to allow the item(s) to receive the event.
I have seen these odd behaviours: It was mostly binary incompatibility - the c++ side looks correct, and the crash just does not make sense. As you stated: In your code the "selected" variable cannot be the cause. Do you might have changed the declaration and forgot the recompile all linked objects. Just clean and recompile all object files. Worked for me in 99% of the cases.
I have QTableView using a QSqlQueryModel (it fetches data from SQLite).
There is a QStyledItemDelegate subclass called MiniItemDelegate that I use as a delegate for the items. I set up a sizeHint() method like this:
QSize MiniItemDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
// just for testing...breakpoint shows this line never gets called
return QSize(256,256);
}
I'm not sure why this method isn't called when I run the following code:
m_pMiniItemDelegate = new MiniItemDelegate(this);
ui->PList_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->PList_tableView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->PList_tableView->setItemDelegate(m_pMiniItemDelegate);
ui->PList_tableView->setAlternatingRowColors(true);
ui->PList_tableView->setModel(ListMiniSqlModel::instance());
This also doesn't work:
ui->PList_tableView->resizeColumnsToContents();
ui->PList_tableView->resizeRowsToContents();
Nor does this:
QHeaderView* headerView = ui->PList_tableView->horizontalHeader();
headerView->setResizeMode(QHeaderView::ResizeToContents);
QStyledItemDelegate::sizeHint is useful only when QTableView::resizeRowsToContents, QTableView::resizeRowToContents, QTableView::resizeColumnsToContents and QTableView::resizeColumnToContents are called. or use
QHeaderView* headerView = tableView->horizontalHeader();
headerView->setResizeMode(QHeaderView::ResizeToContents);
Have you tried: setColumnWidth or setRowHeight and horizontalHeader()->setResizeMode(QHeaderView::Fixed) ?
(Credit where credit is due.)
In #HostileFork's comment about a Qt Forum discussion, there's a comment thread. Within that thread, a user mikhailt offers a good solution.
The verticalHeader has a DefaultSectionSize property that can be adjusted. It doesn't matter whether the vertical header (labels on the left side of the table) is actually being displayed or not, the size will still be used.
ui->PList_tableView->verticalHeader()->setDefaultSectionSize(34);
This just solved my problem with Qt 5.6, and saved me from adjusting each data row's height separately, or causing a resize on a table.
Based on the age of the comment thread where I found it, this was already working in Qt 4, too.
//in my .h file i have:
void on_doubleSpinBox_test_valueChanged(double t);
//in my .cpp(mainwindow):
void MainWindow::on_doubleSpinBox_test_valueChanged(double t)
{
ui->lineEdit_test->setText(QString::number((double) t/2));
}
My problem is that when i set the value for lineEdit in SpinBox everything works, however in doubleSpinBox there is no message sent (changing value doesn't work).
What am i doing wrong? How to make this method to work?
The valueFromText and textFromValue methods might do what you want. They allow displaying the value of the spin box in a customized format, such as always displaying half the value of the spin box as in your code above.
http://doc.trolltech.com/4.7/qdoublespinbox.html#valueFromText
http://doc.trolltech.com/4.7/qdoublespinbox.html#textFromValue
A sample of how to use the methods is in the class QSpinBox and they are used the same in QDoubleSpinBox.
http://doc.trolltech.com/4.7/qspinbox.html#subclassing-qspinbox