Qt QAbstractModel: remove checkbox - qt

I started to learn Qt, and I would like to implement a table filled with data via QTableView. My problem is, that I don't know how to remove the checkboxes from the cells. It seems like they are put in by default.
However, I read that I had to return a NULL-QVariant, but that's not what I was looking for as I still have data to put in.
That's my code so far:
QVariant MyModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
int col = index.column();
QString daten;
switch (col)
{
case 0:
{
daten = "column 1";
break;
}
case 1:
{
daten = "column 2";
break;
}
case 2:
{
daten = "column 3";
break;
}
case 3:
{
daten = "column 4";
break;
}
}
return daten;
}
Now, as you can see, I want to fill the cell with the QString called "daten". But next to the String there is a Checkbox in every cell.
Does somebody know how to remove the checkbox but still fill the content with "daten"?
Thanks!

The fact that the cells in your QTableView have some checkbox hint that they were defined as user-checkable. Check whether you don't have a Qt.ItemIsUserCheckable flag activated somewhere in the definition of your QTableView, and if that's the case, deactivate it. You could try to modify the flags method, for example, forcing every entry not to be checkable
As an additional comment, you should probably modify your ::data method to take into account the case where index is invalid and to return some value only if the role corresponds to Qt.DisplayRole. In Python, the syntax would be
if index.isvalid():
if (role == Qt.DisplayRole):
(row, col) = (index.row(), index.column()
return_something_depending_on_col
return QVariant()
return QVariant()
That way, you cover the case of an invalid index, your code would likely crash otherwise.
The test on role allows you to choose which type of data you want to access. The documentation states for example that:
Each item in the model has a set of data elements associated with it, each with its own role. The roles are used by the view to indicate to the model which type of data it needs. Custom models should return data in these types.
The basic role is Qt.DisplayRole, where you return the QString corresponding to your current cell. You could also return a QBrush for painting the background if your role is Qt.BackgroundRole...
While not mandatory, these tests on role are still highly encouraged: it makes your code cleaner and easier to maintain.

Related

Qt - Dynamically create, read from and destroy widgets (QLineEdit)

I have the following situation:
I have QSpinBox where the user of my application can select how many instances of an item he wants to create. In a next step, he has to designate a name for each item. I wanted to solve this problem by dynamically creating a number of QLabels and QLineEdits corresponding to the number the user selected in the SpinBox. So, when the number is rising, I want to add new LineEdits, when the number falls, I want to remove the now obsolete LineEdits.
Well, guess what - this turns out much more difficult than I expected. I've searched the web, but the results were more than disappointing. There seems to be no easy way to dynamically create, maintain (maybe in a list?) and destroy those widgets. Can anybody point me in the right direction how to do this?
Take a while and check QListWidget, it does what you exactly want for you by using QListWidgetItem.
An little example: this function adds a new element to a QListWidgetwith a defined QWidget as view and return the current index:
QModelIndex MainWindow::addNewItem(QWidget* widget) {
QListWidgetItem* item = new QListWidgetItem;
ui->listWidget->addItem(item1);
ui->listWidget->setItemWidget(item, widget);
return ui->listWidget->indexFromItem(item);
}
Now, if your user selects X items, you should iterate to create X widgets and you could save all the widgets in a QList:
listWidget.clear();
for (int i=0; i<X; i++) {
QTextEdit* edit = new QTextEdit();
const QModelIndex& index = addNetItem(edit);
qDebug() << "New element: " << index;
listWidget.append(edit);
// Handle edit text event
connect(edit, SIGNAL(textChanged()), this, SLOT(yourCustomHandler()));
}
Now, just show the list with all the edit fields.

Set selected item for QComboBox

I have a simple QComboBox widget, which has 2 values inside: True and False.
And I have a QString variable currValue, which is one of those values. I want to set my widget's current value with currValue.
I thought that solution is the following:
first lets initialize currValue;
QString currValue = "False";
QComboBox* combo = new QComboBox();
combo->addItem("True");
combo->addItem("False");
combo->setCurrentIndex(combo->findData(currValue));
But it doesn't work.
Am I doing something wrong ?
And Why QComboBox has no member setCurrentItem() or smth like that ?
You actually need to write it in the following way:
QComboBox* combo = new QComboBox();
combo->addItem("True", "True");
combo->addItem("False", "False");
combo->setCurrentIndex(combo->findData("False"));
The problem in your implementation was that you did not set the items' userData, but only text. In the same time you tried to find item by its userData which was empty.
With the given implementation, I just use the second argument of QComboBox::addItem(const QString &text, const QVariant &userData = QVariant())) function that sets the item's userData (QVariant).
UPDATE:
The alternative way to find the combo box item is setting the specific role as the second argument for QComboBox::findData() function. If you don't want to explicitly set the user data, you can refer to the items texts with Qt::DisplayRole flag, i.e.:
QComboBox* combo = new QComboBox();
combo->addItem("True");
combo->addItem("False");
combo->setCurrentIndex(combo->findData("False", Qt::DisplayRole)); // <- refers to the item text
UPDATE 2:
Another alternative could be using text based lookup function QComboBox::findText():
QComboBox* combo = new QComboBox();
combo->addItem("True");
combo->addItem("False");
combo->setCurrentIndex(combo->findText("False"));
I have got answer to my own question.
combo->setCurrentIndex(combo->findText(currValue));

add a text with two different colors in a QTableView column in qt

