How to highlight the entire row on mouse hover in QTableWidget: Qt5 - c++

I want to highlight the row on mouse hover in my QTableWidget.
When I hover the mouse, only single cell highlighted.
I have tried this approach :
bool MyTabWidget::eventFilter(QObject *target, QEvent *event)
{
if( target == ui->MyTableWidget )
{
//Just to print the event type
qDebug() <<"EventType : "<<event->type();
}
}
Output : EventType : 13.
`(13 = QEvent::Move)`
I have done lost of googling. but not get any proper solution.
Is there any other approach to fulfill my requirment (to highlight entire row on mouse hover)?
Please help. Thank in advance.
EDIT:
Please refer below screen shot for more clear.
This is my QTableWidget
I want to change the background color of that red boarder(edited) row on mouse hover.

Here is my implementation,it works well.First you should subclass QTableView/QTabWidget ,emit a signal to QStyledItemDelegate in mouseMoveEvent/dragMoveEvent function .This signal will send the hovering index.
In QStyledItemDelegate ,use a member variable hover_row_(changed in a slot bind to above signal) to tell paint function which row is be hovered.
Here is the code examaple:
//1: Tableview :
void TableView::mouseMoveEvent(QMouseEvent *event)
{
QModelIndex index = indexAt(event->pos());
emit hoverIndexChanged(index);
...
}
//2.connect signal and slot
connect(this,SIGNAL(hoverIndexChanged(const QModelIndex&)),delegate_,SLOT(onHoverIndexChanged(const QModelIndex&)));
//3.onHoverIndexChanged
void TableViewDelegate::onHoverIndexChanged(const QModelIndex& index)
{
hoverrow_ = index.row();
}
//4.in Delegate paint():
void TableViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
...
if(index.row() == hoverrow_)
{
//HERE IS HOVER COLOR
painter->fillRect(option.rect, kHoverItemBackgroundcColor);
}
else
{
painter->fillRect(option.rect, kItemBackgroundColor);
}
...
}

I had similar task and answer from baixiangcpp helped me, but it worked only when mouse button was pressed, not on simple hovering. I resolved this issue with help from user mrjj on qt forum, who suggested I should set "mouseTracking" property in TableView (CustomView in my case) to "true".
CustomView::CustomView(QWidget *parent) : QTableView(parent)
{
this->setMouseTracking(true);
connect(this,SIGNAL(hoverIndexChanged(const QModelIndex&)),parent,SLOT(onHoverIndexChanged(const QModelIndex&)));
}

It's not the correct way to solve the problem, but if you would like to continue using QTableWidget you can just show the verticalheader and click above them to highlight its specific row.

I'm adding another answer cause it's too long:
Ok, you're right, this is happening on QTableView. Now the question is, why you need a QTableView? If you just need a resume like the one you posted there, you can use a QTreeView, that instead of the QTableView, supports hovering on the entire row, instead of a single cell.
If you absolutely need a QTableView, you need to disable your current hover effect and override the paint and mouseMoveEvent method. On your mouseMoveEvent method calculate the row under the mouse using QTableView::rowAt(y) (also remember to map your mouse coords to the Widget relative coords), and store an index, if it changes from the previous one, invalidate the entire table. On the paint event, just paint a rect around the row manually after calling the base class paint event.

Haven't played with QT5 still, but with QT4 this is super-easy using a style sheet:
QTableView::item:hover {
background-color: rgba(200,200,220,255);
}

Related

Is it possible to add a custom widget into a QListView?

