i trying make simple property editor, property list nested dict , data displayed , edited in qtreeview. (before question -- if has working implementation of in python 3 i'd love pointed @ it).
anyway, after work have qabstractitemmodel , can open qtreeview model , shows data. if click on label in first column (the key) opens editor, either text editor or spinbox etc depending on datatype. when finish editing calls "model.setdata" reject because don't want allow editable keys. can disable editing of using flags , works fine. wanted check works way i'd expect to.
here doesn't happen: if click on cell in second column (the value want edit) bypasses loading of editor , calls model.setdata current value. baffled. i've tried changing tree selectionbehavior , selectionmode no dice. i'm returning qt.itemisenabled | qt.itemisselectable | qt.itemiseditable in flags. seems display fine. won't open editor.
any thoughts stupid mistake must making? i'll include code below, print statements i'm using try debug thing.
thanks
ps 1 thing hung me long time qmodelindex members disappear, indices got garbage. found keeping reference them (throwing them in list) worked. seems problem springs lot in qt work (i had same problem menus disappearing -- guess means should think sooner). there "best practices" way of dealing this?
# -*- coding: utf-8 -*- collections import ordereddict pyqt4.qtcore import qabstractitemmodel, qmodelindex, qt pyqt4.qtgui import qabstractitemview class propertylist(ordereddict): def __init__(self, *args, **kwargs): ordereddict.__init__(self, *args, **kwargs) self.mymodel = propertylistmodel(self) def __getitem__(self,index): if issubclass(type(index), list): item = self key in index: item = item[key] return item else: return ordereddict.__getitem__(self, index) class propertylistmodel(qabstractitemmodel): def __init__(self, proplist, *args, **kwargs): qabstractitemmodel.__init__(self, *args, **kwargs) self.propertylist = proplist self.myindexes = [] # needed stop garbage collection def index(self, row, column, parent): """returns qmodelindex row, column in parent (qmodelindex)""" if not self.hasindex(row, column, parent): return qmodelindex() if parent.isvalid(): indexptr = parent.internalpointer() parentdict = self.propertylist[indexptr] else: parentdict = self.propertylist indexptr = [] rowkey = list(parentdict.keys())[row] childptr = indexptr+[rowkey] newindex = self.createindex(row, column, childptr) self.myindexes.append(childptr) return newindex def get_row(self, key): """returns row of given key (list of keys) in parent""" if key: parent = key[:-1] return list(self.propertylist[parent].keys()).index(key[-1]) else: return 0 def parent(self, index): """ returns parent (qmodelindex) of given item (qmodelindex) top level returns qmodelindex() """ if not index.isvalid(): return qmodelindex() childkeylist = index.internalpointer() if childkeylist: parentkeylist = childkeylist[:-1] self.myindexes.append(parentkeylist) return self.createindex(self.get_row(parentkeylist), 0, parentkeylist) else: return qmodelindex() def rowcount(self, parent): """returns number of rows in parent (qmodelindex)""" if parent.column() > 0: return 0 # keys have children, not values if parent.isvalid(): indexptr = parent.internalpointer() try: parentvalue = self.propertylist[indexptr] except: return 0 if issubclass(type(parentvalue), dict): return len(self.propertylist[indexptr]) else: return 0 else: return len(self.propertylist) def columncount(self, parent): return 2 # key & value def data(self, index, role): """returns data given role given index (qmodelindex)""" # print('looking data in role {}'.format(role)) if not index.isvalid(): return none if role in (qt.displayrole, qt.editrole): indexptr = index.internalpointer() if index.column() == 1: # column 1, send value return self.propertylist[indexptr] else: # column 0, send key if indexptr: return indexptr[-1] else: return "" else: # not display or edit return none def setdata(self, index, value, role): """sets value of index in given role""" print('in setdata') if not index.isvalid(): return false print('trying set {} {}'.format(index,value)) print('that column {}'.format(index.column())) if not index.column(): # change column 1 return false try: ptr = index.internalpointer() self.propertylist[ptr[:-1]][ptr[-1]] = value self.emit(self.datachanged(index, index)) return true except: return false def flags(self, index): """indicates can done data""" if not index.isvalid(): return qt.noitemflags if index.column(): # enable editing of values, not keys return qt.itemisenabled | qt.itemisselectable | qt.itemiseditable else: return qt.itemisenabled | qt.itemisselectable | qt.itemiseditable #qt.noitemflags if __name__ == '__main__': p = propertylist({'k1':'v1','k2':{'k3':'v3','k4':4}}) import sys pyqt4 import qtgui qapp = qtgui.qapplication(sys.argv) treeview = qtgui.qtreeview() # i've played settings on these no avail treeview.setheaderhidden(false) treeview.setallcolumnsshowfocus(true) treeview.setuniformrowheights(true) treeview.setselectionbehavior(qabstractitemview.selectrows) treeview.setselectionmode(qabstractitemview.singleselection) treeview.setalternatingrowcolors(true) treeview.setedittriggers(qabstractitemview.doubleclicked | qabstractitemview.selectedclicked | qabstractitemview.editkeypressed | qabstractitemview.anykeypressed) treeview.settabkeynavigation(true) treeview.setmodel(p.mymodel) treeview.show() sys.exit(qapp.exec_())
@strubbly real close forgot unpack tuple in index method.
here's working code qt5. there couple of imports , stuff need fixed. cost me couple weeks of life :)
import sys collections import ordereddict pyqt5 import qtcore, qtwidgets pyqt5.qtcore import qt class tuplekeyedordereddict(ordereddict): def __init__(self, *args, **kwargs): super().__init__(sorted(kwargs.items())) def __getitem__(self, key): if isinstance(key, tuple): item = self k in key: if item != (): item = item[k] return item else: return super().__getitem__(key) def __setitem__(self, key, value): if isinstance(key, tuple): item = self previous_item = none k in key: if item != (): previous_item = item item = item[k] previous_item[key[-1]] = value else: return super().__setitem__(key, value) class settingsmodel(qtcore.qabstractitemmodel): def __init__(self, data, parent=none): super().__init__(parent) self.root = data self.my_index = {} # needed stop garbage collection def index(self, row, column, parent): if not self.hasindex(row, column, parent): return qtcore.qmodelindex() if parent.isvalid(): index_pointer = parent.internalpointer() parent_dict = self.root[index_pointer] else: parent_dict = self.root index_pointer = () row_key = list(parent_dict.keys())[row] child_pointer = (*index_pointer, row_key) try: child_pointer = self.my_index[child_pointer] except keyerror: self.my_index[child_pointer] = child_pointer index = self.createindex(row, column, child_pointer) return index def get_row(self, key): if key: parent = key[:-1] if not parent: return 0 return list(self.root[parent].keys()).index(key[-1]) else: return 0 def parent(self, index): if not index.isvalid(): return qtcore.qmodelindex() child_key_list = index.internalpointer() if child_key_list: parent_key_list = child_key_list[:-1] try: parent_key_list = self.my_index[parent_key_list] except keyerror: self.my_index[parent_key_list] = parent_key_list return self.createindex(self.get_row(parent_key_list), 0, parent_key_list) else: return qtcore.qmodelindex() def rowcount(self, parent): if parent.column() > 0: return 0 # keys have children, not values if parent.isvalid(): indexptr = parent.internalpointer() parentvalue = self.root[indexptr] if isinstance(parentvalue, ordereddict): return len(self.root[indexptr]) else: return 0 else: return len(self.root) def columncount(self, parent): return 2 # key & value def data(self, index, role): if not index.isvalid(): return none if role in (qtcore.qt.displayrole, qtcore.qt.editrole): indexptr = index.internalpointer() if index.column() == 1: # column 1, send value return self.root[indexptr] else: # column 0, send key if indexptr: return indexptr[-1] else: return none else: # not display or edit return none def setdata(self, index, value, role): pointer = self.my_index[index.internalpointer()] self.root[pointer] = value self.datachanged.emit(index, index) return true def flags(self, index): if not index.isvalid(): return 0 return qt.itemisenabled | qt.itemisselectable | qt.itemiseditable if __name__ == '__main__': app = qtwidgets.qapplication(sys.argv) data = tuplekeyedordereddict(**{'1': ordereddict({'sub': 'b'}), '2': ordereddict({'subsub': '3'})}) model = settingsmodel(data) tree_view = qtwidgets.qtreeview() tree_view.setmodel(model) tree_view.show() sys.exit(app.exec_())
Comments
Post a Comment