Results 1 to 6 of 6

Thread: Custom QItemDelegate: mouse hover, margins, colors?

  1. #1
    Join Date
    Apr 2009
    Posts
    7
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Platforms
    Unix/X11 Windows

    Default Custom QItemDelegate: mouse hover, margins, colors?

    Hello,

    I am playing around with a custom delegate for a QTableWidget, which just shows a combobox as an editor, stores the value as a number (which is a foreign key to a list), and displays the text corresponding to the item at that value's position in a list. For example: it stores 1 and displays 'one' if the list is ['someitem', 'one'], and stores 4 and displays 'cow' if the list is ['bird', 'horse', 'pig', 'snake', 'cow'].

    My problem is I cannot get the appearance of the cells which use the custom delegate 'right' - which in this case would be the same as the default delegate. In particular:
    • Hovering normal cells with the mouse produces a shadow over the hovered cell. How can I reproduce this effect in my custom delegate? I have tried to find a custom delegate with a working "hover" effect in the examples, without luck. Both the star delegate and the spin box delegate examples have the same problem in the columns affected.
    • My delegate's painter does not draw using the same cell margins as the default delegate's painter. How should I set these margins in my custom delegate?
    • Which is the correct way of looking up colors and effects to be used by the custom delegate's paint method so that it mimics the default delegate's style? In particular, the selected item under the default delegate has a sublte "glow" texture in its background,which is not reproduced by my delegate's paint method, which simply uses a solid color as background. I couldn't find this effect in the examples, either, they too seem to use a solid color as background when using custom delegates. (Edited to clarify)

    Thanks!
    Qt Code:
    1. #! /usr/bin/env python
    2. # -*- coding: utf-8 -*-
    3. import sys
    4. from PyQt4 import QtCore
    5. from PyQt4 import QtGui
    6.  
    7. class QRelationalDelegate(QtGui.QStyledItemDelegate):
    8. VALUES = ['zero', 'one', 'two', 'three', 'four']
    9.  
    10. def paint(self, painter, option, index):
    11. value = index.data(QtCore.Qt.DisplayRole).toInt()[0] # integer stored in tablewidget model
    12. text = self.VALUES[value] # text to be displayed
    13. painter.save()
    14. if option.state & QtGui.QStyle.State_Selected: # highligh background if selected
    15. painter.fillRect(option.rect, option.palette.highlight())
    16. painter.drawText(option.rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, text)
    17. painter.restore()
    18.  
    19. def createEditor(self, parent, option, index):
    20. combobox = QtGui.QComboBox(parent)
    21. combobox.addItems(self.VALUES)
    22. combobox.setEditable(True)
    23. return combobox
    24.  
    25. def setEditorData(self, editor, index):
    26. text = self.VALUES[index.data(QtCore.Qt.DisplayRole).toInt()[0]]
    27. pos = editor.findText(text)
    28. if pos == -1: #text not found, set cell value to first item in VALUES
    29. pos = 0
    30. editor.setCurrentIndex(pos)
    31.  
    32. def setModelData(self, editor, model, index):
    33. model.setData(index, QtCore.QVariant(editor.currentIndex()))
    34.  
    35.  
    36. class myWindow(QtGui.QDialog):
    37. def __init__(self):
    38. QtGui.QDialog.__init__(self)
    39. DATA = [['First row', 1, 1], ['Second Row', 2, 2]]
    40. self.table = QtGui.QTableWidget(self)
    41. self.table.setSortingEnabled(False)
    42. self.table.setRowCount(len(DATA))
    43. self.table.setColumnCount(len(DATA[0]))
    44. self.table.setItemDelegateForColumn(1, QRelationalDelegate(self))
    45. for row in range(len(DATA)):
    46. for col in range(len(DATA[row])):
    47. item = QtGui.QTableWidgetItem(str(DATA[row][col]))
    48. self.table.setItem(row, col, item)
    49. layout = QtGui.QVBoxLayout()
    50. layout.addWidget(self.table)
    51. self.setLayout(layout)
    52.  
    53.  
    54. if __name__ == "__main__":
    55. app = QtGui.QApplication(sys.argv)
    56. window = myWindow()
    57. window.show()
    58. app.exec_()
    To copy to clipboard, switch view to plain text mode 
    Attached is a picture the hover effect which works for columns 0 and 2 in my example but not for column 1 which uses the custom delegate.default_hover_ef&#1.png
    Last edited by fede; 26th May 2010 at 02:32. Reason: removed useless debug leftovers from code, again

  2. The following user says thank you to fede for this useful post:

    sami1592 (2nd March 2017)

  3. #2
    Join Date
    May 2010
    Posts
    4
    Thanked 2 Times in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Custom QItemDelegate: mouse hover, margins, colors?

    Hi!

    I had the same question but I think I finally found a nice solution and thought to share it.

    I can't answer all your questions, but as I understand it you were looking for a way to display text in a cell with a custom delegate of a tableview. Furthermore this cell should look in case of selection and mouse hovering exactly like the cells which are rendered by the default delegate.

    The following should work:
    Qt Code:
    1. class myDelegate(QStyledItemDelegate):
    2. def __init__(self, parent = None):
    3. super(myDelegate, self).__init__(parent)
    4. def paint(self, painter, option, index):
    5. styleOption = QStyleOptionViewItemV4(option)
    6.  
    7. # define the text to be shown
    8. styleOption.text = "mytext"
    9. # paint the cell
    10. self.parent().style().drawControl(QStyle.CE_ItemViewItem, styleOption, painter)
    To copy to clipboard, switch view to plain text mode 
    Now hovering and selecting looks like it should. For self.parent().style() to work I made the tableview parent of the delegate. You could also use QApplication.style() or any other Style you like.

    It is also possible to let Qt render the item with drawControl first. Afterwards, you can use the painter to paint sth over the nicely rendered background.

    However, I havn't figured out how to determine the colors/painting effects (esp. the hovering color) and the margins.

    Have fun!

  4. The following 2 users say thank you to yetien for this useful post:

    fede (31st May 2010), sami1592 (2nd March 2017)

  5. #3
    Join Date
    Apr 2009
    Posts
    7
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Platforms
    Unix/X11 Windows

    Default Re: Custom QItemDelegate: mouse hover, margins, colors?

    Thanks a lot, that is exactly what I needed!

    I have no need to determine myself the colors and painting effects for the time being, your suggestion works perfectly.

    self.parent().style() seems to work fine without making the tableview parent of the delegate, in my example self.parent().style().drawControl(...) is calling the main window's qstyle control (myWindow.style().drawControl(...)). Is there a reason for your suggestion to make the tableview parent of the delegate that I'm missing?

    It is curious that the working examples in Qt (and in the two books I have on Qt) do not resort to this method and fail to reproduce the default style's behaviour. Which makes your reply awesome. Thanks a lot, again!

    Here is the working code using your suggestion, which I post for reference: an example of a tablewidget with a custom delegate which is a simple combobox

    Qt Code:
    1. #! /usr/bin/env python
    2. # -*- coding: utf-8 -*-
    3. import sys
    4. from PyQt4 import QtCore
    5. from PyQt4 import QtGui
    6.  
    7. class QRelationalDelegate(QtGui.QStyledItemDelegate):
    8. VALUES = ['zero', 'one', 'two', 'three', 'four']
    9.  
    10. def __init__(self, parent = None):
    11. super(QRelationalDelegate, self).__init__(parent)
    12.  
    13. def paint(self, painter, option, index):
    14. styleOption = QtGui.QStyleOptionViewItemV4(option)
    15. # define the text to be shown
    16. value = index.data(QtCore.Qt.DisplayRole).toInt()[0] # integer stored in tablewidget model
    17. styleOption.text = self.VALUES[value]
    18. # paint the cell
    19. self.parent().style().drawControl(QtGui.QStyle.CE_ItemViewItem, styleOption, painter)
    20.  
    21. def createEditor(self, parent, option, index):
    22. combobox = QtGui.QComboBox(parent)
    23. combobox.addItems(self.VALUES)
    24. combobox.setEditable(True)
    25. return combobox
    26.  
    27. def setEditorData(self, editor, index):
    28. text = self.VALUES[index.data(QtCore.Qt.DisplayRole).toInt()[0]]
    29. pos = editor.findText(text)
    30. if pos == -1: #text not found, set cell value to first item in VALUES
    31. pos = 0
    32. editor.setCurrentIndex(pos)
    33.  
    34. def setModelData(self, editor, model, index):
    35. model.setData(index, QtCore.QVariant(editor.currentIndex()))
    36.  
    37.  
    38. class myWindow(QtGui.QDialog):
    39. def __init__(self):
    40. QtGui.QDialog.__init__(self)
    41. DATA = [['First row', 1, 1], ['Second Row', 2, 2]]
    42. self.table = QtGui.QTableWidget(self)
    43. self.table.setSortingEnabled(False)
    44. self.table.setRowCount(len(DATA))
    45. self.table.setColumnCount(len(DATA[0]))
    46. self.table.setItemDelegateForColumn(1, QRelationalDelegate(self))
    47. for row in range(len(DATA)):
    48. for col in range(len(DATA[row])):
    49. item = QtGui.QTableWidgetItem(str(DATA[row][col]))
    50. self.table.setItem(row, col, item)
    51. layout = QtGui.QVBoxLayout()
    52. layout.addWidget(self.table)
    53. self.setLayout(layout)
    54.  
    55.  
    56. if __name__ == "__main__":
    57. app = QtGui.QApplication(sys.argv)
    58. window = myWindow()
    59. window.show()
    60. app.exec_()
    To copy to clipboard, switch view to plain text mode 

  6. #4
    Join Date
    May 2010
    Posts
    4
    Thanked 2 Times in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Custom QItemDelegate: mouse hover, margins, colors?

    Fine that I could help you. :-)

    self.parent().style() seems to work fine without making the tableview parent of the delegate, in my example self.parent().style().drawControl(...) is calling the main window's qstyle control (myWindow.style().drawControl(...)). Is there a reason for your suggestion to make the tableview parent of the delegate that I'm missing?
    I thought if the tableview has a different stylesheet than the main application (for whatever reason) it would be a little more clean to refer to the "closest" style; no other thoughts beyond that.

  7. #5
    Join Date
    Oct 2010
    Posts
    1
    Qt products
    Platforms
    Unix/X11

    Cool Re: Custom QItemDelegate: mouse hover, margins, colors?

    Just what I needed. Although a programmer of 20 years, I struggle very hard with the PyQt stuff. Models are difficult! Anyway many thanks for this wonderful example.

    HW

  8. #6
    Join Date
    Apr 2009
    Posts
    7
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Platforms
    Unix/X11 Windows

    Default Re: Custom QItemDelegate: mouse hover, margins, colors?

    You're welcome, I'm glad it's useful. I am myself struggling with the Qt models and I do agree they are difficult.

    I also wanted to post an update: a slight change to the code above so that the delegate will work with (key, display text) pairs, where key is a meaningful value stored by the model (in my actual program from which this example is taken it is a column in an sql table which is the foreign key to the table where the display text is stored). In any case, the code above just stores the combobox's index values in the model, whereas the code below is slightly more sophisticated and allows for storing arbitrary values in the model, using the UserRole property of the combobox. Hope it's useful to someone out there!

    Qt Code:
    1. #! /usr/bin/env python
    2. # -*- coding: utf-8 -*-
    3. import sys
    4. from PyQt4 import QtCore
    5. from PyQt4 import QtGui
    6.  
    7. class MyRelationalDelegate(QtGui.QStyledItemDelegate):
    8. def __init__(self, parent=None):
    9. self.choices = {1: 'One', 2: 'Two', 3:'Three', 7: 'Seven'}
    10. super(MyRelationalDelegate, self).__init__(parent)
    11.  
    12. def paint(self, painter, option, index):
    13. styleOption = QtGui.QStyleOptionViewItemV4(option)
    14. # define the text to be shown
    15. text = self.choices[index.data(QtCore.Qt.DisplayRole).toInt()[0]] # value in choices dict corresponding to integer stored in tablewidget model
    16. styleOption.text = text
    17. # paint the cell
    18. self.parent().style().drawControl(QtGui.QStyle.CE_ItemViewItem, styleOption, painter)
    19.  
    20. def createEditor(self, parent, option, index):
    21. combobox = QtGui.QComboBox(parent)
    22. combobox.setEditable(True)
    23. for c in sorted(self.choices.items()):
    24. combobox.addItem(c[1], c[0]) # Add items to combobox, storing display text in DisplayRole and underlying value in UserRole
    25. return combobox
    26.  
    27. def select_line_editor(self):
    28. self.line_editor.setText("")
    29.  
    30. def setEditorData(self, editor, index):
    31. pos = editor.findData(index.data().toInt()[0])
    32. if pos == -1: #text not found, set cell value to first item in VALUES
    33. pos = 0
    34. editor.setCurrentIndex(pos)
    35.  
    36. def setModelData(self, editor, model, index):
    37. model.setData(index, QtCore.QVariant(editor.itemData(editor.currentIndex()))) # editor.currentIndex es la posición actual en el combobox; itemData recupera (por default) la data UserRole almacenada allÃ*, que en este caso es el valor del foreign key
    38.  
    39.  
    40. class myWindow(QtGui.QDialog):
    41. def __init__(self):
    42. QtGui.QDialog.__init__(self)
    43. DATA = [['First row', 1, 1], ['Second Row', 2, 2]]
    44. self.table = QtGui.QTableWidget(self)
    45. self.table.setSortingEnabled(False)
    46. self.table.setRowCount(len(DATA))
    47. self.table.setColumnCount(len(DATA[0]))
    48. self.table.setItemDelegateForColumn(1, MyRelationalDelegate(self))
    49. for row in range(len(DATA)):
    50. for col in range(len(DATA[row])):
    51. item = QtGui.QTableWidgetItem(str(DATA[row][col]))
    52. self.table.setItem(row, col, item)
    53. layout = QtGui.QVBoxLayout()
    54. layout.addWidget(self.table)
    55. self.setLayout(layout)
    56.  
    57.  
    58. if __name__ == "__main__":
    59. app = QtGui.QApplication(sys.argv)
    60. window = myWindow()
    61. window.show()
    62. app.exec_()
    To copy to clipboard, switch view to plain text mode 

Similar Threads

  1. Replies: 26
    Last Post: 7th January 2016, 20:26
  2. Hover on mouse over while dragging
    By mooreaa in forum Qt Programming
    Replies: 3
    Last Post: 6th February 2010, 10:31
  3. QSystemTrayIcon capture mouse hover event
    By alan in forum Qt Programming
    Replies: 2
    Last Post: 1st August 2009, 19:42
  4. Replies: 1
    Last Post: 7th July 2009, 16:46
  5. segmentation fault on mouse hover
    By elessaar in forum Qt Programming
    Replies: 6
    Last Post: 26th August 2008, 12:51

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.