Results 1 to 9 of 9

Thread: Draw Grid lines with Painter

  1. #1
    Join Date
    Apr 2011
    Posts
    19
    Thanks
    3
    Thanked 4 Times in 2 Posts
    Qt products
    Platforms
    Unix/X11 Windows

    Default Draw Grid lines with Painter

    I want to draw grid lines for only some boxes in my QTableView. For this I have implemented a delegate that draws the lines. This all works however when I draw the lines they are thick and black. How do I get a hold of the default settings for the grid lines from my view that way I can make it look like all my other QTableview grid lines? My code is below.
    Qt Code:
    1. class setup_delegate(QtGui.QStyledItemDelegate):
    2.  
    3.  
    4. def __init__(self, parent=None):
    5. super(setup_delegate, self).__init__(parent)
    6.  
    7. def paint(self, painter, option, index):
    8. if index.data().toBool() or index.row() == 0:
    9. painter.save()
    10. painter.drawRect(option.rect)
    11. painter.restore()
    12. QtGui.QStyledItemDelegate.paint(self, painter, option, index)
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Apr 2011
    Posts
    19
    Thanks
    3
    Thanked 4 Times in 2 Posts
    Qt products
    Platforms
    Unix/X11 Windows

    Default Re: Draw Grid lines with Painter

    Ok so this isn't as simple as I initially though. The code in the above post draws lines but it doesn't acutally draw them in the right place. Reading the documentation carfully drawRect atually

    Draws the current rectangle with the current pen and brush.

    A filled rectangle has a size of rectangle.size(). A stroked rectangle has a size of rectangle.size() plus the pen width.
    So because the size of the rect is plus the pen width it looks like my grid lines are actually being drawn outside of my "cell". I think this is causing me problems because when a cell whose gridlines aren't being drawn is filled with some color this causes the grid lines for the column next to it to be covered. I made as short an example as I can. Its below some one please respond I could really use some help on this. Take a look at the cells with alternative color backgrounds.

    Qt Code:
    1. import random
    2. from PyQt4 import QtCore, QtGui
    3.  
    4. model_idx = QtCore.QModelIndex
    5.  
    6.  
    7. class TestModel(QtCore.QAbstractTableModel):
    8.  
    9. def __init__(self, dataset, parent=None):
    10. super(TestModel, self).__init__(parent)
    11. self.ds = dataset
    12.  
    13. def columnCount(self, index=model_idx()):
    14. return len(self.ds.keys())
    15.  
    16. def rowCount(self, index=model_idx()):
    17. return max(map(len, self.ds.values()))
    18.  
    19. def data(self, index, role=QtCore.Qt.DisplayRole):
    20. row = index.row()
    21. col = index.column()
    22. field = self.ds.keys()[col]
    23. if (role == QtCore.Qt.DisplayRole) or (role == QtCore.Qt.EditRole):
    24. values = self.ds[field]
    25. if row == 0:
    26. k = None
    27. elif len(values) > (row - 1):
    28. k = values[row - 1]
    29. else:
    30. k = None
    31. return QtCore.QVariant(k)
    32. return QtCore.QVariant()
    33.  
    34. def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
    35. if orientation == QtCore.Qt.Horizontal:
    36. if role == QtCore.Qt.DisplayRole:
    37. name = self.ds.keys()[section]
    38. return QtCore.QVariant(name)
    39. return QtCore.QVariant()
    40.  
    41.  
    42. class TestDelegate(QtGui.QStyledItemDelegate):
    43.  
    44. def __init__(self, parent=None):
    45. super(TestDelegate, self).__init__(parent)
    46.  
    47. def paint(self, painter, option, index):
    48. if index.data().toBool() or index.row() == 0:
    49. painter.save()
    50. painter.drawRect(option.rect)
    51. super(TestDelegate, self).paint(painter, option, index)
    52. painter.restore()
    53.  
    54. def SetupData():
    55. random.seed(92)
    56. space = xrange(5000)
    57. data = {}
    58. for k in range(5):
    59. key = 'Data Field {}'.format(k)
    60. ds = random.sample(space, 10)
    61. num_toss = random.randint(0,10)
    62. for it in range(num_toss):
    63. idx = random.randint(0,len(ds) - 1)
    64. ds.pop(idx)
    65. data[key] = ds
    66. return data
    67.  
    68. if __name__ == "__main__":
    69. import sys
    70. app = QtGui.QApplication(sys.argv)
    71. data = SetupData()
    72. form = QtGui.QTableView()
    73. model = TestModel(data, form)
    74. delegate = TestDelegate(form)
    75.  
    76. form.setShowGrid(False)
    77. form.setAlternatingRowColors(True)
    78. form.setModel(model)
    79. form.setItemDelegate(delegate)
    80. form.show()
    81. sys.exit(app.exec_())
    To copy to clipboard, switch view to plain text mode 

  3. #3
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Draw Grid lines with Painter

    I'm not sure the delegate is the right place to draw the grid. The original grid is drawn by the table view itself. If you want to do it in the delegate then I suppose you have to adjust the rectangle covered by the fill and possibly by the grid itself when the grid is being drawn. An alternative is to not paint the grid in the delegate's paint routine but rather subclass the view, have it do its original job (that calls paint on the delegate) and then call the delegate for each cell again just to overpaint the grid.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  4. The following user says thank you to wysota for this useful post:

    mechsin (17th January 2013)

  5. #4
    Join Date
    Apr 2011
    Posts
    19
    Thanks
    3
    Thanked 4 Times in 2 Posts
    Qt products
    Platforms
    Unix/X11 Windows

    Default Re: Draw Grid lines with Painter

    Thanks Wysota after digging around in the source code myself I came to the same conclusion. That is that it would be better to subclass the view and then re-implement the paintEvent method. I did get the grid lines to draw using the delegate by resizing the rectangle like you suggested but in the end this approach still had problems because the delegate isn't always getting called when the background is getting redrawn so sometimes my gridlines will disappear and not come back till I click in the box or some other event happens that calls the delegate. I am going to work on my implementation using the view and paintEvent method I also have to work out how to deal with the alternating back ground color I am not for sure yet when that is getting painted but I do know that I need to paint it in the paintEvent.

    If I work something out I will be sure to post it.

    Thanks

  6. #5
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Draw Grid lines with Painter

    The background is painted by the delegate, as far as I remember. The delegate checks for the QStyleOptionViewItemV2::Alternate feature and decides which background to draw.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  7. #6
    Join Date
    Apr 2011
    Posts
    19
    Thanks
    3
    Thanked 4 Times in 2 Posts
    Qt products
    Platforms
    Unix/X11 Windows

    Default Re: Draw Grid lines with Painter

    Wysota you are correct the background of the item/cell is painted by the delegate. However from digging around in the source code I found that the alternating background is drawn by something else. In the C++ source code I found a private method call drawCell this is the function that does the final setup to the Style Item Options object and then calls the delegate. But just before it calls the delegate it does this

    Qt Code:
    1. q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q);
    To copy to clipboard, switch view to plain text mode 

    I am pretty sure that this is when the alternate base background gets painted. Of course I am really no good at C++ so I could be wrong but if I sub the delegate and then overwrite the paint method and just do pass the alternate background colors will still get painted at least in PyQt.

    In any case the paintEvent is where the grid lines are getting drawn. So I sub classed the view and then over wrote that. It is a lot of work to get the math set up right and I had to duplicate a lot of code that was already in the source but the final result is what I deemed a SparseTableView. If I was better at C++ I would make suggestions to change the code so that this doesn't have to be such a chore, maybe in the future. I would have liked if the drawCell method were made public and the grid drawing done there. Anyway I posted my example here in PyQt of course in case anyone else needs it. It is well explained in the comments but basically if a cell doesn't have data I don't paint the grid lines. One caveat is this view cannot deal with cells that span more than one column or row. It was way more work to get that to happen. If you need that my best advice is to take a look at the QTableView source and then the paintEvent method.

    Qt Code:
    1. import random
    2. from PyQt4 import QtCore, QtGui
    3.  
    4. model_idx = QtCore.QModelIndex
    5.  
    6.  
    7. class TestModel(QtCore.QAbstractTableModel):
    8.  
    9. """ Test Setup Data Model
    10.  
    11. In this model the first row of the table is always empty.
    12. """
    13.  
    14. def __init__(self, dataset, parent=None):
    15. super(TestModel, self).__init__(parent)
    16. self.ds = dataset
    17.  
    18. def columnCount(self, index=model_idx()):
    19. return len(self.ds.keys())
    20.  
    21. def rowCount(self, index=model_idx()):
    22. return max(map(len, self.ds.values()))
    23.  
    24. def data(self, index, role=QtCore.Qt.DisplayRole):
    25. row = index.row()
    26. col = index.column()
    27. try:
    28. field = self.ds.keys()[col]
    29. except IndexError:
    30. return QtCore.QVariant()
    31. if (role == QtCore.Qt.DisplayRole) or (role == QtCore.Qt.EditRole):
    32. values = self.ds[field]
    33. if row == 0:
    34. k = None
    35. elif len(values) > (row - 1):
    36. k = values[row - 1]
    37. else:
    38. k = None
    39. return QtCore.QVariant(k)
    40. return QtCore.QVariant()
    41.  
    42. def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
    43. if orientation == QtCore.Qt.Horizontal:
    44. if role == QtCore.Qt.DisplayRole:
    45. name = self.ds.keys()[section]
    46. return QtCore.QVariant(name)
    47. return QtCore.QVariant()
    48.  
    49. def flags(self, index):
    50. if index.isValid():
    51. return QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsEnabled |
    52. QtCore.Qt.ItemIsSelectable)
    53.  
    54. class SparseTableView(QtGui.QTableView):
    55.  
    56. """ Table View With Sparse Grid Lines
    57.  
    58. This is an example of how you can overide the paintEvent in the QTableView
    59. to draw your own grid lines. In this particular view grid lines won't be
    60. drawn for empty cells with the exception of the first row which if use with
    61. the TestModel is always empty. Note this view cannot deal with cells that
    62. span multiple rows or columns.
    63.  
    64. """
    65.  
    66. def __init__(self, parent=None):
    67. super(SparseTableView, self).__init__(parent)
    68. self.setAlternatingRowColors(True)
    69. self.setShowGrid(False)
    70.  
    71. def paintEvent(self, event):
    72. # Call the super here first. It will draw a bunch of stuff including
    73. # alternating background color and the text. It will also call the item
    74. # delegate if one is set. We do this first otherwise our gridlines
    75. # might get painted over.
    76. super(SparseTableView, self).paintEvent(event)
    77.  
    78. # Get all the specifics about the grid color and size and setup the
    79. # grid pen
    80. sh = QtGui.QStyle.SH_Table_GridLineColor
    81. gridHint = self.style().styleHint(sh, self.viewOptions())
    82. # Do this to convert from signed int to unsigned int
    83. gridHint &= 0xffffffff
    84. gridColor = QtGui.QColor.fromRgb(gridHint)
    85. gridSize = 1
    86. gridPen = QtGui.QPen(gridColor, gridSize, self.gridStyle())
    87.  
    88. # Create the painter
    89. painter = QtGui.QPainter(self.viewport())
    90.  
    91. # Shortcuts to vertical and horizontal headers
    92. vh = self.verticalHeader()
    93. hh = self.horizontalHeader()
    94.  
    95. # Get the first and last rows that are visible in the view and if the
    96. # last visiable row returns -1 set it to the row count
    97. firstVisualRow = max([vh.visualIndexAt(0), 0])
    98. lastVisualRow = vh.visualIndexAt(vh.viewport().height())
    99. if lastVisualRow == -1:
    100. lastVisualRow = self.model().rowCount(self.rootIndex()) - 1
    101.  
    102. # Get the first and last columns that are visible in the view and if
    103. # if the last visible column is -1 set it to the column count.
    104. firstVisualColumn = max([hh.visualIndexAt(0), 0])
    105. lastVisualColumn = hh.visualIndexAt(hh.viewport().width())
    106. if lastVisualColumn == -1:
    107. lastVisualColumn = hh.count() - 1
    108.  
    109. # Iterate through each row and column drawing only the
    110. # bottom and left side lines for each cell. Skipping rows and columns
    111. # that are hidden
    112. for vrow in range(firstVisualRow, lastVisualRow + 1):
    113. row = vh.logicalIndex(vrow)
    114. FirstRow = (vrow == 0)
    115. if vh.isSectionHidden(row):
    116. continue
    117. # Get top left Y coordinate and row height
    118. rowY = self.rowViewportPosition(row)
    119. rowh = self.rowHeight(row)
    120. for vcol in range(firstVisualColumn, lastVisualColumn + 1):
    121. col = hh.logicalIndex(vcol)
    122. FirstColumn = (vcol == 0)
    123. if hh.isSectionHidden(col):
    124. continue
    125. # Get top left X coordinate and column width
    126. colX = self.columnViewportPosition(col)
    127. colw = self.columnWidth(col)
    128.  
    129. # Get the model index
    130. index = self.model().createIndex(row, col)
    131.  
    132. # Specify top, bottom, left and right of the cell accounting
    133. # for the with of the grid pen.
    134. top = rowY
    135. bottom = rowY + rowh - gridSize
    136. left = colX
    137. right = colX + colw - gridSize
    138.  
    139. # Boolean holder values for if this cell has data or if the
    140. # adjacent cell has data. If the adjacent cell has data. We
    141. # Still need to draw some lines.
    142. cell_has_data = index.data().toBool()
    143. adj_idx = index.model().createIndex(row, col + 1)
    144. adjacent_cell_filled = adj_idx.data().toBool()
    145.  
    146. # Save the painter and set the pen
    147. painter.save()
    148. painter.setPen(gridPen)
    149.  
    150. # If the cell is empty erase the back ground that way the
    151. # alternating background colors won't show up
    152. if not cell_has_data:
    153. painter.eraseRect(colX, rowY, colw, rowh)
    154.  
    155. # Draw Horizontal Lines
    156. # Only do this if the cell has data or is in the first row
    157. # We draw only the bottom line
    158. if cell_has_data or FirstRow:
    159. painter.drawLine(left, bottom, right, bottom)
    160.  
    161. # Draw Vertical Lines
    162. # Only do this if this cell or the right side adjacent cell has
    163. # data or we are in the first row.
    164. # We draw only the right side line
    165. if cell_has_data or adjacent_cell_filled or FirstRow:
    166. painter.drawLine(right, top, right, bottom)
    167.  
    168. # Restore painter
    169. painter.restore()
    170.  
    171. def SetupData():
    172. random.seed(94546)
    173. space = xrange(5000)
    174. data = {}
    175. for k in range(5):
    176. key = 'Data Field {}'.format(k)
    177. ds = random.sample(space, 10)
    178. num_toss = random.randint(0,10)
    179. for it in range(num_toss):
    180. idx = random.randint(0,len(ds) - 1)
    181. ds.pop(idx)
    182. data[key] = ds
    183. return data
    184.  
    185. if __name__ == "__main__":
    186. import sys
    187. app = QtGui.QApplication.instance()
    188. if not app:
    189. app = QtGui.QApplication(sys.argv)
    190. data = SetupData()
    191. form = SparseTableView()
    192. model = TestModel(data, form)
    193. form.setModel(model)
    194. form.show()
    195. sys.exit(app.exec_())
    To copy to clipboard, switch view to plain text mode 

  8. #7
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Draw Grid lines with Painter

    Quote Originally Posted by mechsin View Post
    I am pretty sure that this is when the alternate base background gets painted. Of course I am really no good at C++ so I could be wrong but if I sub the delegate and then overwrite the paint method and just do pass the alternate background colors will still get painted at least in PyQt.
    You can always overpaint it yourself in the delegate.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  9. #8
    Join Date
    Jan 2007
    Posts
    29
    Thanks
    3

    Default Re: Draw Grid lines with Painter

    What I do is turn grid lines off in the QTableView, and then use the delegate to draw lines when I want.

    Qt Code:
    1. /* Line for Columns */
    2. QPen grid_pen( Qt::Black, 0, Qt::SolidLine );
    3. painter->setPen( grid_pen );
    4. painter->drawLine( opt.rect.topRight(), opt.rect.bottomRight() );
    To copy to clipboard, switch view to plain text mode 

  10. #9
    Join Date
    Apr 2011
    Posts
    19
    Thanks
    3
    Thanked 4 Times in 2 Posts
    Qt products
    Platforms
    Unix/X11 Windows

    Default Re: Draw Grid lines with Painter

    Kiss that is what I tried first but then then my and it seemed to work but then I found there was some problem with my lines. They didn't always show up. It seems like after I moused over the box sometimes my lines would disappear and sometimes they would stay. I though it had to do with the order in which the lines were being drawn so I reordered some stuff but no dice. Of course I am using Python does this work for you in C++. Keep in mind that my table is "sparse" so there isn't data in every box and if there is no data it shouldn't get grid lines.

    Although the though just occurred to me I wonder if I create my own painter in the delegate and draw the line if that would solve the problem. That is basically the only difference between what I am doing in the paintEvent and what I was doing in the delegate.

Similar Threads

  1. Replies: 0
    Last Post: 3rd November 2011, 19:03
  2. Replies: 3
    Last Post: 26th July 2011, 20:11
  3. painter's drawPie() function how to draw few sensible PIEZ..
    By salmanmanekia in forum Qt Programming
    Replies: 15
    Last Post: 31st August 2008, 17:52
  4. Increasing the Width of grid lines in qtable
    By raghvendramisra in forum Qt Programming
    Replies: 5
    Last Post: 29th August 2008, 17:19
  5. use Qpainter to draw lines + problem in my painter
    By ReSu in forum Qt Programming
    Replies: 4
    Last Post: 5th March 2008, 16:44

Tags for this Thread

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.