PDA

View Full Version : On mouse click changing selection's background color in tableview



DoTheEvo
23rd January 2016, 11:44
Program is for linux distributions.
I am trying to notify user when something is wrong with the item in the list when it is double clicked.
I used setStyleSheet, only to find out that it does not work on unity and cinnamon, while it works on KDE and i3.

I am not all that savvy with qbrushes or some other stuff I am googling out.
So whats the best way to solve this, so that it works everywhere? Thanks.

Heres a gif of my solution in action on i3wm.

11658



from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys


class My_Model_table(QAbstractTableModel):
def __init__(self, table_data=[], parent=None):
super().__init__()
self.table_data = table_data

def rowCount(self, parent):
return len(self.table_data)

def columnCount(self, parent):
return 1

def data(self, index, role):
if role == Qt.DisplayRole:
value = self.table_data[index.row()]
return value
if role == Qt.TextAlignmentRole:
return Qt.AlignCenter


class My_table(QTableView):
def __init__(self, parent=None):
super().__init__()
self.activated.connect(self.double_click_enter)

def double_click_enter(self, QModelIndex):
row = QModelIndex.row()

self.setStyleSheet('selection-background-color:red;')

self.alarm = QTimer()
self.alarm.timeout.connect(self.row_color_back)
self.alarm.setSingleShot(True)
self.alarm.start(200)
return

def row_color_back(self):
self.setStyleSheet('')


if __name__ == '__main__':
app = QApplication(sys.argv)
data = ['1', '2', '3', '4', '5']
main_table = My_table()
main_table.setModel(My_Model_table(data))

main_table.show()
sys.exit(app.exec_())

d_stranz
24th January 2016, 01:47
I'm not that good at Python, but hopefully you can get my meaning.

At some point, you will need to add logic to your model that contains the information about whether a table entry is valid or not, right?

So, add a case to your model's data() method to handle the Qt::BackgroundRole role. In this role, for an entry in error you will return a QBrush with the error color, otherwise you do nothing and the superclass data() method will be invoked to return the default color.

If you want the color to blink in response to your double-click, then your timer approach is on the right track. In this case, you must also implement the setData() method for your model, and within that, implement a handler for the Qt::BackgroundRole case. In that handler, you will store the QBrush and model index that are passed in. You'll need new member variables somewhere in your model. Since this is a transient thing, you don't need to add storage for every model index, just the row / column of the item with the error that has been clicked.

In the double-click handler, when you determine that the item has an error, you call setData() with the index, Qt::BackgroundRole, and QBrush for the error color. Start your timer. When the double-click handler exits, the model will tell the view to update the background color for the changed index (it will send the "dataChanged()" signal, which the table listens for).

When your timer times out, in the timeout slot call setData() again with the same index and role, but with the default background color. This will again cause the table to update the cell.

There may be other ways to do this, but this way uses the machinery already in place in the model / view architecture to change the background color.

DoTheEvo
25th January 2016, 11:33
THANKS for replying.

But theres some initial problems with this approach.

Playing here with backgroundRole and the brush, if I use this code to color every other row green. Well the problem is that it is the background color of the cell
not the color of the selection indicator. So I am assuming it might not even be visible if I managed to call from view a change in model with custom setData() method.


elif role == Qt.BackgroundRole:
if index.row() % 2 == 0:
return QBrush(Qt.green)

But will play with it more I guess and try to make it work, I only need to indicate to user that shit went wrong on double click(text in status bar is already telling that but I feel some color emphasis would be nice), so flashing background of all rows red for 0.2sec would serve well as notice of this, even if bit less elegant than just that row...

http://i.imgur.com/3t0Gj8l.gif

d_stranz
25th January 2016, 22:39
What I suggested is that you use the setData() method as a way to accomplish flashing the color:

1 - When the user double-clicks, use a setData() call to send the index of the offending cell into the model. You have to implement setData() and in the BackgroundRole handler, you use the information in the call to set member variables that "remember" the index (or row / column) as being the one with the problem.
2 - The setData call will cause the view to update. In your data() method, you compare the index passed in with the index you "remembered", and if there's a match, you return a red brush. Otherwise, you return whatever brush you want to use for that index under normal conditions.
3 - In your double-click handler, you also start a timer, as you showed in your example.
4 - When the timer times out, the slot should call setData() again, this time with an invalid index for the background role. This resets the index you "remembered".
5 - When the view again updates, none of the indexes will match the invalid one, so the indexes get painted with their normal background colors.

