Mind that this question isn't a duplicate of question Making only one column of a QTreeWidgetItem editable, as it's proposed solution doesn't work.
Hello, so I just want to make only ONE column of my treeWidget editable.
propertyItems.push_back(new QTreeWidgetItem); //gets filled by the while-loop
propertyItems[propertyItems.size()-1]->setText(0, prop.name); //sets the text of the item
propertyItems[propertyItems.size()-1]->setText(1, prop.value);//set the text of the other item
propertyItems[propertyItems.size()-1]->setFlags(Qt::ItemIsEditable);
ui->treeWidget_3->insertTopLevelItem(ui->treeWidget_3->topLevelItemCount(), propertyItems[propertyItems.size()-1]); //appends the items
counter ++;
and
void MainWindow::onTreeWidget3ItemDoubleClicked()
{
if (ui->treeWidget_3->currentColumn() == 2) {
ui->treeWidget_3->editItem(ui->treeWidget_3->currentItem(), ui->treeWidget_3->currentColumn());
}
}
is my approach. ontreeWidget3ItemDoubleClicked is connected with treeWidget::doubleClicked, treeWidget_3 has NO edit-triggers
BUT: when I execute the programm, the QTreeView is just grayed out.
That said, I also tried
propertyItems[propertyItems.size()-1]->setFlags(propertyItems[propertyItem.size()].flags | Qt::ItemIsEditable);
The treeWidget_3 isn't grayed off anymore, but it is still uneditable...
How can I fix this?
BTW: I am a newb to Qt so I might have forgotten something crucial. Sorry in this case.
As mentioned in the documentation:
The QTreeWidgetItem class provides an item for use with the QTreeWidget convenience class.
It means that it won't work for all use cases. The solution is to create your own model and overload the flags(const QModelIndex& index) method returning the appropriate values (basically Qt:: ItemIsEnabled for read-only columns and Qt:: ItemIsEnabled | Qt::ItemIsEditable for the editable one). You can get the column from index.column().
Qt provides an example to start with trees and models.
Related
I'm working on a custom QDialog for the user to choose a directory on the filesystem. I'm using a QFIleSystemModel inside a QTreeView. Whenever the user selects an item in the QTreeView the directory is written to a QLineEdit. My problem is I would like to do the opposite of-sorts by expanding through the QTreeView nodes by taking the typed text and... well... obviously expanding the nodes if the text typed is an existing, absolute path.
I've searched for quite a few variations of my problem(although I know it's very loaded) and looked through a lot of the documentation of the classes but I can't find anything to really help me. I'm guessing I need to use QTreeView::expand(const QModelIndex) to expand them after finding but searching through the index is my biggest problem as of now. I'm open for any suggestions and any help is truly appreciated. Thank you in advanced and sorry for making such a wall of text.
searching through the index is my biggest problem as of now
And index is just a "pointer" to an item in the model. You can't search "through" it, because there is nothing "in" an index. It's just a pointer to exactly one item.
You should search through the model. The index(const QString & path) method does that for you. Given a path, it returns an index into the element at the end of the path. You can then iterate upwards to extend the items:
// ...because QModelIndex::operator= doesn't exist
QModelIndex & assign(QModelIndex & dst, const QModelIndex & src) {
dst.~QModelIndex();
new (&dst) QModelIndex(src);
return dst;
}
void extend(const QString & path, QTreeView * view) {
auto fs = qobject_cast<QFileSystemModel*>(view->model());
if (!fs) return;
auto idx = fs->index(path);
// ascend up from the child item and expand items in the tree
while (idx.isValid()) {
view->expand(idx);
assign(idx, idx.parent());
}
}
You can use this method with the last item removed from the path as well, since - presumably - the last item might not be valid, and thus fs->index might fail.
Have you check here?
[Model/View Programming][Insert and delete rows in QTreeView]
http://doc.qt.io/qt-4.8/model-view-programming.html
QTreeView & QAbstractItemModel & insertRow
As base design principle, View only render the data.You should not expect to modify the data via View directly.
Im writing an QT application, where I have 3 QComboBoxes, with a list of values. I'm trying to do so, when I select one item in a QComboBox, I will remove it from the other QComboBoxes, and when I select another or nothing, it will reappear in the other QComboBoxes again.
Do you have any ideas for this?
Edit:
I have tried to use QStringList, where I had a slot, which removed it from the other QComboBoxes, but it was very bugged and often inserted 2 whitespaces and the same drink twice.
If all comboboxes contain the same items, then you can use the current index of one combobox to disable and hide that index of other comboboxes.
You can just subclass QComboBox and create a slot like this:
void MyComboBox::disableItem(int index)
{
QListView *list_view = qobject_cast<QListView*>(view());
if(list_view)
{
QStandardItemModel *model = qobject_cast<QStandardItemModel*>(list_view->model());
list_view->setRowHidden(index, true);
if(model)
{
model->item(index, 0)->setEnabled(false);
}
}
}
Then you just connect the QComboBox::currentIndexChanged(int index) signal from other comboboxes to this slot. Do this for all 3 comboboxes.
You should also create a logic for enabling and showing the items again when they shouldn't be disabled. It's almost the same function as the one above. You could just create a list of indexes that should be hidden for that combobox and use that to show all the other indexes.
I have a QTableWidget in my Main Window class.
I am unable to find a functionality which will undo the text change for the specified cell.
What I want to do is:
void myCellUndoFunc(int row, int col)
{
table->item(row, col)->undo(); //table is my QTableWidget
}
The problem is that there is no such undo().
So my question is, can there be a workaround for this problem using maybe some foo-doo combination of SIGNAL's & SLOT's?
Thanks!
PS: Please do not suggest to use Model/View framework because I have used QTableWidget extensively in my application. Sorry for the same.
Maybe you should use the
void QTableWidgetItem::setData ( int role, const QVariant & value ) [virtual]
using the Qt::UserRole you are able to specify the last value. In your method u can access the previously set value with the data()-Method. The only thing you have to do is always keep the old value up-to-date.
Before you set the new value of the QTableWidgetItem
tw->setData(Qt::UserRole, tw->text())
and on undo u could than retrieve the data with
tw->setText(tw->data(Qt::UserRole).toString())
where "tw" is the current QTableWidgetItem using the contextmenu-event, the clicked-event or whatever u want. You could also subclass the QTableWidgetItem and handle this whole thing internally in your class, creating an undo()-method, storing the old value, etc.
I'm trying to improve this example for a diagram editor.
The example uses a nodes class with a few attributes unrelated to my needs. Right now I want to add a "list of arrays" to this node class in order to then populate a QTableView in the "properties" dialog. I already designed the properties dialog with the QTableView. I'm not even sure this is feasible/makes sense.
Basically the table must have 4 columns: name, type, value and unit.
Each row of the table is a certain "property" I need the node to have.
My question is: how can/should I model the table at class level? I ask this because I have been looking to QList, QVariant, QMap, QMultiMap and I can't figure out out to use them correctly, and none of the examples I found so far are any help either, at least for what I need to do. I saw something about the QStandardItemModel class, and I think it relates to the solution, but I can't understand how/why.
To top it off, I'm a Qt/C++ beginner, so much of the dynamics and jargon in Qt/C++ are still elluding me.
If anyone could give me some pointers, that would be great.
EDIT:
This isn't getting much attention, and I don't know if its because I wasn't clear enough, but anyway, try to picture this:
a Node has a PropertyList
that PropertyList lists Properties
Properties are sort of dictionaries, as they have always 4 attributes and respective values: name:(string), type:(string), value:(double) and unit:(string).
one Node can only have one PropertyList; a PropertyList can have several Properties. Some Nodes will have 3 properties, others will have 4, etc...
Now, the Nodes are represented in a diagram via a QGraphicsScene with QGraphicsItem. You can access a PropertiesDialog widget that has a QTableWidget in it. This table will show the PropertyList.
I want to know what kind of "structure" I can create/define that enables me to easily insert/read data in the table widget - ie, I load the data into the PropertiesList of the Node and it shows up in the table widget; if I change the data in the table widget, it passes on to the PropertiesList of the Node.
Hope this helps clearing out any doubts that may arise.
Well, after a lot of hair pulling, I got what I needed.
My first step was to create a Property class. This class has functions to set or get a name string, a type string, a value double and a unit string.
Next, I updated the Node class to include functions to add and remove Property object pointers to a QList<Property *>. Also included was a listProperties function that returns all the Property objects from a certain Node.
After this, the function to populate the QTableWidget with a certain Node's properties was coded like this:
propertiesList = node->listMyProperties();
for (int row = 0; row < propertiesList.size(); ++row) {
Property *property = propertiesList.at(row);
addRow();
tableWidget->item(row, 0)->setData(Qt::DisplayRole, property->propertyName());
tableWidget->item(row, 1)->setData(Qt::DisplayRole, property->propertyType());
tableWidget->item(row, 2)->setData(Qt::DisplayRole, property->propertyValue());
tableWidget->item(row, 3)->setData(Qt::DisplayRole, property->propertyUnit());
}
And the addRow() function:
void PropertiesDialog::addRow()
{
int row = tableWidget->rowCount();
tableWidget->insertRow(row);
QTableWidgetItem *item0 = new QTableWidgetItem;
item0->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
tableWidget->setItem(row, 0, item0);
QTableWidgetItem *item1 = new QTableWidgetItem;
item1->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
tableWidget->setItem(row, 1, item1);
QTableWidgetItem *item2 = new QTableWidgetItem;
item2->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
tableWidget->setItem(row, 2, item2);
QTableWidgetItem *item3 = new QTableWidgetItem;
item3->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
tableWidget->setItem(row, 3, item3);
tableWidget->setCurrentItem(item0);
}
This produces what I needed: to have a class to hold the property values related to each node, and present them on a QTableWidget. Next step is to make the reverse path, meaning, when edits occur in the QTableWidget, those changes should propagate to the class. Now I think I can find my way, hope this helps anyone trying to find something related. I'll also update the tags and maybe edit the title to make it more relevant/meaningful.
I don't have enough experience with Qt yet to make a good design choice. Any help by experienced Qt programmers would be very appreciated.
I'm trying to find out which model to subclass, which view to use, what delegate subclassing / extending I should do...
My problem is similar to: I have these zones I would like to display, 1 per row:
class Zone{
//inputs
string country; //edited with a QComboBox
string city; //edited with a QComboBox
int ageMin;
//stored result
int nbInhabitantsOlderThanMin;
}
Here's what I'd like to do, and the design choices each requirements makes me think of:
I would like to display a list of them (--> QListView )
But to display 1 item I need several columns (--> QTableView )
I would like a double click on a row to trigger editing in a custom widget, since nbInhabitantsOlderThanMin can not be edited, and choosing a country restricts the list of cities that can be chosen in the QComboBox (and vice versa in my real example) (--> I should probably use a QDataWidgetMapper (or subclass?) somewhere...)
So whereas the edition of a row should happen in a widget, the display is simple / not custom, and subclassing a delegate (QStyledItemDelegate for instance) (I'm not so sure about this one) doesn't seem to be the right way to have 1 custom widget with many child input widget to edit the 3 fields at the same time.
I think the data to model would favor a model subclassing QAbstractListModel, but the display with many columns compatible with default delegate viewing favors a QAbstractTableModel..
So I don't really know which design to go for. Any experienced help connecting the dots is very welcome :)
QDataWidgetMapper is a slightly different thing. It is a way to display one item from a Model (ex. QStandardItemModel), using custom controls. You can read more about it here, with accompanying snapshots and an example of how to implement one.
While it is certainly cool, I don't think it is what you want here. Mostly because you specified that you want to view your items in a list format. However, you could display all your items in a simple list, double-click which would open a dialog using the QDataWidgetMapper. In which case all you would need to do with a QListView/QListWidget is implement the double-click event.
Still, I personally don't like the added burden of the extra window on a user. I prefer to use popups sparingly. But if you like that approach, then go ahead. This is another example of the QDataWidgetMapper which is pretty nice.
My preferred approach is still to use the QTableView, and provide delegates for the columns that need specialized editing. Here is a great walk-through of all things Model/View. So if you decide to use the QListView or QTableView it will give you a great start. It also talks about how you can create delegates to edit fields however you want.
So, how do you create a custom delegate? Basically, you just inherit from QItemDelegate. There are some examples in the link above, but I'll highlight a few salient points.
QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &index) const
{
QComboBox *editor = new QComboBox (parent);
// Add items to the combobox here.
// You can use the QModelIndex passed above to access the model
// Add find out what country was selected, and therefore what cities
// need to be listed in the combobox
return editor;
}
void ComboBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QComboBox *comboBox= static_cast<QComboBox *>(editor);
int _SelectedItem = // Figure out which is the currently selected index;
comboBox->setCurrentIndex(_SelectedItem);
}
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *comboBox= static_cast<QComboBox *>(editor);
comboBox->interpretText();
int value = comboBox->currentIndex();
// Translate the current index to whatever you actually want to
// set in your model.
model->setData(index, value, Qt::EditRole);
}
Fill in the gaps that I left in my example and you have your Delegate.
Now, how to use this in your QTableView:
You can set a delegate for a particular column of your table as follows:
setItemDelegateForColumn(_ColumnIndex, new ComboBoxDelegate(_YourTableModel));
And, if you want to prevent certain columns from being editable:
_YourTableModel->setColumnEditable(_ColumnIndex, false);
Once you have your model set up, everything else should take care of itself.
Hope that helps.
First, you should subclass QAbstractItemDelegate (or QItemDeleage, that could be more convenient), where reimplement createEditor, setEditorData and setModelData.
Than, you should set your own itemDelegate (see QAbstractItemView::setItemDelegate).
It's commonly no difference, what widget to use to present data: It could be either QTreeWidet, or QTreeView, or QTableWidget, or QTableView. Note, that "widgets" are easier to use, than "views", but they are not so powerfull
I just finished something very similar to this in that I needed multiple fields for each object which I wanted in a row. The way I did it, which worked out very well, was to subclass QAbstractListmodel and use a custom ListItem. I kind of faked the columns by having a custom delegate and using some javascript to figure out the size of the largest thing in each field, and then set the column size to that width. I think this is the easiest way of doing it.
For the comment below, zone would inherit from:
class ListItem: public QObject {
Q_OBJECT
public:
ListItem(QObject* parent = 0) : QObject(parent) {}
virtual ~ListItem() {}
virtual QString id() const = 0;
virtual QVariant data(int role) const = 0;
virtual QHash<int, QByteArray> roleNames() const = 0;
signals:
void dataChanged();
};