Synchronize QTableWidget cell with widget in the center of the cell - qt

I use QCheckBox in QTableWidgetCell
QWidget *widget = new QWidget();
QCheckBox *checkBox = new QCheckBox();
QHBoxLayout *layout = new QHBoxLayout(widget);
layout->addWidget(checkBox);
layout->setAlignment(Qt::AlignCenter);
layout->setContentsMargins(0, 0, 0, 0);
widget->setLayout(layout);
table->setCellWidget(0, 0, widget);
The result of this code is a cell with checkbox in the center.
I need to make checkbox reaction to mouse moving and clicking in the empty area like when cursor is under checkbox.

If you dont want the fullblown functionality of QCheckBox but just the checkmark, You can use a simple QTableWidgetItem and modify his checkstate.
QTableWidgetItem* item = new QTableWidgetItem();
item->setCheckState(Qt::Unchecked);
table->setItem ( 0, 0, item );
connect(table, SIGNAL(cellClicked(int , int )), this, SLOT(OnClicked( int, int)));
void OnClicked( int row, int column )
{
if(row != checkablelerow && col != checkablelecol )
return; // or do something else
item = table->item(row, column);
item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
}
Now the whole cell will be checkable. Code may need refinement.

You can override eventFilter method to highlight your checkbox when cursor enters the cell. http://qt-project.org/doc/qt-4.8/qobject.html#eventFilter
bool ExampleDialog::eventFilter(QObject *obj, QEvent *event)
{
if (qobject_cast<QTableWidgetItem*>(object)) {
if (event->type() == QEvent::HoverEnter) {
QCheckBox* qcb = object->findChild<QCheckBox*>();
//here you can do something to highlight your checkbox
return true;
}
else if(event->type() == QEvent::HoverLeave){
QCheckBox* qcb = object->findChild<QCheckBox*>();
//here you have to stop highlighting checkbox
return true;
}
else {
return false;
}
}
}
After overriding, you have to install event filter on your widgets like this
widget->installEventFilter(this); //this points to parent of the widget, the exampleDialog
And use a clicked slot of QTableWidgetItem* to set your checkbox checked.
void QTableWidgetItemClicked( int row, int column )
{
QCheckBox* qcb = QObject::sender()->findChild<QCheckBox*>();
qcb->setChecked(!qcb->isChecked());
}
Remember to connect QTableWidgetItems signals to slots.

Related

Getting QTableWidgetItem out of cellWidget()'s QCheckBox

