QTableView signal or event for "row height changed" - qt

I' m looking for an Event or Signal like "row height changed" that is called if the user changes the height of a row in my QTableView. I want to use this signal to resize all other rows in the QTableView to the new height. I didn' t find such an Event or Signal so I reckon there must be some kind of handy workaround.
Any suggestions will be appreciated.

Row resizing is performed by the vertical QHeaderView. The object emmiting it is QTableView::verticalHeader()
the signal you are interested in is
void QHeaderView::sectionResized ( int logicalIndex, int oldSize, int newSize )
This signal is emitted when a section is resized. The section's logical number is specified by logicalIndex, the old size by oldSize, and the new size by newSize.

Use QHeaderView (You can get an instance by calling QTableView::horizontalHeader()/QTableView::verticalHeader() ) and connect to geometriesChanged() or sectionResized()

The solution with QHeaderView and sectionResized works very well for me. So for resizing all
rows to the same height as the edited row I use this code now:
class MyWidget(QWidget):
def __init__(self, parent=None):
#Do some other init things
self.myTable.verticalHeader().sectionResized.connect(
self.row_resized)
def row_resized(self, index, old_size, new_size):
for i in range(self.myTable.verticalHeader().count()):
self.myTable.setRowHeight(i, new_size)

I wanted to save data after a section was resized, but found sectionResized fired a signal continuously while adjusting, rather than once at the end.
The simplest solution I could think of was to subclass the QHeaderView.
class CustomHeader(QtWidgets.QHeaderView):
"""
Custom header class to return when a section has been resized
Only after mouse released, opposed to continuously while resizing
"""
on_section_resized = QtCore.Signal(int)
def __init__(self, *args, **kwargs):
super(CustomHeader, self).__init__(*args, **kwargs)
self._has_resized = False
self._index = 0
self.sectionResized.connect(self.section_resized)
def section_resized(self, index, old_size, new_size):
self._has_resized = True
self._index = index
return
def mouseReleaseEvent(self, event):
super(CustomHeader, self).mouseReleaseEvent(event)
if self._has_resized:
self._has_resized = False
self.on_section_resized.emit(self._index)
return
In my main class I assigned the header to the table widget:
self.table_widget.setVerticalHeader(CustomHeader(QtCore.Qt.Vertical, self.table_widget))
Then connected the custom signal to my function within the original class:
self.table_widget.verticalHeader().on_section_resized.connect(self.save_section_height)
Part of the Save function:
def save_section_height(self, row):
"""
Updates the edited row's section height
:param row: int, row that has been changed
:return: None
"""
new_section_height = self.table_widget.verticalHeader().sectionSize(row)
I expect some things could be optimised better, but this at least fires one save signal opposed to ten plus, or something!

Related

Iterate through a pyqt pyside QtreeView with a filesystem model and format value based on a condition [duplicate]