I am using a QTableView to display user activity in my application.
My model contains four columns, each column displays a different type of text like:
username in the 1st column
user activity in the 2nd column
details of activity in the 3rd column
I want to display both the text of 2nd and 3rd columns in a single column with a different text color separated with a "-".
My application currently looks like this:
However, I want it to look similar to this
Can someone please help me to solve this out, thanks in advance
I would approach to this topic in two steps:
change model in such way that 2nd and 3rd columns are a single column but value from old 2nd column is returned as Qt:DisplayRole (standard way) and value from old 3rd column is returned for Qt::SomeUserRole
Add delegate to table view which will handle visualization of merged columns and display item in "gmail" style. Values for visualization are fetched form model using respective role values from previous step.
You can also treat the whole thing as a list with lots of different data roles: each row as a single item (this could be handled very nicely in qml).
You can override data method of your model and return custom color with Qt::ForegroundRole for necessary columns.
But if you like "gmail" visualisation - than you should create your own delegate (as proposed by #Marek). You may use QTextDocument for advanced text rendering. Pseudo-code:
void QStyledItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
painter->save();
const QString title = index.model()->data( YourTitleRoleId ).toString();
const QString content = index.model()->data( YourContentRoleId ).toString();
// Use any qt-supported html tags
const QString html = QString( "<html><b>%1</b> - <i>%2</i></html>" ).arg( title ).arg( content );
QTextDocument doc;
doc.setHtml( html );
QRectF rc( option.rect );
doc.drawContents( painter, rc );
painter->restore();
}
Feel free to ask, if something is not clear.

Checking then Adding items to QCompleter model

I am currently working on a code editor written in Qt,
I have managed to implement most of the features which I desire, i.e. auto completion and syntax highlighting but there is one problem which I can't figure out.
I have created a model for which the QCompleter uses, which is fine for things like html tags and c++ keywords such as if else etc.
But I would like to add variables to the completer as they are entered by the user.
So I created an event on the QTextEdit which will get the word (I know I need to check to make sure that it is a variable etc but I just want to get it working for now).
void TextEdit::checkWord()
{
//going to get the previous word and try to do something with it
QTextCursor tc = textCursor();
tc.movePosition(QTextCursor::PreviousWord);
tc.select(QTextCursor::WordUnderCursor);
QString word = tc.selectedText();
//check to see it is in the model
}
But now I want to work out how to check to see if that word is already in the QCompleters model and if it isn't how do I add it?
I have tried the following:
QAbstractItemModel *m = completer->model();
//dont know what to do with it now :(
You can check if word is in your QCompleter really by using
QAbstractItemModel *m = completer->model();
as you can see, method model() returns const pointer.
That is good for checking procedure, you can check like this:
bool matched = false;
QString etalon("second");
QStringListModel *strModel = qobject_cast<QStringListModel*>(completer.model());
if (strModel!=NULL)
foreach (QString str, strModel->stringList()) {
if (str == etalon)
{
matched = true;
break;
}
}
qDebug()<<matched;
But for your purposes, I recommend you to declare QStringListModel, and connect it to your completer, and then, all of operations you'll must do thru your model, according to Qt's principles of MVC programming (http://doc.qt.digia.com/qt/model-view-programming.html).
Your code can be like this:
// declaration
QCompleter completer;
QStringListModel completerModel;
// initialization
completer.setModel(&completerModel);
QStringList stringListForCompleter;
stringListForCompleter << "first" << "second" << "third";
completerModel.setStringList(stringListForCompleter);
// adding new word to your completer list
completerModel.setStringList(completerModel.stringList() << "New Word");
Good luck!

Trouble getting `QWidget* editor` for a `QModelIndex`

I am having trouble augmenting the default edit behavior of QTableView. I want the following behavior when the Enter key is pressed:
Start editing the current cell if it is not already being edited.
If the cell is being edited,
2a. commit the data and close the editor. Then,
2b. make the cell below, if present, the current cell.
2a is the default behavior, and 2b can likely be achieved by using QAbstractItemView::setCurrentIndex() in a re-implementation of QItemDelegate::eventFilter() (as suggested here in a similar context).
The problem is in achieving 1. I list below the approaches I have tried till now.
Reconfigure the "platform edit key" By default, "Editing starts when the platform edit key has been pressed over an item." (QAbstractItemView::EditKeyPressed) This key is F2 on my platform (Ubuntu 12.04). I could reconfigure the platform edit key to Enter but
Altering platform defaults seems like a bad idea.
I could not find out how to do it.
Capture the Enter key press I use QShortCut to do this as follows:
class CourseTable : public QTableView {
/* ... */
};
/* ... */
CourseTable::CourseTable(/* ... */) {
/* ... */
QShortcut* shortcut = new QShortcut(QKeySequence(Qt::Key_Return), this);
connect(shortcut, SIGNAL(activated()), this, SLOT(handleEnter_()));
/* ... */
}
/* ... */
void CourseTable::handleEnter_() {
QModelIndex idx = this->currentIndex();
if (this->state() != QAbstractItemView::EditingState)
this->edit(idx);
/* else // see below */
}
This does capture the Enter key-press and accomplishes 1 (from above) but now 2 is broken. So, I need to look into the else clause in CourseTable::handleEnter_() above, possibly calling QAbstractItemView::commitData() and QAbstractItemView::closeEditor in it. The problem is that both these functions require a QWidget *editor argument which I just cannot figure out how to get. I could subclass QAbstractItemDelegate, add a getEditor() method to the derived class, and modify existing code to pass instances of the derived delegate class to CourseTable::setItemDelegate*() functions. But that sounds like too much work.
So, any ideas how I can cleanly accomplish both 1 and 2 without having to rewrite my code?
Why cant you just filter the event also for starting the edit?
Just handle the event if state is != QAbstractItemView::EditingState
Returning true in this function makes the event stop propagating to the filtered object.
If state is Editing you can just return falseand allow the table and editor continue processing the event.
Something like this:
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == tableView && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Return && tableView->state() != QAbstractItemView::EditingState) {
// set current cell to edit
return true;
} else
return false;
}
return false;
}

Resources