I'm storing QCheckBox in QTableWidget, in following way:
QCheckBox *checkBox = new QCheckBox();
QWidget *widget = new QWidget();
QHBoxLayout *layout = new QHBoxLayout(widget);
layout->addWidget(checkBox);
layout->setAlignment(Qt::AlignCenter);
layout->setContentsMargins(0,0,0,0);
widget->setLayout(layout);
tableWidget->setCellWidget(row, 2, widget);
Then, I catch stateChanged() of the checkBox:
connect( checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxStateChanged(int)) );
void MainWindow::checkBoxStateChanged(int)
{
QCheckBox * box = qobject_cast< QCheckBox * >( sender() );
if( !box ) {
return;
}
}
Now, I can get to QTableWidget – it is box->parent()->parent()->parent(). Object before that, i.e. box->parent()->parent(), is qt_scrollarea_viewport (that's objectName()). I've searched children of the "viewport", and there's 16 QWidgets – the number of rows in my table. However, their children are only QHBoxLayout and QCheckBox. There apparently is no reference to QTableWidgetItem – it looks like if I were in some parallel object hierarchy, and QTableWidgetItem is in other hierarchy. Is that true? How to get the item?
See this question: How to work with signals from QTableWidget cell with cellWidget set
Adapted to you case:
void MainWindow::checkBoxStateChanged(int)
{
QCheckBox * box = qobject_cast< QCheckBox * >( sender() );
if (box)
{
int row = box->property("row").toInt();
int column = box->property("column").toInt();
QTableWidgetItem* item = tableWidget->item(row, column);
}
}

How do I implement the ability to edit a QTableWidget's vertical and horizontal header text in-line from the GUI instead of programmatically?

I have a QTableWidget that is displayed in the user interface that I can add and remove rows and columns using buttons. The problem is, when I add a row or column, I can change the data in the actual cells, but I cannot name the row or column. The name is simply a static number.
Is there a way to allow the user of my program to perhaps double-click on a row/column header and edit the name in-line or something similar?
Thanks.
As far as I know there is no built-in way to do this. However this can be implemented manually. The main idea of the following code is to detect double clicks on header items, place QLineEdit over them and save edited text once it loses focus. The example is based on Qt generated Designer Form Class with a table named ui->tableWidget which can be either QTableWidget or QTableView.
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QLineEdit* header_editor;
int editor_index;
bool eventFilter(QObject*, QEvent*);
};
Source:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
header_editor = 0;
ui->setupUi(this);
ui->tableWidget->horizontalHeader()->viewport()->installEventFilter(this);
ui->tableWidget->verticalHeader()->viewport()->installEventFilter(this);
}
MainWindow::~MainWindow() {
delete ui;
}
bool MainWindow::eventFilter(QObject* object, QEvent* event) {
if ((object == ui->tableWidget->horizontalHeader()->viewport() ||
object == ui->tableWidget->verticalHeader()->viewport()) &&
event->type() == QEvent::MouseButtonDblClick) {
if (header_editor) { //delete previous editor just in case
header_editor->deleteLater();
header_editor = 0;
}
QMouseEvent* e = static_cast<QMouseEvent*>(event);
QHeaderView* header = static_cast<QHeaderView*>(object->parent());
int mouse_pos = header->orientation() == Qt::Horizontal ? e->x() : e->y();
int logical_index = header->logicalIndex(header->visualIndexAt(mouse_pos));
if (logical_index >= 0) { // if mouse is over an item
QRect rect; // line edit rect in header's viewport's coordinates
if (header->orientation() == Qt::Horizontal) {
rect.setLeft(header->sectionPosition(logical_index));
rect.setWidth(header->sectionSize(logical_index));
rect.setTop(0);
rect.setHeight(header->height());
} else {
rect.setTop(header->sectionPosition(logical_index));
rect.setHeight(header->sectionSize(logical_index));
rect.setLeft(0);
rect.setWidth(header->width());
}
rect.adjust(1, 1, -1, -1);
header_editor = new QLineEdit(header->viewport());
header_editor->move(rect.topLeft());
header_editor->resize(rect.size());
header_editor->setFrame(false);
//get current item text
QString text = header->model()->
headerData(logical_index, header->orientation()).toString();
header_editor->setText(text);
header_editor->setFocus();
editor_index = logical_index; //save for future use
header_editor->installEventFilter(this); //catch focus out event
//if user presses Enter it should close editor
connect(header_editor, SIGNAL(returnPressed()),
ui->tableWidget, SLOT(setFocus()));
header_editor->show();
}
return true; // filter out event
} else if (object == header_editor && event->type() == QEvent::FocusOut) {
QHeaderView* header = static_cast<QHeaderView*>(
header_editor->parentWidget()->parentWidget());
//save item text
header->model()->setHeaderData(editor_index, header->orientation(),
header_editor->text());
header_editor->deleteLater(); //safely delete editor
header_editor = 0;
}
return false;
}
Downsides of this method are that it's hacky, things go bad when headers are resized or table is scrolled. It's just an example that can be improved.
I have a feeling there has to be a simpler way. But Qt headers ignore Qt::ItemIsEditable flag and can't use delegates.

QListView and delegate display unintended item