Thus, the only index that turns red is the one that was double-clicked, and only during the period between the end of the double-click and the timeout. The view takes care of updating itself.

DoTheEvo
15th February 2016, 17:38
Finally got some free time and taste for looking in to this again.

As I said before, returning of the QBrush in data() only changes background color, while the color of the selected row stays the same as the "selection-background-color"
so going for some passing of index and trying to find which row to have color change, well that seem waste of time (apart from learning experience) since we would never see the color change, it would be covered by the defauly highlight color

http://i.imgur.com/DkUcHGd.gif


Above is gif of changing qbrush color for everything. I got problem getting the default color of the background of the table... but solved it by just not applying any brush when I am not setting it
Anyway, this code works on ubuntu and it does give me some control over colors, but not over selection/highlight color.
It looks worse, less elegant than the stylesheet way that can be viewed in OP post in this thread, but still better than nothing, and this makes me appreciate how awesome stylesheets are.



from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys


class My_Model_table(QAbstractTableModel):
def __init__(self, table_data=[], parent=None):
super().__init__()
self.table_data = table_data
self.color_enabled = False
self.color_back = Qt.magenta # just something there

def rowCount(self, parent):
return len(self.table_data)

def columnCount(self, parent):
return 1

def data(self, index, role):
if role == Qt.DisplayRole:
value = self.table_data[index.row()]
return value
if role == Qt.TextAlignmentRole:
return Qt.AlignCenter

if role == Qt.BackgroundRole and self.color_enabled:
return QBrush(self.color_back)

def change_color(self, qt_color, color_enabled):
self.layoutAboutToBeChanged.emit()
self.color_enabled = color_enabled
self.color_back = qt_color
self.layoutChanged.emit()


class My_table(QTableView):
def __init__(self, parent=None):
super().__init__()
self.activated.connect(self.double_click_enter)

def double_click_enter(self, QModelIndex):
QModelIndex.model().change_color(Qt.red, True)

self.alarm = QTimer()
self.alarm.setSingleShot(True)
self.alarm.timeout.connect(self.color_timeout)
self.alarm.start(200)

def color_timeout(self):
self.model().change_color(Qt.magenta, False)


if __name__ == '__main__':
app = QApplication(sys.argv)
data = ['1', '2', '3', '4', '5']
main_table = My_table()
main_table.setModel(My_Model_table(data))

main_table.show()
sys.exit(app.exec_())

DoTheEvo
15th February 2016, 21:30
OK, final edit since I figured it out when I started to play with qdarkstylesheet and noticed that everything worked nicely even on ubuntu...
my mistake in original code was just using selection-background-color, when I use QTableView::item:selected:active everything now works perfectly

http://i.imgur.com/mMJbROZ.gif



from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys


class My_Model_table(QAbstractTableModel):
def __init__(self, table_data=[], parent=None):
super().__init__()
self.table_data = table_data

def rowCount(self, parent):
return len(self.table_data)

def columnCount(self, parent):
return 1

def data(self, index, role):
if role == Qt.DisplayRole:
value = self.table_data[index.row()]
return value
if role == Qt.TextAlignmentRole:
return Qt.AlignCenter


class My_table(QTableView):
def __init__(self, parent=None):
super().__init__()
self.activated.connect(self.double_click_enter)

def double_click_enter(self, QModelIndex):
row = QModelIndex.row()

self.setStyleSheet(
'''
QTableView::item:selected:active {
background: #ff0000;
}
'''
)

self.alarm = QTimer()
self.alarm.timeout.connect(self.row_color_back)
self.alarm.setSingleShot(True)
self.alarm.start(200)
return

def row_color_back(self):
self.setStyleSheet('')


if __name__ == '__main__':
app = QApplication(sys.argv)
data = ['1', '2', '3', '4', '5']
main_table = My_table()
main_table.setModel(My_Model_table(data))

main_table.show()
sys.exit(app.exec_())


/EDIT

:mad::mad::mad:

damn, and it does not work in my actual code because I use delegate to get html rich text to get bold emphasize
oh how I hate it, I undertand very little and its always huge pain in the ass in getting it to done things without screwing something else
heres (http://pastebin.com/VUG7zY9z) my delegate if someone is guru with them