I'm having a QListView with a QFileSystemModel. Based on a selection in a QTreeView, the QListView shows the content of the folder.
Now I need to change the color of the filenames depending on some condition.
The initial idea would be to iterate over the items in the QListView and set the color for each item depending on whether the condition is fulfilled. However this seems to be impossible, since the setData() method of QFileSystemModel only accepts changes to the EditRole, ignoring something like [see this]
self.FileModel.setData(index, QtGui.QBrush(QtCore.Qt.red), role=QtCore.Qt.ForegroundRole)
This has also been pointed out here
and the suggestion in the latter was to subclass QItemDelegate for the purpose of colorizing items in the QListView.
I therefore subclassed QStyledItemDelegate and reimplemented its paint() method to show the filename in green, if the condition is fulfilled - which works fine. However it now looks kind of ugly: File icons are lost and the "mouse_over" effect is not working anymore.
While this subclassing is anyway a messy work-around, my top-level question would be
Is there a way to colorize items in a QListView connected to a QFileSystemModel based on a condition?
Now provided that this might not be the case and sticking to the subclassing of QItemDelegate,
Is there a way to get the original behaviour with nice selections and icons back?
Does anyone know which ItemDelegate is originally used for QFileSystemModel in a QListView and how to use it?
Is it possible to get its source code and copy the paint method from there ?
Here is a minimal code that uses subclassing and shows the descibed behaviour. It uses a QLineEdit where one can type in a string, such that all files containing that string are highlighted in green.
import sys
from PyQt4 import QtGui, QtCore
class MyFileViewDelegate(QtGui.QStyledItemDelegate ):
def __init__(self, parent=None, *args, **kwargs):
QtGui.QItemDelegate.__init__(self, parent, *args)
self.condition = None
self.isMatch = False
self.brush_active = QtGui.QBrush(QtGui.QColor("#79b9ed"))
self.brush_active_matched = QtGui.QBrush(QtGui.QColor("#58cd1c"))
self.pen = QtGui.QPen(QtGui.QColor("#414141") )
self.pen_matched = QtGui.QPen(QtGui.QColor("#39c819") )
self.pen_active = QtGui.QPen(QtGui.QColor("#eef2fd") )
self.pen_active_matched = QtGui.QPen(QtGui.QColor("#e7fade") )
def paint(self, painter, option, index):
text = index.data(QtCore.Qt.DisplayRole)
self.matchText(text)
painter.save()
######## set background
painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
if option.state & QtGui.QStyle.State_Selected:
if self.isMatch:
painter.setBrush(self.brush_active_matched)
else:
painter.setBrush(self.brush_active)
painter.drawRect(option.rect)
######## set font color
if option.state & QtGui.QStyle.State_Selected:
if self.isMatch:
painter.setPen(self.pen_active_matched)
else:
painter.setPen(self.pen_active)
else:
if self.isMatch:
painter.setPen(self.pen_matched)
else:
painter.setPen(self.pen)
painter.drawText(option.rect, QtCore.Qt.AlignLeft, text)
painter.restore()
def matchText(self, filename):
# testing condition. In the real case this is much more complicated
if (self.condition != None) and (self.condition != "") and (self.condition in filename):
self.isMatch = True
else:
self.isMatch = False
def setCondition(self, condition):
self.condition = condition
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None, useDelegate = False):
super(MainWidget, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
self.FolderModel = QtGui.QFileSystemModel()
self.FolderModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.AllDirs)
self.FolderModel.setRootPath("")
self.FolderView = QtGui.QTreeView(parent=self)
self.FolderView.setModel(self.FolderModel)
self.FolderView.setHeaderHidden(True)
self.FolderView.hideColumn(1)
self.FolderView.hideColumn(2)
self.FolderView.hideColumn(3)
self.FolderView.expanded.connect(self.FolderView.scrollTo)
self.FolderView.clicked[QtCore.QModelIndex].connect(self.browserClicked)
self.FileModel = QtGui.QFileSystemModel()
self.FileModel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files)
self.FileView = QtGui.QListView(parent=self)
self.FileView.setModel(self.FileModel)
self.FileViewDelegate = None
if useDelegate:
self.FileViewDelegate = MyFileViewDelegate()
self.FileView.setItemDelegate(self.FileViewDelegate)
self.FileView.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection )
self.LineEdit = QtGui.QLineEdit()
self.LineEdit.textChanged.connect(self.changeCondition)
# Add Widgets to layout
self.layout().addWidget(self.FolderView)
self.layout().addWidget(self.FileView)
self.layout().addWidget(self.LineEdit)
def changeCondition(self, text):
if self.FileViewDelegate:
self.FileViewDelegate.setCondition(text)
def browserClicked(self, index):
# the signal passes the index of the clicked item
# set the FileView's root_index to the clicked index
dir_path = self.FileModel.filePath(index)
root_index = self.FileModel.setRootPath(dir_path)
self.FileView.setRootIndex(root_index)
class App(QtGui.QMainWindow):
def __init__(self, parent=None, useDelegate=False):
super(App, self).__init__(parent)
self.central = MainWidget(parent =self, useDelegate=useDelegate)
self.setCentralWidget(self.central)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App(None, True) # set False to view App without custom FileViewDelegate
thisapp.show()
sys.exit(app.exec_())
This is the comparison of how it looks with and without subclassing QItemDelegate:
just to mention, another problem with this code is, that once the condition is changed, one needs to move the mouse into the QFileView to initiate the repainting. I wonder which slot I could use to connect to the LineEdit.textChange signal to do that directly.
There's no need for an item-delegate. It can be achieved much more simply by reimplementing the data method of the QFileSystemModel:
class FileSystemModel(QtGui.QFileSystemModel):
def __init__(self, *args, **kwargs):
super(FileSystemModel, self).__init__(*args, **kwargs)
self.condition = None
def setCondition(self, condition):
self.condition = condition
self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
def data(self, index, role=QtCore.Qt.DisplayRole):
if self.condition and role == QtCore.Qt.TextColorRole:
text = index.data(QtCore.Qt.DisplayRole)
if self.condition in text:
return QtGui.QColor("#58cd1c")
return super(FileSystemModel, self).data(index, role)
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None, useDelegate = False):
super(MainWidget, self).__init__(parent)
...
self.FileModel = FileSystemModel(self)
...
def changeCondition(self, text):
self.FileModel.setCondition(text)