I've a problem with my QListView, it paint an unintended item on the top left of the QListView :
http://s4.postimage.org/64orbk5kd/Screen_Shot_2013_02_14_at_20_23_14.png
I use a QStyledItemDelegate in my QListView :
m_stringList.push_back("FIRST");
m_stringList.push_back("SECOND");
m_stringList.push_back("THIRD");
m_model.setStringList(m_stringList);
ui->processesListView->setFlow(QListView::LeftToRight);
ui->processesListView->setModel(&m_model);
ui->processesListView->setItemDelegate(new ProcessItemDelegate(this, ui->processesListView));
The delegate (ProcessItemDelegate) paint method use a custom QWidget to display the information :
void ProcessItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex &inIndex ) const
{
_listItem->setContent(_listView->model()->data(inIndex).toString());
painter->save();
painter->translate(option.rect.center());
_listItem->render(painter);
painter->restore();
}
The setContent method of the QWidget is very simple :
void ProcessItem::setContent(const QString &s)
{
ui->processId->setText(s);
}
I have another way to add a widget to some list using a QListWidget.
For example knowing that ui->historyView is a QListWidget element and HistoryElementView a subclass of QWidget.
void View::onHistoryChanged(const QList<HistoryElement> &history)
{
clearHistory();
foreach(HistoryElement elt, history)
{
HistoryElementView *historyViewElement = new HistoryElementView(elt.getDateTime("dd/MM/yyyy - hh:mm"), elt.getFilename());
QListWidgetItem *item = new QListWidgetItem();
ui->historyView->addItem(item);
ui->historyView->setItemWidget(item, historyViewElement);
}
}
void View::clearHistory()
{
QListWidgetItem *item;
while (ui->historyView->count() != 0)
{
item = ui->historyView->takeItem(0);
delete item;
}
}
You do not need to delete the widgets inside your QListWidgetItem, it will be handle by Qt.
Once your widgets are inside the list, you can retrieve them using :
// Using index
QListWidgetItem *item = ui->historyView->item(0);
HistoryElementView *elt = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
// Using position
QListWidgetItem *item = ui->historyView->itemAt(pos);
HistoryElementView *historyElement = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
Hope it helps.

Position of icon in QTreeWidgetItem