I have a large log data (100, 1000, 100000, ... records) and I want to visualize it in the following manner:
Which widget (e.g. QListView, QListWidget) should I use and how, in order to stay away from performance and memory problems?
Is it possible to add a custom widget into a QListView?
Please, read about:
How to display a scrollable list with a substantial amount of widgets as items in a Qt C++ app?
I want to show every log message in the above format
Solution
To achieve the desired result and stay away from performance issues, even with a very long data log, use a QListView with a custom delegate:
Create a subclass of QStyledItemDelegate, say Delegate
Reimplement the QStyledItemDelegate::paint method to do the custom drawing
Reimplement the QStyledItemDelegate::sizeHint to report the correct size of the items in the list
Use the custom delegate in the view by calling QAbstractItemView::setItemDelegate
Example
I have prepared a working example for you in order to demonstrate how the proposed solution could be implemented and used in an application.
The essential part of the example is the way the delegate paints the items in the list view:
void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyleOptionViewItem opt(option);
initStyleOption(&opt, index);
const QPalette &palette(opt.palette);
const QRect &rect(opt.rect);
const QRect &contentRect(rect.adjusted(m_ptr->margins.left(),
m_ptr->margins.top(),
-m_ptr->margins.right(),
-m_ptr->margins.bottom()));
const bool lastIndex = (index.model()->rowCount() - 1) == index.row();
const bool hasIcon = !opt.icon.isNull();
const int bottomEdge = rect.bottom();
QFont f(opt.font);
f.setPointSize(m_ptr->timestampFontPointSize(opt.font));
painter->save();
painter->setClipping(true);
painter->setClipRect(rect);
painter->setFont(opt.font);
// Draw background
painter->fillRect(rect, opt.state & QStyle::State_Selected ?
palette.highlight().color() :
palette.light().color());
// Draw bottom line
painter->setPen(lastIndex ? palette.dark().color()
: palette.mid().color());
painter->drawLine(lastIndex ? rect.left() : m_ptr->margins.left(),
bottomEdge, rect.right(), bottomEdge);
// Draw message icon
if (hasIcon)
painter->drawPixmap(contentRect.left(), contentRect.top(),
opt.icon.pixmap(m_ptr->iconSize));
// Draw timestamp
QRect timeStampRect(m_ptr->timestampBox(opt, index));
timeStampRect.moveTo(m_ptr->margins.left() + m_ptr->iconSize.width()
+ m_ptr->spacingHorizontal, contentRect.top());
painter->setFont(f);
painter->setPen(palette.text().color());
painter->drawText(timeStampRect, Qt::TextSingleLine,
index.data(Qt::UserRole).toString());
// Draw message text
QRect messageRect(m_ptr->messageBox(opt));
messageRect.moveTo(timeStampRect.left(), timeStampRect.bottom()
+ m_ptr->spacingVertical);
painter->setFont(opt.font);
painter->setPen(palette.windowText().color());
painter->drawText(messageRect, Qt::TextSingleLine, opt.text);
painter->restore();
}
The complete code of the example is available on GitHub.
Result
As written, the given example produces the following result:

Identify which QPixmapItem has been selected