Implementing a delegate for wordwrap in a QTreeView (Qt/PySide/PyQt)?

I have a tree view with a custom delegate to which I am trying to add word wrap functionality. The word wrapping is working fine, but the sizeHint() seems to not work, so when the text wraps, the relevant row does not expand to include it.
I thought I was taking care of it in sizeHint() by returning document.size().height().
def sizeHint(self, option, index):
text = index.model().data(index)
document = QtGui.QTextDocument()
document.setHtml(text)
document.setTextWidth(option.rect.width())
return QtCore.QSize(document.idealWidth(), document.size().height())
However, when I print out document.size().height() it is the same for every item.
Also, even if I manually set the height (say, to 75) just to check that things will look reasonable, the tree looks like a goldfish got shot by a bazooka (that is, it's a mess):
As you can see, the text in each row is not aligned properly in the tree.
Similar posts
Similar issues have come up before, but no solutions to my problem (people usually say to reimplement sizeHint(), and that's what I am trying):
QTreeWidget set height of each row depending on content
QTreeView custom row height of individual rows
http://www.qtcentre.org/threads/1289-QT4-QTreeView-and-rows-with-multiple-lines
SSCCE
import sys
from PySide import QtGui, QtCore
class SimpleTree(QtGui.QTreeView):
def __init__(self, parent = None):
QtGui.QTreeView.__init__(self, parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setGeometry(500,200, 400, 300)
self.setUniformRowHeights(False) #optimize: but for word wrap, we don't want this!
print "uniform heights in tree?", self.uniformRowHeights()
self.model = QtGui.QStandardItemModel()
self.model.setHorizontalHeaderLabels(['Task', 'Description'])
self.setModel(self.model)
self.rootItem = self.model.invisibleRootItem()
item0 = [QtGui.QStandardItem('Sneeze'), QtGui.QStandardItem('You have been blocked up')]
item00 = [QtGui.QStandardItem('Tickle nose, this is a very long entry. Row should resize.'), QtGui.QStandardItem('Key first step')]
item1 = [QtGui.QStandardItem('<b>Get a job</b>'), QtGui.QStandardItem('Do not blow it')]
self.rootItem.appendRow(item0)
item0[0].appendRow(item00)
self.rootItem.appendRow(item1)
self.setColumnWidth(0,150)
self.expandAll()
self.setWordWrap(True)
self.setItemDelegate(ItemWordWrap(self))
class ItemWordWrap(QtGui.QStyledItemDelegate):
def __init__(self, parent=None):
QtGui.QStyledItemDelegate.__init__(self, parent)
self.parent = parent
def paint(self, painter, option, index):
text = index.model().data(index)
document = QtGui.QTextDocument() # #print "dir(document)", dir(document)
document.setHtml(text)
document.setTextWidth(option.rect.width()) #keeps text from spilling over into adjacent rect
painter.save()
painter.translate(option.rect.x(), option.rect.y())
document.drawContents(painter) #draw the document with the painter
painter.restore()
def sizeHint(self, option, index):
#Size should depend on number of lines wrapped
text = index.model().data(index)
document = QtGui.QTextDocument()
document.setHtml(text)
document.setTextWidth(option.rect.width())
return QtCore.QSize(document.idealWidth() + 10, document.size().height())
def main():
app = QtGui.QApplication(sys.argv)
myTree = SimpleTree()
myTree.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
The issue seems to stem from the fact that the value for option.rect.width() passed into QStyledItemDelegate.sizeHint() is -1. This is obviously bogus!
I've solved this by storing the width in the model from within the paint() method and accessing this from sizeHint().
So in your paint() method add the line:
index.model().setData(index, option.rect.width(), QtCore.Qt.UserRole+1)
and in your sizeHint() method, replace document.setTextWidth(option.rect.width()) with:
width = index.model().data(index, QtCore.Qt.UserRole+1)
if not width:
width = 20
document.setTextWidth(width)

Disable user to click over QTableWidget

I have QTableWidget with CheckBoxes in some cells. I want to disable user to perform mouse click over the table cells (so he can't change checkBox state) for some time while I am using data from the table. I've tried table.setDisabled(1) but that disables whole table and I need scroll to be enabled.
Any help would be appreciated.
EDIT
To be more precise: there could be up to 15x3000 cells in table, filled with text(editable), checkbox(checkable), svg graphic(opens other window when double click on it) or some custom widgets(which also have clickable or editable parts). I need to disable user to click or double click over cells(so he can't change any of them) for 1sec - 10sec time interval (solution must be something fast, not iterating through all items), but I need scroll-bar to be enabled and normal table visibility.
One way to achieve this is to subclass QTableWidgetItem and re-implement the setData method. That way, you can control whether items accept values for certain roles.
To control the "checkability" for all items, you could add a class attribute to the subclass which could be tested whenever a value for the check-state role was passed to setData.
Here's what the subclass might look like:
class TableWidgetItem(QtGui.QTableWidgetItem):
_blocked = True
#classmethod
def blocked(cls):
return cls._checkable
#classmethod
def setBlocked(cls, checkable):
cls._checkable = bool(checkable)
def setData(self, role, value):
if role != QtCore.Qt.CheckStateRole or self.checkable():
QtGui.QTableWidgetItem.setData(self, role, value)
And the "checkability" of all items would be disabled like this:
TableWidgetItem.setCheckable(False)
UPDATE:
The above idea can be extended by adding a generic wrapper class for cell widgets.
The classes below will block changes to text and check-state for table-widget items, and also a range of keyboard and mouse events for cell widgets via an event-filter (other events can be blocked as required).
The cell-widgets would need to be created like this:
widget = CellWidget(self.table, QtGui.QLineEdit())
self.table.setCellWidget(row, column, widget)
and then accessed like this:
widget = self.table.cellWidget().widget()
Blocking for the whole table would be switched on like this:
TableWidgetItem.setBlocked(True)
CellWidget.setBlocked(True)
# or Blockable.setBlocked(True)
Here are the classes:
class Blockable(object):
_blocked = False
#classmethod
def blocked(cls):
return cls._blocked
#classmethod
def setBlocked(cls, blocked):
cls._blocked = bool(blocked)
class TableWidgetItem(Blockable, QtGui.QTableWidgetItem):
def setData(self, role, value):
if (not self.blocked() or (
role != QtCore.Qt.EditRole and
role != QtCore.Qt.CheckStateRole)):
QtGui.QTableWidgetItem.setData(self, role, value)
class CellWidget(Blockable, QtGui.QWidget):
def __init__(self, parent, widget):
QtGui.QWidget.__init__(self, parent)
self._widget = widget
layout = QtGui.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(widget)
widget.setParent(self)
widget.installEventFilter(self)
if hasattr(widget, 'viewport'):
widget.viewport().installEventFilter(self)
widget.show()
def widget(self):
return self._widget
def eventFilter(self, widget, event):
if self.blocked():
etype = event.type()
if (etype == QtCore.QEvent.KeyPress or
etype == QtCore.QEvent.KeyRelease or
etype == QtCore.QEvent.MouseButtonPress or
etype == QtCore.QEvent.MouseButtonRelease or
etype == QtCore.QEvent.MouseButtonDblClick or
etype == QtCore.QEvent.ContextMenu or
etype == QtCore.QEvent.Wheel):
return True
return QtGui.QWidget.eventFilter(self, widget, event)
Just iterate through all QStandardItems and change flags values for items which should not be changeable.
You can use flag: Qt::ItemIsEditable or/and Qt::ItemIsEnabled.
You would need to disable the items themselves as opposed to the whole table if you have other items than QCheckBoxes that you would not like to disable. See the python code below for details:
'''
Iterate through all the check boxes in the standard items
and make sure the enabled flag is cleared so that the items are disabled
'''
for standardItem in standardItems:
standardItem.setFlags(standardItem.flags() & ~Qt.ItemIsEnabled)
Here you can find the corresponding documentation:
void QTableWidgetItem::setFlags(Qt::ItemFlags flags)
Sets the flags for the item to the given flags. These determine whether the item can be selected or modified.

wxPython Solitaire GUI

I'm writing a Solitaire GUI using wxPython, and I'm on Windows 7. I've only written one GUI before (in Java Swing), so I'm not as familiar as I could be with all the different types of widgets and controls. I'm faced with the challenge of having resizable, cascading piles of cards in the Tableaux of the Solitaire board. To me, using BitmapButtons for each card (or at least for face-up cards) and having a panel contain a pile of cards seemed natural, since it is legal to move sub-piles of cards in the Tableau from pile to pile in Solitaire. I'm sure there is a better way to do this, but for now I've been fiddling with a smaller GUI (not my main GUI) to try and achieve this. I've attached the code for the test GUI below.
Note: My main GUI uses a GridBagSizer with 14 cells. I haven't tried using the following panel/buttons in the GridBagSizer, or even know if a GridBagSizer is the best way to go about this.
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id_, title):
wx.Frame.__init__(self, parent, id_, title, size=(810, 580))
self.panel = wx.Panel(self, size=(72, 320), pos=(20,155))
self.buttons = []
self.init_buttons()
def init_buttons(self):
for i in range(6):
face_down = wx.Image('img/cardback.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()
wid = face_down.GetWidth()
hgt = face_down.GetHeight()
bmpbtn = wx.BitmapButton(self.panel, -1, bitmap=face_down, pos=(20,155+7*i), size=(wid, hgt))
bmpbtn.Bind(wx.EVT_ENTER_WINDOW, self.onMouseOver)
self.buttons.append(bmpbtn)
for i in range(1,14):
rank = 14 - i
if i % 2 == 0:
filename = 'img/%sC.png' % rank
else:
filename = 'img/%sH.png' % rank
img = wx.Image(filename, wx.BITMAP_TYPE_PNG).ConvertToBitmap()
wid = img.GetWidth()
hgt = img.GetHeight()
bmpbtn = wx.BitmapButton(self.panel, -1, bitmap=img, pos=(20, 177+20*i), size=(wid, hgt))
bmpbtn.Bind(wx.EVT_ENTER_WINDOW, self.onMouseOver)
self.buttons.append(bmpbtn)
def onMouseOver(self, event):
#event.Skip()
pass
class MyApp(wx.App):
def OnInit(self):
wx.InitAllImageHandlers()
self.frame = MyFrame(None, -1, "Solitaire")
self.frame.Show(True)
self.SetTopWindow(self.frame)
return True
app = MyApp(0)
app.MainLoop()
This is what results from running:
http://oi44.tinypic.com/1zv4swj.jpg
Which I was satisfied with, until I moved my mouse over some of the buttons:
http://oi44.tinypic.com/2rdupmq.jpg
This must have to do with the EVT_ENTER_WINDOW event. I attempted to write an event handler, but realized I didn't really know how to achieve what I need. According to the docs, a BitmapButton has different bitmaps for each of its states - hover, focus, selected, inactive, etc. However, I do not want to change the Bitmap on a mouseover event. I simply want the button to stay put, and to not display itself on top of other buttons.
Any help would be greatly appreciated. Incidentally, if anybody has advice for a better way (than GridBagSizer and these panels of buttons) to implement this GUI, I would love that!
I would recommend against using actual window controls for each of the cards. I would instead have a single canvas upon which you render the card bitmaps in their appropriate locations. You'll have to do a little extra math to determine what cards are being clicked on, but this is definitely the way to go.
Use a wx.Panel with a EVT_PAINT handler to do your drawing.
Here's a starting point that is written to use double-buffering to avoid flickering.
P.S. You can use bitmap = wx.Bitmap(path) to load an image, instead of bothering with wx.Image and converting it to a bitmap object.
import wx
class Panel(wx.Panel):
def __init__(self, parent):
super(Panel, self).__init__(parent)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
self.Bind(wx.EVT_LEFT_UP, self.on_left_up)
def on_left_down(self, event):
print 'on_left_down', event.GetPosition()
def on_left_up(self, event):
print 'on_left_up', event.GetPosition()
def on_paint(self, event):
dc = wx.AutoBufferedPaintDC(self)
# Use dc.DrawBitmap(bitmap, x, y) to draw the cards here
class Frame(wx.Frame):
def __init__(self):
super(Frame, self).__init__(None)
self.SetTitle('My Title')
Panel(self)
def main():
app = wx.App()
frame = Frame()
frame.Center()
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()

Clickable elements or child widgets inside custom-painted delegate

I have a QListView, where I display items using a custom delegate with custom painting. Within each item (i.e. each list row) I want to be able to show a couple of "hyperlinks" which the user could click on and which would then call on some functions.
I have already tried to check the official documentation (e.g. Model/View Programming) as well as quite a lot of googling, but haven't been able to figure out how to accomplish this.
I have two ideas, each with their own problems:
I could draw them using child widgets, like a flat QPushButton. How do I then position and display these widgets?
I could also draw them as text strings. How do I then make them clickable? Or can I capture click events on the parent QListView and somehow determine coordinates from those? I could then match coordinates to these clickable elements and act accordingly.
My initial approach was to use QListWidget with .setItemWidget(), where I had a proper widget with a layout and child widgets. Unfortunately this was too slow when my list grew to hundreds or thousands of items. That's why I changed to QListView with a delegate.
I seem to be closing in on a solution.
I can receive clicks on the elements by overriding the delegate's .editorEvent(event, model, option, index). I can then find out the event.type(), the clicked row from index.row() and the actual coordinates from event.x() and event.y() (since, if the event type is MouseButtonRelease, the event is a QMouseEvent).
From these, I think I can correlate the coordinates to my elements on screen and act accordingly.
I will update this answer once I have working code.
EDIT
A simple working example, using PySide:
class MyModel(QtGui.QStandardItemModel):
def __init__(self):
super(MyModel, self).__init__()
for i in range(10): self.appendRow(QtGui.QStandardItem("Row %d" % i))
class MyDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent=None):
super(MyDelegate, self).__init__(parent)
self.links = {}
def makeLinkFunc(self, row, text):
def linkFunc(): print("Clicked on %s in row %d" % (text, row))
return linkFunc
def paint(self, painter, option, index):
painter.save()
textHeight = QtGui.QFontMetrics(painter.font()).height()
painter.drawText(option.rect.x()+2, option.rect.y()+2+textHeight, index.data())
rowLinks = {}
for i in range(3):
text = "Link %d" % (3-i)
linkWidth = QtGui.QFontMetrics(font).width(text)
x = option.rect.right() - (i+1) * (linkWidth + 10)
painter.drawText(x, y, text)
rect = QtCore.QRect(x, y - textHeight, linkWidth, textHeight)
rowLinks[rect] = self.makeLinkFunc(index.row(), text)
self.links[index.row()] = rowLinks
painter.restore()
def sizeHint(self, option, index):
hint = super().sizeHint(option, index)
hint.setHeight(30)
return hint
def editorEvent(self, event, model, option, index):
if event.type() == QtCore.QEvent.MouseButtonRelease:
for rect, link in self.links[index.row()].items():
if rect.contains(event.pos()):
link()
return True
return False
listmodel = MyModel()
listview = QtGui.QListView()
listview.setModel(listmodel)
listview.setItemDelegate(MyDelegate(parent=listview))
listview.setSelectionMode(QtGui.QAbstractItemView.NoSelection)

Resources