My QTreeWidget has a single column. Its items have a check box, an icon, and text. If the user clicks inside an item, I want to know whether the icon was clicked. How can I find the position and size of the icon in a QTreeWidgetItem?
Updated to add: Here is the code for my eventual solution, as requested by webclectic.
First, I sub-classed QItemDelegate so that I could access the coordinates of each part of a QTreeWidgetItem (check box, icon, and text). Here is the header file:
#include <QItemDelegate>
class MyItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit MyItemDelegate (MyTreeWidget *parent)
: QItemDelegate (parent), ParentView (parent) { }
~MyItemDelegate() { }
void GetRects (const QModelIndex &index, QRect& CheckBox, QRect& Icon, QRect& Text) const ;
private:
MyTreeWidget* ParentView ;
} ;
And here is the source file:
void MyItemDelegate::GetRects (const QModelIndex &index, QRect& CheckBox, QRect& Icon, QRect& Text) const
{
QStyleOptionViewItem option = ParentView -> viewOptions() ;
CheckBox = rect (option, index, Qt::CheckStateRole) ;
Icon = rect (option, index, Qt::DecorationRole) ;
Text = rect (option, index, Qt::DisplayRole) ;
doLayout (option, &CheckBox, &Icon, &Text, true) ;
QRect VisualRect = ParentView -> visualRect (index) ;
CheckBox.translate (VisualRect.topLeft()) ;
Icon.translate (VisualRect.topLeft()) ;
Text.translate (VisualRect.topLeft()) ;
}
Then I added a MyItemDelegate* member to MyTreeWidget, and set it as the item view's delegate. In the header:
class MyTreeWidget : public QTreeWidget
{
...
MyItemDelegate* Delegate ;
...
} ;
In the source :
MyTreeWidget::MyTreeWidget (QObject* parent)
{
...
Delegate = new MyItemDelegate (this) ;
setItemDelegate (ItemDelegate) ;
}
Now, to get the coordinates of each part of a QTreeWidgetItem:
QTreeWidgetItem* item ;
...
QModelIndex ModelIndex = indexFromItem (item) ;
QRect CheckBoxRect, IconRect, TextRect ;
ItemDelegate -> GetRects (ModelIndex, &CheckBoxRect, &IconRect, &TextRect) ;
Unfortunately there is no simple way to achieve what you want. The problem is that QTreeWidget is responsible for painting its items so the item itself has no information about the position of its elements in the view.
First of all you have to subclass QTreeWidget and reimplement the mousePressEvent (or mouseReleaseEvent if you prefer). Inside the event you should calculate the position of the icon and handle it correspondingly.
Sample code (but untested) follows:
void mousePressEvent(QMouseEvent *event)
{
QModelIndex clickedIndex = indexAt(event->pos());
// make sure the event was on a valid item
if (clickedIndex.isValid() == false)
return;
// Get the tree widget's x position
int treeX = header()->sectionViewportPosition(0);
// Get the x coordinate of the root item. It is required in order to calculate
// the identation of the item
int rootX = visualRect(rootIndex()).x();
// Get the rectangle of the viewport occupied by the pressed item
QRect vrect = visualRect(clickedIndex);
// Now we can easily calculate the x coordinate of the item
int itemX = treeX + vrect.x() - rootX;
// The item is a checkbox, then an icon and finally the text.
// 1. Get the rect surrounding the checkbox
QRect checkboxRect = QRect(itemX,
vrect.y(),
style()->pixelMetric(QStyle::PM_IndicatorWidth),
vrect.height());
// 2. Get the rect surrounding the icon
QRect iconRect = QRect(itemX + checkboxRect.width(),
vrect.y(),
iconSize().width(),
vrect.height());
// 3. Finally get the rect surrounding the text
QRect textRect = QRect(itemX + checkboxRect.width() + iconRect.width(),
vrect.y(),
vrect.width() - checkboxRect.width() - iconRect.width(),
vrect.height());
// Now check where the press event took place and handle it correspondingly
if(checkboxRect.contains(event->pos()))
{
qDebug() << "Checkbox pressed";
QTreeWidget::mousePressEvent(event);
return;
}
else if (iconRect.contains(event->pos()))
{
qDebug() << "Icon pressed";
QTreeWidget::mousePressEvent(event);
return;
}
else
{
qDebug() << "Text pressed";
QTreeWidget::mousePressEvent(event);
return;
}
}
I repeat that the code is untested but you get the idea about how to achieve what you want.

QTreeview mousePressEvent implementation prevents selection of items

Hello all
I have class that inherited from Qtreeview and I implement simple ( empty ) mousePressEvent function
But whenever I try to do this , the selection of the items in the Qtreeview are disabled , when I remove this function everything is working fine
What im missing here ?
Here Is the code:
void MyTreeWidget::mousePressEvent(QMouseEvent *event)
{
QModelIndex index = this->indexAt(event->pos());
QAbstractItemModel *model = this->model();
QMap<int, QVariant> ItemData = model->itemData(index);
QMap<int, QVariant>::const_iterator i = ItemData.constBegin();
while (i != ItemData.constEnd()) {
QString k = QString::number(i.key());
QString v = i.value().toString();
++i;
}
if (event->button() == Qt::LeftButton) {
QByteArray itemData ;
QString urlTo;
itemData.append(urlTo);
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-dnditemdata", itemData);
drag->setMimeData(mimeData);
Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
if (dropAction == Qt::MoveAction)
{
UT::getInstance()->LogToFile("dropAction");
}
}
QTreeView::mousePressEvent(event);
}
It's because that when you override a method, the original on is not called anymore. You would have to manually call the mousePressEvent method of QTreeView in the method you created.
Here is how to do it:
void YourClass::mousePressEvent ( QMouseEvent * event )
{
QTreeView::mousePressEvent(event);
}
Hope this helps.

Resources