I am adding QGraphicsPixmapItems to my scene, and I can see that when I pick on the item, it gets the white dashed selection rectangle, but I'm struggling to get any data from this selection. Here is how I'm adding it to the scene.
void MainWindow::drawImage(curTarget *newTarget){
QGraphicsPixmapItem *tgt = new QGraphicsPixmapItem;//new pixmap
tgt = scene->addPixmap(newTarget->myIcon);//assign pixmap image
tgt->setFlag(QGraphicsItem::ItemIsSelectable, true);
scene->addItem(tgt);
}
Each PixmapItem that I add to the scene has struct data associated with it, and I need to be able to retrieve that data when I select on the QGraphicsPixmapItem inside of the QGraphicsScene. If the selection rectangle is showing up when the pixmapitem is selected, isn't there some easy way to return information to me based on that fact? A Pointer to what is selected perhaps?
I do have a mousePressEvent method implemented but I'm struggling getting anything relevant with that.
void MainWindow::mousePressEvent(QMouseEvent *event){
qDebug() << "Clicked" << endl;
}
When I run the app, I see Clicked everywhere in my scene except when I click on my pixmapitems.
I've tried every version of the mousePressEvents available and the ones that actually do something, only do something as long as I don't press on my pixmapitems.
Thank to the help I received in my comments, I will post up what ultimately worked for me.
void MainWindow::drawImage(curTarget *newTarget)
{
QGraphicsPixmapItem *tgt = new QGraphicsPixmapItem
tgt = scene->addPixmap(newTarget->myIcon);
tgt->setFlag(QGraphicsItem::ItemIsSelectable, true);
scene->addItem(tgt);
}
With the new function added...
void MainWindow::whatIsSelected(){
QDebug() <<scene->selectedItems() << endl;}
And then I made the connection of the scene to the window elsewhere...
QObject::connect(scene, SIGNAL(selectionChanged()), this, SLOT(whatIsSelected);

Qt: QSS and drawComplexControl()

I have a QDialog containing a QTableView, along with a custom delegate showing a QComboBox for enum types.
When the row is not selected, I still want the QComboBox to be visible (I would like to avoid using QTableView::openPersistentEditor()).
To do so, the custom delegate forwards the paint event to the following method:
QStyleOptionViewItem &option) const
{
painter->save();
QStyleOptionComboBox comboBoxOption;
comboBoxOption.rect = option.rect;
comboBoxOption.state = option.state;
comboBoxOption.state |= QStyle::State_Enabled;
comboBoxOption.editable = false;
comboBoxOption.currentText = enumInfo.valueToKey(curValue);
// The cast is successful, and srcWidget is the QTableView
QWidget *srcWidget = qobject_cast<QWidget *>(option.styleObject);
// style->metaObject()->className() = QStyleSheetStyle
QStyle *style = srcWidget ? srcWidget->style() : QApplication::style();
// However, the QSS is ignored here (while srcWidget->styleSheet() correctly
// returns the style I've set in Qt Designer)
style->drawComplexControl(QStyle::CC_ComboBox, &comboBoxOption, painter, srcWidget);
style->drawControl(QStyle::CE_ComboBoxLabel, &comboBoxOption, painter, srcWidget);
painter->restore();
}
The problem is that I’ve styled the combo box control using QSS, but drawComplexControl() seems to ignore that, despite using the QTableView’s style. Here’s a screenshot:
Is it possible for drawComplexControl() to consider the style sheet?
Thanks
I think that the only way is to grab widget with QPixmap::grabWidget(). And to use this image in delegate.
Seems, that it is not possible to do because of QSS limitation
I believe that you need to use dirty hacks with casting style() to private QStyleSheetStyle

How to put an image and a QProgressBar inside a QTableView?

