def __init__(self, treeParam, parent=None): super().__init__(parent) self.show_tree_icons = treeParam.show_tree_icons self.convert_fraction = treeParam.convert_fraction self.setHeaderHidden(True) self.setAutoScroll(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.contextMenuEvent) self.currentItemChanged.connect(self.itemActivatedEvent) self.itemExpanded.connect(self.expandedEvent) self.collapsed.connect(self.collapsedEvent) self.treeParam = treeParam # Changing of TreeView is enabled (by signals from model or ListView) self.changingEnabled = True locale = Settings()['locale'] self.collator = QCollator(QLocale(locale)) self.collator.setNumericMode(True) self.setItemDelegate(AutoToolTipDelegate())
def __init__(self, parent=None): super().__init__(parent) self.setDynamicSortFilter(True) locale = Settings()['locale'] self.collator = QCollator(QLocale(locale)) self.collator.setNumericMode(True)
class SortFilterProxyModel(QSortFilterProxyModel): def __init__(self, parent=None): super().__init__(parent) self.setDynamicSortFilter(True) locale = Settings()['locale'] self.collator = QCollator(QLocale(locale)) self.collator.setNumericMode(True) def sort(self, column, order=Qt.AscendingOrder): self.model = self.sourceModel() super().sort(column, order) def lessThan(self, left, right): leftData = self.model.dataDisplayRole(left) rightData = self.model.dataDisplayRole(right) if isinstance(leftData, str): rightData = str(rightData) return self.collator.compare(leftData, rightData) < 0 elif isinstance(rightData, str): leftData = str(leftData) return self.collator.compare(leftData, rightData) < 0 return leftData < rightData
class TreeView(QTreeWidget): FiltersRole = Qt.UserRole FieldsRole = Qt.UserRole + 1 ParamRole = Qt.UserRole + 2 SortDataRole = Qt.UserRole + 3 def __init__(self, treeParam, parent=None): super().__init__(parent) self.show_tree_icons = treeParam.show_tree_icons self.convert_fraction = treeParam.convert_fraction self.setHeaderHidden(True) self.setAutoScroll(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.contextMenuEvent) self.currentItemChanged.connect(self.itemActivatedEvent) self.itemExpanded.connect(self.expandedEvent) self.collapsed.connect(self.collapsedEvent) self.treeParam = treeParam # Changing of TreeView is enabled (by signals from model or ListView) self.changingEnabled = True locale = Settings()['locale'] self.collator = QCollator(QLocale(locale)) self.collator.setNumericMode(True) self.setItemDelegate(AutoToolTipDelegate()) def setModel(self, model, reference): self.db = model.database() self.model = model self.reference = reference self.treeParam.rootTitle = model.title rootItem = QTreeWidgetItem([ model.title, ]) rootItem.setData(0, self.ParamRole, 0) rootItem.setData(0, self.FiltersRole, '') self.addTopLevelItem(rootItem) def expandedEvent(self, item): for i in range(item.childCount()): child = item.child(i) if child.childCount() == 0: paramIndex = child.data(0, self.ParamRole) + 1 filters = child.data(0, self.FiltersRole) self.__updateChilds(child, paramIndex, filters) self.resizeColumnToContents(0) def collapsedEvent(self, parentItem): self.resizeColumnToContents(0) def __updateChilds(self, item, paramIndex=0, filters=''): fields = self.treeParam.fieldNames(paramIndex) if not fields: return sql = "SELECT DISTINCT %s FROM coins" % ','.join(fields) if filters: sql += " WHERE " + filters query = QtSql.QSqlQuery(sql, self.db) hasEmpty = False while query.next(): record = query.record() data = [] orig_data = [] filterSql = [] for i in range(record.count()): if record.isNull(i): hasEmpty = True continue orig_data.append(record.value(i)) text = str(record.value(i)) if text: if fields[i] == 'status': data.append(Statuses[text]) elif fields[i] == 'year': label = text try: year = int(text) if year < 0: label = "%d BC" % -year except ValueError: pass data.append(label) elif fields[i] == 'value': label, _ = numberWithFraction(text, self.convert_fraction) data.append(label) else: data.append(text) escapedText = text.replace("'", "''") filterSql.append("%s='%s'" % (fields[i], escapedText)) else: hasEmpty = True if data: if len(data) > 1: newFilters = ' AND '.join(filterSql) text = ' '.join(data) child = TreeWidgetItem([ text, ]) child.setData(0, self.SortDataRole, orig_data) else: newFilters = filterSql[0] child = TreeWidgetItem(data) child.setData(0, self.SortDataRole, orig_data) if filters: newFilters = filters + ' AND ' + newFilters child.setData(0, self.ParamRole, paramIndex) child.setData(0, self.FiltersRole, newFilters) child.setData(0, self.FieldsRole, fields) if self.show_tree_icons: icon = self.reference.getIcon(fields[0], data[0]) if icon: child.setIcon(0, icon) item.addChild(child) # Restore selection if newFilters == self.model.extFilter: self.currentItemChanged.disconnect(self.itemActivatedEvent) self.setCurrentItem(child) self.currentItemChanged.connect(self.itemActivatedEvent) item.sortChildren(0, Qt.AscendingOrder) if hasEmpty and len(fields) == 1 and item.childCount() > 0: text = self.tr("Other") newFilters = "ifnull(%s,'')=''" % fields[0] if filters: newFilters = filters + ' AND ' + newFilters child = QTreeWidgetItem([ text, ]) child.setData(0, self.ParamRole, paramIndex) child.setData(0, self.FiltersRole, newFilters) child.setData(0, self.FieldsRole, fields) item.addChild(child) # Restore selection if newFilters == self.model.extFilter: self.currentItemChanged.disconnect(self.itemActivatedEvent) self.setCurrentItem(child) self.currentItemChanged.connect(self.itemActivatedEvent) # Recursion for next field if nothing selected if item.childCount() == 0: self.__updateChilds(item, paramIndex + 1, filters) def modelChanged(self): if self.changingEnabled: self.collapseAll() rootItem = self.topLevelItem(0) self.currentItemChanged.disconnect(self.itemActivatedEvent) rootItem.takeChildren() # remove all children self.currentItemChanged.connect(self.itemActivatedEvent) self.__updateChilds(rootItem) self.expandItem(rootItem) def rowChangedEvent(self, current): if self.changingEnabled: if current.isValid(): self.collapseAll() self.scrollToIndex(current) def scrollToIndex(self, index, parent=None): if not parent: parent = self.topLevelItem(0) for i in range(parent.childCount()): subItem = parent.child(i) fields = subItem.data(0, self.FieldsRole) text1 = subItem.text(0) textPart = [] for field in fields: index = self.model.index(index.row(), self.model.fieldIndex(field)) if field in ('status', 'year'): textPart.append(str(index.data())) else: val = str(index.data(Qt.UserRole)) if val: textPart.append(val) text2 = ' '.join(textPart) if text1 == text2 or (not text2 and text1 == self.tr("Other")): self.expandItem(parent) self.scrollToItem(subItem) self.scrollToIndex(index, subItem) break def scrollToItem(self, item, hint=QTreeWidget.EnsureVisible): super().scrollToItem(item, hint) parentItem = item.parent() if parentItem: itemRect = self.visualItemRect(parentItem) if itemRect.x() < 0: columnWidth = self.columnWidth(0) itemWidth = itemRect.width() self.horizontalScrollBar().setValue(columnWidth - itemWidth) elif self.viewport().width() / 2 < itemRect.x(): columnWidth = self.columnWidth(0) itemWidth = itemRect.width() self.horizontalScrollBar().setValue(itemRect.x()) def itemActivatedEvent(self, current, previous): self.scrollToItem(current) self.resizeColumnToContents(0) self.changingEnabled = False filter_ = current.data(0, self.FiltersRole) self.model.setAdditionalFilter(filter_) self.changingEnabled = True def contextMenuEvent(self, pos): menu = QMenu(self) act = menu.addAction(self.tr("Add new coin..."), self._addCoin) if not (self.model.rowCount() and self.selectedItems()): act.setDisabled(True) act = menu.addAction(self.tr("Edit coins..."), self._multiEdit) if not (self.model.rowCount() and self.selectedItems()): act.setDisabled(True) menu.addSeparator() menu.addAction(self.tr("Customize tree..."), self._customizeTree) menu.exec_(self.mapToGlobal(pos)) def _customizeTree(self): dialog = CustomizeTreeDialog(self.model, self.treeParam, self) if dialog.exec_() == QDialog.Accepted: self.treeParam.save() self.modelChanged() def _addCoin(self): self.changingEnabled = False storedFilter = self.model.intFilter # TODO: This change ListView! self.model.setFilter('') self.changingEnabled = True newRecord = self.model.record() # Fill new record with values of first record for j in range(newRecord.count()): newRecord.setValue(j, self.model.record(0).value(j)) for i in range(self.model.rowCount()): record = self.model.record(i) for j in range(newRecord.count()): value = record.value(j) if newRecord.value(j) != value or not value: newRecord.setNull(j) self.model.addCoin(newRecord, self) self.model.setFilter(storedFilter) def _multiEdit(self): self.changingEnabled = False storedFilter = self.model.intFilter self.model.setFilter('') self.changingEnabled = True # Fill multi record for editing multiRecord = self.model.record(0) usedFields = [Qt.Checked] * multiRecord.count() for i in range(self.model.rowCount()): record = self.model.record(i) for j in range(multiRecord.count()): value = record.value(j) if multiRecord.value(j) != value or not value: multiRecord.setNull(j) usedFields[j] = Qt.Unchecked # TODO: Make identical with ListView._multiEdit dialog = EditCoinDialog(self.model, multiRecord, self, usedFields) result = dialog.exec_() if result == QDialog.Accepted: progressDlg = Gui.ProgressDialog(self.tr("Updating records"), self.tr("Cancel"), self.model.rowCount(), self) # Fill records by used fields in multi record multiRecord = dialog.getRecord() usedFields = dialog.getUsedFields() for i in range(self.model.rowCount()): progressDlg.setValue(i) if progressDlg.wasCanceled(): break record = self.model.record(i) for j in range(multiRecord.count()): if usedFields[j] == Qt.Checked: record.setValue(j, multiRecord.value(j)) self.model.setRecord(i, record) self.model.submitAll() progressDlg.reset() self.model.setFilter(storedFilter)