I'm developing some kind of download manager and display the file name, it's size and the remaining bytes in a QTableView. Now I want to visualize the progress with a QProgressBar and display an image (to indicate whether it's an down- or upload). How can I add or display a QProgressBar and an image inside the QTableView?
If you are using QTableView, I presume you use a model linked to this view.
One solution would be to use delegates (see QItemDelegate) to paint the progress, In QItemDelegate::paint method you have to define, use QStyle of the widget (widget->style()) to paint the progress (use QStyle::drawControl with QStyle::CE_ProgressBarContents as control identifier).
Check the documentation from the example Star Delegate, to see how to define the delegate for the column you need.
Later edit: Example of defining the delegate paint method (code sketch, not really tested, take it as a principle, not fully working).
void MyDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QStyleOptionProgressBar progressStyle;
progressStyle.rect = option.rect; // Maybe some other initialization from option would be needed
// For the sake of the example, I assume that the index indicates the progress, and the next two siblings indicate the min and max of the progress.
QModelIndex minIndex = index.sibling( index.row(), index.column() + 1);
QModelIndex maxIndex = index.sibling( index.row(), index.column() + 2);
progressStyle.minimum = qvariant_cast< int>( minIndex.data( Qt::UserRole));
progressStyle.maximum = qvariant_cast< int>( maxIndex.data( Qt::UserRole));
progressStyle.progress = qvariant_cast< int>( index.data( Qt::UserRole));
progressStyle.textVisible = false;
qApp->style()->drawControl( QStyle::CE_ProgressBarContents, progressStyleOption, painter);
}
TrackDelegate::TrackDelegate(QObject *parent)
: QItemDelegate(parent)
--------------------------------------------------------------------------------
void TrackDelegate::paint( QPainter* painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyleOptionViewItem viewOption(option);
QImage image(m_RowBackGroundImagePath);
QPixmap pixmap(m_RowBackGroundImagePath);
qDebug()<<"forward"<<pixmap.width()<<pixmap.height();
pixmap.scaled(option.rect.width(),option.rect.height());
qDebug()<<"back"<<pixmap.width()<<pixmap.height();
qDebug()<<option.rect.width()<<option.rect.height();
QBrush brush(pixmap);
painter->save();
painter->fillRect(option.rect, brush/*QColor(238, 233, 233, 255)*/);
painter->restore();
viewOption.rect = QRect(option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height());
// viewOption.palette.setColor(QPalette::Text, QColor(Qt::red));
// viewOption.palette.setBrush ( QPalette::ButtonText, brush1);
QItemDelegate::paint(painter, viewOption,index);
int progress = index.model()->data(index,Qt::DisplayRole).toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = QRect(option.rect.x(), option.rect.y()+(SETHEIGHT - PROGRESSBARHEIGHT)/2, option.rect.width(), /*option.rect.height()*/PROGRESSBARHEIGHT);
//qDebug()<<progressBarOption.rect.x()<<progressBarOption.rect.y()<<progressBarOption.rect.height()<<progressBarOption.rect.width();
//qDebug()<<option.rect.x()<<option.rect.y()<<option.rect.height()<<option.rect.width();
progressBarOption.state |= QStyle::State_Enabled;
progressBarOption.direction = QApplication::layoutDirection();
progressBarOption.fontMetrics = QApplication::fontMetrics();
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.textAlignment = Qt::AlignCenter;
progressBarOption.textVisible = true;
progressBarOption.progress = progress < 0 ? 0 : progress;
progressBarOption.text = QString().sprintf("%d%%", progressBarOption.progress);
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
break;
}
You probably want to use QTableWidget for this. It has a method which allows you to add widgets like a QProgressBar. It's the "setCellWidget" method.
There is a slot in QProgressBar called setValue(int),
You can update it sending a signal to this progress bar from Your file manager.
This one should be designed in a way it could check or monitor download state and sends those signals periodically.
Good approach to manage up/down/finish images would be to have additional column in table with the image item.
It would be quite easy to update your image if the socket/connection/file corrupt state would change.
Writing any example would be actually writing you a program, so i suggest to post parts of the problems (if any) with code performed by yourself.
QTableView is not for displaying widgets in a layout. Use QGridLayout or some other suitable layout and put the widgets in that layout.

How can I set the line style of a specific cell in a QTableView?

I am working with a QT GUI. I am implementing a simple hex edit control using a QTableView. My initial idea is to use a table with seventeen columns. Each row of the table will have 16 hex bytes and then an ASCII representation of that data in the seventeenth column. Ideally, I would like to edit/set the style of the seventeenth column to have no lines on the top and bottom of each cell to give the text a free flowing appearance. What is the best way to approach this using the QTableView?
I could think about a couple of ways of doing what you need; both would include drawing custom grid as it looks like there is no straight forward way of hooking into the grid painting routine of QTableView class:
1.Switch off the standard grid for your treeview grid by calling setShowGrid(false) and draw grid lines for cells which need them using item delegate. Below is an example:
// custom item delegate to draw grid lines around cells
class CustomDelegate : public QStyledItemDelegate
{
public:
CustomDelegate(QTableView* tableView);
protected:
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
private:
QPen _gridPen;
};
CustomDelegate::CustomDelegate(QTableView* tableView)
{
// create grid pen
int gridHint = tableView->style()->styleHint(QStyle::SH_Table_GridLineColor, new QStyleOptionViewItemV4());
QColor gridColor = static_cast<QRgb>(gridHint);
_gridPen = QPen(gridColor, 0, tableView->gridStyle());
}
void CustomDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyledItemDelegate::paint(painter, option, index);
QPen oldPen = painter->pen();
painter->setPen(_gridPen);
// paint vertical lines
painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
// paint horizontal lines
if (index.column()!=1) //<-- check if column need horizontal grid lines
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
painter->setPen(oldPen);
}
// set up for your tree view:
ui->tableView->setShowGrid(false);
ui->tableView->setItemDelegate(new CustomDelegate(ui->tableView));
2.Create a QTableView descendant and override the paintEvent method. There you could either draw your own grid or let base class to draw it and then paint horizontal lines on top of the grid with using tableview's background color.
hope this helps, regards