class AddonManagerWidget(QWidget): statechanged = Signal() def __init__(self, parent=None, **kwargs): super(AddonManagerWidget, self).__init__(parent, **kwargs) self.__items = [] self.setLayout(QVBoxLayout()) self.__header = QLabel(wordWrap=True, textFormat=Qt.RichText) self.__search = QLineEdit(placeholderText=self.tr("Filter")) self.layout().addWidget(self.__search) self.__view = view = QTreeView(rootIsDecorated=False, editTriggers=QTreeView.NoEditTriggers, selectionMode=QTreeView.SingleSelection, alternatingRowColors=True) self.__view.setItemDelegateForColumn(0, TristateCheckItemDelegate()) self.layout().addWidget(view) self.__model = model = QStandardItemModel() model.setHorizontalHeaderLabels(["", "Name", "Version", "Action"]) model.dataChanged.connect(self.__data_changed) proxy = QSortFilterProxyModel(filterKeyColumn=1, filterCaseSensitivity=Qt.CaseInsensitive) proxy.setSourceModel(model) self.__search.textChanged.connect(proxy.setFilterFixedString) view.setModel(proxy) view.selectionModel().selectionChanged.connect(self.__update_details) header = self.__view.header() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.__details = QTextBrowser( frameShape=QTextBrowser.NoFrame, readOnly=True, lineWrapMode=QTextBrowser.WidgetWidth, openExternalLinks=True, ) self.__details.setWordWrapMode(QTextOption.WordWrap) palette = QPalette(self.palette()) palette.setColor(QPalette.Base, Qt.transparent) self.__details.setPalette(palette) self.layout().addWidget(self.__details) def set_items(self, items): self.__items = items model = self.__model model.clear() model.setHorizontalHeaderLabels(["", "Name", "Version", "Action"]) for item in items: if isinstance(item, Installed): installed = True ins, dist = item name = dist.project_name summary = get_dist_meta(dist).get("Summary", "") version = ins.version if ins is not None else dist.version else: installed = False (ins, ) = item dist = None name = ins.name summary = ins.summary version = ins.version updatable = is_updatable(item) item1 = QStandardItem() item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | (Qt.ItemIsTristate if updatable else 0)) if installed and updatable: item1.setCheckState(Qt.PartiallyChecked) elif installed: item1.setCheckState(Qt.Checked) else: item1.setCheckState(Qt.Unchecked) item2 = QStandardItem(cleanup(name)) item2.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) item2.setToolTip(summary) item2.setData(item, Qt.UserRole) if updatable: version = "{} < {}".format(dist.version, ins.version) item3 = QStandardItem(version) item3.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) item4 = QStandardItem() item4.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) model.appendRow([item1, item2, item3, item4]) self.__view.resizeColumnToContents(0) self.__view.setColumnWidth(1, max(150, self.__view.sizeHintForColumn(1))) self.__view.setColumnWidth(2, max(150, self.__view.sizeHintForColumn(2))) if self.__items: self.__view.selectionModel().select( self.__view.model().index(0, 0), QItemSelectionModel.Select | QItemSelectionModel.Rows) def item_state(self): steps = [] for i, item in enumerate(self.__items): modelitem = self.__model.item(i, 0) state = modelitem.checkState() if modelitem.flags() & Qt.ItemIsTristate and state == Qt.Checked: steps.append((Upgrade, item)) elif isinstance(item, Available) and state == Qt.Checked: steps.append((Install, item)) elif isinstance(item, Installed) and state == Qt.Unchecked: steps.append((Uninstall, item)) return steps def __selected_row(self): indices = self.__view.selectedIndexes() if indices: proxy = self.__view.model() indices = [proxy.mapToSource(index) for index in indices] return indices[0].row() else: return -1 def set_install_projects(self, names): """Mark for installation the add-ons that match any of names""" model = self.__model for row in range(model.rowCount()): item = model.item(row, 1) if item.text() in names: model.item(row, 0).setCheckState(Qt.Checked) def __data_changed(self, topleft, bottomright): rows = range(topleft.row(), bottomright.row() + 1) for i in rows: modelitem = self.__model.item(i, 0) actionitem = self.__model.item(i, 3) item = self.__items[i] state = modelitem.checkState() flags = modelitem.flags() if flags & Qt.ItemIsTristate and state == Qt.Checked: actionitem.setText("Update") elif isinstance(item, Available) and state == Qt.Checked: actionitem.setText("Install") elif isinstance(item, Installed) and state == Qt.Unchecked: actionitem.setText("Uninstall") else: actionitem.setText("") self.statechanged.emit() def __update_details(self): index = self.__selected_row() if index == -1: self.__details.setText("") else: item = self.__model.item(index, 1) item = item.data(Qt.UserRole) assert isinstance(item, (Installed, Available)) # if isinstance(item, Available): # self.__installed_label.setText("") # self.__available_label.setText(str(item.available.version)) # elif item.installable is not None: # self.__installed_label.setText(str(item.local.version)) # self.__available_label.setText(str(item.available.version)) # else: # self.__installed_label.setText(str(item.local.version)) # self.__available_label.setText("") text = self._detailed_text(item) self.__details.setText(text) def _detailed_text(self, item): if isinstance(item, Installed): remote, dist = item if remote is None: meta = get_dist_meta(dist) description = meta.get("Description") or meta.get('Summary') else: description = remote.description else: description = item[0].description if docutils is not None: try: html = docutils.core.publish_string( trim(description), writer_name="html", settings_overrides={ "output-encoding": "utf-8", # "embed-stylesheet": False, # "stylesheet": [], # "stylesheet_path": [] }).decode("utf-8") except docutils.utils.SystemMessage: html = "<pre>{}<pre>".format(escape(description)) except Exception: html = "<pre>{}<pre>".format(escape(description)) else: html = "<pre>{}<pre>".format(escape(description)) return html def sizeHint(self): return QSize(480, 420)
class AddonManagerWidget(QWidget): stateChanged = Signal() def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.__items = [] # type: List[Item] self.setLayout(QVBoxLayout()) self.__header = QLabel( wordWrap=True, textFormat=Qt.RichText ) self.__search = QLineEdit( placeholderText=self.tr("Filter") ) self.tophlayout = topline = QHBoxLayout() topline.addWidget(self.__search) self.layout().addLayout(topline) self.__view = view = QTreeView( rootIsDecorated=False, editTriggers=QTreeView.NoEditTriggers, selectionMode=QTreeView.SingleSelection, alternatingRowColors=True ) view.setItemDelegateForColumn(0, TristateCheckItemDelegate(view)) self.layout().addWidget(view) self.__model = model = PluginsModel() model.dataChanged.connect(self.__data_changed) proxy = QSortFilterProxyModel( filterKeyColumn=1, filterCaseSensitivity=Qt.CaseInsensitive ) proxy.setSourceModel(model) self.__search.textChanged.connect(proxy.setFilterFixedString) view.setModel(proxy) view.selectionModel().selectionChanged.connect( self.__update_details ) header = self.__view.header() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.__details = QTextBrowser( frameShape=QTextBrowser.NoFrame, readOnly=True, lineWrapMode=QTextBrowser.WidgetWidth, openExternalLinks=True, ) self.__details.setWordWrapMode(QTextOption.WordWrap) palette = QPalette(self.palette()) palette.setColor(QPalette.Base, Qt.transparent) self.__details.setPalette(palette) self.layout().addWidget(self.__details) def setItems(self, items): # type: (List[Item]) -> None """ Set a list of items to display. Parameters ---------- items: List[Item] A list of :class:`Available` or :class:`Installed` """ self.__items = items model = self.__model model.setRowCount(0) for item in items: row = model.createRow(item) model.appendRow(row) model.sort(1) self.__view.resizeColumnToContents(0) self.__view.setColumnWidth( 1, max(150, self.__view.sizeHintForColumn(1))) self.__view.setColumnWidth( 2, max(150, self.__view.sizeHintForColumn(2))) if self.__items: self.__view.selectionModel().select( self.__view.model().index(0, 0), QItemSelectionModel.Select | QItemSelectionModel.Rows ) def items(self): # type: () -> List[Item] """ Return a list of items. Return ------ items: List[Item] """ return list(self.__items) def itemState(self): # type: () -> List['Action'] """ Return the current `items` state encoded as a list of actions to be performed. Return ------ actions : List['Action'] For every item that is has been changed in the GUI interface return a tuple of (command, item) where Ccmmand is one of `Install`, `Uninstall`, `Upgrade`. """ steps = [] for i in range(self.__model.rowCount()): modelitem = self.__model.item(i, 0) item = modelitem.data(Qt.UserRole) state = modelitem.checkState() if modelitem.flags() & Qt.ItemIsUserTristate and state == Qt.Checked: steps.append((Upgrade, item)) elif isinstance(item, Available) and state == Qt.Checked: steps.append((Install, item)) elif isinstance(item, Installed) and state == Qt.Unchecked: steps.append((Uninstall, item)) return steps def setItemState(self, steps): # type: (List['Action']) -> None """ Set the current state as a list of actions to perform. i.e. `w.setItemState([(Install, item1), (Uninstall, item2)])` will mark item1 for installation and item2 for uninstallation, all other items will be reset to their default state Parameters ---------- steps : List[Tuple[Command, Item]] State encoded as a list of commands. """ model = self.__model if model.rowCount() == 0: return for row in range(model.rowCount()): modelitem = model.item(row, 0) # type: QStandardItem item = modelitem.data(Qt.UserRole) # type: Item # Find the action command in the steps list for the item cmd = -1 for cmd_, item_ in steps: if item == item_: cmd = cmd_ break if isinstance(item, Available): modelitem.setCheckState( Qt.Checked if cmd == Install else Qt.Unchecked ) elif isinstance(item, Installed): if cmd == Upgrade: modelitem.setCheckState(Qt.Checked) elif cmd == Uninstall: modelitem.setCheckState(Qt.Unchecked) elif is_updatable(item): modelitem.setCheckState(Qt.PartiallyChecked) else: modelitem.setCheckState(Qt.Checked) else: assert False def __selected_row(self): indices = self.__view.selectedIndexes() if indices: proxy = self.__view.model() indices = [proxy.mapToSource(index) for index in indices] return indices[0].row() else: return -1 def __data_changed(self, topleft, bottomright): rows = range(topleft.row(), bottomright.row() + 1) for i in rows: modelitem = self.__model.item(i, 0) actionitem = self.__model.item(i, 3) item = modelitem.data(Qt.UserRole) state = modelitem.checkState() flags = modelitem.flags() if flags & Qt.ItemIsUserTristate and state == Qt.Checked: actionitem.setText("Update") elif isinstance(item, Available) and state == Qt.Checked: actionitem.setText("Install") elif isinstance(item, Installed) and state == Qt.Unchecked: actionitem.setText("Uninstall") else: actionitem.setText("") self.stateChanged.emit() def __update_details(self): index = self.__selected_row() if index == -1: self.__details.setText("") else: item = self.__model.item(index, 1) item = item.data(Qt.UserRole) assert isinstance(item, (Installed, Available)) text = self._detailed_text(item) self.__details.setText(text) def _detailed_text(self, item): # type: (Item) -> str if isinstance(item, Installed): remote, dist = item.installable, item.local if remote is None: meta = get_dist_meta(dist) description = meta.get("Description", "") or \ meta.get('Summary', "") else: description = remote.description else: description = item.installable.description try: html = docutils.core.publish_string( trim(description), writer_name="html", settings_overrides={ "output-encoding": "utf-8", } ).decode("utf-8") except docutils.utils.SystemMessage: html = "<pre>{}<pre>".format(escape(description)) except Exception: html = "<pre>{}<pre>".format(escape(description)) return html def sizeHint(self): return QSize(480, 420)
class AddonManagerWidget(QWidget): stateChanged = Signal() def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.__items = [] # type: List[Item] self.setLayout(QVBoxLayout()) self.__header = QLabel( wordWrap=True, textFormat=Qt.RichText ) self.__search = QLineEdit( placeholderText=self.tr("Filter") ) self.tophlayout = topline = QHBoxLayout() topline.addWidget(self.__search) self.layout().addLayout(topline) self.__view = view = QTreeView( rootIsDecorated=False, editTriggers=QTreeView.NoEditTriggers, selectionMode=QTreeView.SingleSelection, alternatingRowColors=True ) view.setItemDelegateForColumn(0, TristateCheckItemDelegate(view)) self.layout().addWidget(view) self.__model = model = PluginsModel() model.dataChanged.connect(self.__data_changed) proxy = QSortFilterProxyModel( filterKeyColumn=1, filterCaseSensitivity=Qt.CaseInsensitive ) proxy.setSourceModel(model) self.__search.textChanged.connect(proxy.setFilterFixedString) view.setModel(proxy) view.selectionModel().selectionChanged.connect( self.__update_details ) header = self.__view.header() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.__details = QTextBrowser( frameShape=QTextBrowser.NoFrame, readOnly=True, lineWrapMode=QTextBrowser.WidgetWidth, openExternalLinks=True, ) self.__details.setWordWrapMode(QTextOption.WordWrap) palette = QPalette(self.palette()) palette.setColor(QPalette.Base, Qt.transparent) self.__details.setPalette(palette) self.layout().addWidget(self.__details) def setItems(self, items): # type: (List[Item]) -> None """ Set a list of items to display. Parameters ---------- items: List[Item] A list of :class:`Available` or :class:`Installed` """ self.__items = items model = self.__model model.setRowCount(0) for item in items: row = model.createRow(item) model.appendRow(row) model.sort(1) self.__view.resizeColumnToContents(0) self.__view.setColumnWidth( 1, max(150, self.__view.sizeHintForColumn(1))) self.__view.setColumnWidth( 2, max(150, self.__view.sizeHintForColumn(2))) if self.__items: self.__view.selectionModel().select( self.__view.model().index(0, 0), QItemSelectionModel.Select | QItemSelectionModel.Rows ) def items(self): # type: () -> List[Item] """ Return a list of items. Return ------ items: List[Item] """ return list(self.__items) def itemState(self): # type: () -> List['Action'] """ Return the current `items` state encoded as a list of actions to be performed. Return ------ actions : List['Action'] For every item that is has been changed in the GUI interface return a tuple of (command, item) where Ccmmand is one of `Install`, `Uninstall`, `Upgrade`. """ return self.__model.itemState() def setItemState(self, steps): # type: (List['Action']) -> None """ Set the current state as a list of actions to perform. i.e. `w.setItemState([(Install, item1), (Uninstall, item2)])` will mark item1 for installation and item2 for uninstallation, all other items will be reset to their default state Parameters ---------- steps : List[Tuple[Command, Item]] State encoded as a list of commands. """ self.__model.setItemState(steps) def __selected_row(self): indices = self.__view.selectedIndexes() if indices: proxy = self.__view.model() indices = [proxy.mapToSource(index) for index in indices] return indices[0].row() else: return -1 def __data_changed(self, topleft, bottomright): self.stateChanged.emit() def __update_details(self): index = self.__selected_row() if index == -1: text = "" self.__details.setText("") else: item = self.__model.item(index, PluginsModel.StateColumn) text = item.data(DetailedText) if not isinstance(text, str): text = "" self.__details.setText(text) def sizeHint(self): return QSize(480, 420)
class AddonManagerWidget(QWidget): statechanged = Signal() def __init__(self, parent=None, **kwargs): super(AddonManagerWidget, self).__init__(parent, **kwargs) self.__items = [] self.setLayout(QVBoxLayout()) self.__header = QLabel( wordWrap=True, textFormat=Qt.RichText ) self.__search = QLineEdit( placeholderText=self.tr("Filter") ) self.tophlayout = topline = QHBoxLayout() topline.addWidget(self.__search) self.layout().addLayout(topline) self.__view = view = QTreeView( rootIsDecorated=False, editTriggers=QTreeView.NoEditTriggers, selectionMode=QTreeView.SingleSelection, alternatingRowColors=True ) self.__view.setItemDelegateForColumn(0, TristateCheckItemDelegate()) self.layout().addWidget(view) self.__model = model = QStandardItemModel() model.setHorizontalHeaderLabels(["", "Name", "Version", "Action"]) model.dataChanged.connect(self.__data_changed) self.__proxy = proxy = QSortFilterProxyModel( filterKeyColumn=1, filterCaseSensitivity=Qt.CaseInsensitive ) proxy.setSourceModel(model) self.__search.textChanged.connect(proxy.setFilterFixedString) view.setModel(proxy) view.selectionModel().selectionChanged.connect( self.__update_details ) header = self.__view.header() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.__details = QTextBrowser( frameShape=QTextBrowser.NoFrame, readOnly=True, lineWrapMode=QTextBrowser.WidgetWidth, openExternalLinks=True, ) self.__details.setWordWrapMode(QTextOption.WordWrap) palette = QPalette(self.palette()) palette.setColor(QPalette.Base, Qt.transparent) self.__details.setPalette(palette) self.layout().addWidget(self.__details) def set_items(self, items): # type: (List[Item]) -> None self.__items = items model = self.__model model.setRowCount(0) for item in items: if isinstance(item, Installed): installed = True ins, dist = item name = dist.project_name summary = get_dist_meta(dist).get("Summary", "") version = ins.version if ins is not None else dist.version else: installed = False (ins,) = item dist = None name = ins.name summary = ins.summary version = ins.version updatable = is_updatable(item) item1 = QStandardItem() item1.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | (Qt.ItemIsTristate if updatable else 0)) if installed and updatable: item1.setCheckState(Qt.PartiallyChecked) elif installed: item1.setCheckState(Qt.Checked) else: item1.setCheckState(Qt.Unchecked) item1.setData(item, Qt.UserRole) item2 = QStandardItem(cleanup(name)) item2.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) item2.setToolTip(summary) item2.setData(item, Qt.UserRole) if updatable: version = "{} < {}".format(dist.version, ins.version) item3 = QStandardItem(version) item3.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) item4 = QStandardItem() item4.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) model.appendRow([item1, item2, item3, item4]) model.sort(1) self.__view.resizeColumnToContents(0) self.__view.setColumnWidth( 1, max(150, self.__view.sizeHintForColumn(1))) self.__view.setColumnWidth( 2, max(150, self.__view.sizeHintForColumn(2))) if self.__items: self.__view.selectionModel().select( self.__view.model().index(0, 0), QItemSelectionModel.Select | QItemSelectionModel.Rows ) self.__proxy.sort(1) # sorting list of add-ons in alphabetical order def items(self): # type: () -> List[Item] return list(self.__items) def item_state(self): # type: () -> List['Action'] steps = [] for i in range(self.__model.rowCount()): modelitem = self.__model.item(i, 0) item = modelitem.data(Qt.UserRole) state = modelitem.checkState() if modelitem.flags() & Qt.ItemIsTristate and state == Qt.Checked: steps.append((Upgrade, item)) elif isinstance(item, Available) and state == Qt.Checked: steps.append((Install, item)) elif isinstance(item, Installed) and state == Qt.Unchecked: steps.append((Uninstall, item)) return steps def set_item_state(self, steps): # type: (List['Action']) -> None model = self.__model if model.rowCount() == 0: return for row in range(model.rowCount()): modelitem = model.item(row, 0) # type: QStandardItem item = modelitem.data(Qt.UserRole) # type: Item # Find the action command in the steps list for the item cmd = -1 for cmd_, item_ in steps: if item == item_: cmd = cmd_ break if isinstance(item, Available): modelitem.setCheckState( Qt.Checked if cmd == Install else Qt.Unchecked ) elif isinstance(item, Installed): if cmd == Upgrade: modelitem.setCheckState(Qt.Checked) elif cmd == Uninstall: modelitem.setCheckState(Qt.Unchecked) elif is_updatable(item): modelitem.setCheckState(Qt.PartiallyChecked) else: modelitem.setCheckState(Qt.Checked) else: assert False def __selected_row(self): indices = self.__view.selectedIndexes() if indices: proxy = self.__view.model() indices = [proxy.mapToSource(index) for index in indices] return indices[0].row() else: return -1 def set_install_projects(self, names): """Mark for installation the add-ons that match any of names""" model = self.__model for row in range(model.rowCount()): item = model.item(row, 1) if item.text() in names: model.item(row, 0).setCheckState(Qt.Checked) def __data_changed(self, topleft, bottomright): rows = range(topleft.row(), bottomright.row() + 1) for i in rows: modelitem = self.__model.item(i, 0) actionitem = self.__model.item(i, 3) item = modelitem.data(Qt.UserRole) state = modelitem.checkState() flags = modelitem.flags() if flags & Qt.ItemIsTristate and state == Qt.Checked: actionitem.setText("Update") elif isinstance(item, Available) and state == Qt.Checked: actionitem.setText("Install") elif isinstance(item, Installed) and state == Qt.Unchecked: actionitem.setText("Uninstall") else: actionitem.setText("") self.statechanged.emit() def __update_details(self): index = self.__selected_row() if index == -1: self.__details.setText("") else: item = self.__model.item(index, 1) item = item.data(Qt.UserRole) assert isinstance(item, (Installed, Available)) text = self._detailed_text(item) self.__details.setText(text) def _detailed_text(self, item): if isinstance(item, Installed): remote, dist = item if remote is None: meta = get_dist_meta(dist) description = meta.get("Description") or meta.get('Summary') else: description = remote.description else: description = item[0].description if docutils is not None: try: html = docutils.core.publish_string( trim(description), writer_name="html", settings_overrides={ "output-encoding": "utf-8", # "embed-stylesheet": False, # "stylesheet": [], # "stylesheet_path": [] } ).decode("utf-8") except docutils.utils.SystemMessage: html = "<pre>{}<pre>".format(escape(description)) except Exception: html = "<pre>{}<pre>".format(escape(description)) else: html = "<pre>{}<pre>".format(escape(description)) return html def sizeHint(self): return QSize(480, 420)
class AddonManagerWidget(QWidget): stateChanged = Signal() def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.__items = [] # type: List[Item] self.setLayout(QVBoxLayout()) self.__header = QLabel( wordWrap=True, textFormat=Qt.RichText ) self.__search = QLineEdit( placeholderText=self.tr("Filter") ) self.tophlayout = topline = QHBoxLayout() topline.addWidget(self.__search) self.layout().addLayout(topline) self.__view = view = QTreeView( rootIsDecorated=False, editTriggers=QTreeView.NoEditTriggers, selectionMode=QTreeView.SingleSelection, alternatingRowColors=True ) view.setItemDelegateForColumn(0, TristateCheckItemDelegate(view)) self.layout().addWidget(view) self.__model = model = PluginsModel() model.dataChanged.connect(self.__data_changed) proxy = QSortFilterProxyModel( filterKeyColumn=1, filterCaseSensitivity=Qt.CaseInsensitive ) proxy.setSourceModel(model) self.__search.textChanged.connect(proxy.setFilterFixedString) view.setModel(proxy) view.selectionModel().selectionChanged.connect( self.__update_details ) header = self.__view.header() header.setSectionResizeMode(0, QHeaderView.Fixed) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) self.__details = QTextBrowser( frameShape=QTextBrowser.NoFrame, readOnly=True, lineWrapMode=QTextBrowser.WidgetWidth, openExternalLinks=True, ) self.__details.setWordWrapMode(QTextOption.WordWrap) palette = QPalette(self.palette()) palette.setColor(QPalette.Base, Qt.transparent) self.__details.setPalette(palette) self.layout().addWidget(self.__details) def setItems(self, items): # type: (List[Item]) -> None """ Set a list of items to display. Parameters ---------- items: List[Item] A list of :class:`Available` or :class:`Installed` """ self.__items = items model = self.__model model.setRowCount(0) for item in items: row = model.createRow(item) model.appendRow(row) model.sort(1) self.__view.resizeColumnToContents(0) self.__view.setColumnWidth( 1, max(150, self.__view.sizeHintForColumn(1))) self.__view.setColumnWidth( 2, max(150, self.__view.sizeHintForColumn(2))) if self.__items: self.__view.selectionModel().select( self.__view.model().index(0, 0), QItemSelectionModel.Select | QItemSelectionModel.Rows ) def items(self): # type: () -> List[Item] """ Return a list of items. Return ------ items: List[Item] """ return list(self.__items) def itemState(self): # type: () -> List['Action'] """ Return the current `items` state encoded as a list of actions to be performed. Return ------ actions : List['Action'] For every item that is has been changed in the GUI interface return a tuple of (command, item) where Ccmmand is one of `Install`, `Uninstall`, `Upgrade`. """ steps = [] for i in range(self.__model.rowCount()): modelitem = self.__model.item(i, 0) item = modelitem.data(Qt.UserRole) state = modelitem.checkState() if modelitem.flags() & Qt.ItemIsUserTristate and state == Qt.Checked: steps.append((Upgrade, item)) elif isinstance(item, Available) and state == Qt.Checked: steps.append((Install, item)) elif isinstance(item, Installed) and state == Qt.Unchecked: steps.append((Uninstall, item)) return steps def setItemState(self, steps): # type: (List['Action']) -> None """ Set the current state as a list of actions to perform. i.e. `w.setItemState([(Install, item1), (Uninstall, item2)])` will mark item1 for installation and item2 for uninstallation, all other items will be reset to their default state Parameters ---------- steps : List[Tuple[Command, Item]] State encoded as a list of commands. """ model = self.__model if model.rowCount() == 0: return for row in range(model.rowCount()): modelitem = model.item(row, 0) # type: QStandardItem item = modelitem.data(Qt.UserRole) # type: Item # Find the action command in the steps list for the item cmd = None # type: Optional[Command] for cmd_, item_ in steps: if item == item_: cmd = cmd_ break if isinstance(item, Available): modelitem.setCheckState( Qt.Checked if cmd == Install else Qt.Unchecked ) elif isinstance(item, Installed): if cmd == Upgrade: modelitem.setCheckState(Qt.Checked) elif cmd == Uninstall: modelitem.setCheckState(Qt.Unchecked) elif is_updatable(item): modelitem.setCheckState(Qt.PartiallyChecked) else: modelitem.setCheckState(Qt.Checked) else: assert False def __selected_row(self): indices = self.__view.selectedIndexes() if indices: proxy = self.__view.model() indices = [proxy.mapToSource(index) for index in indices] return indices[0].row() else: return -1 def __data_changed(self, topleft, bottomright): rows = range(topleft.row(), bottomright.row() + 1) for i in rows: modelitem = self.__model.item(i, 0) actionitem = self.__model.item(i, 3) item = modelitem.data(Qt.UserRole) state = modelitem.checkState() flags = modelitem.flags() if flags & Qt.ItemIsUserTristate and state == Qt.Checked: actionitem.setText("Update") elif isinstance(item, Available) and state == Qt.Checked: actionitem.setText("Install") elif isinstance(item, Installed) and state == Qt.Unchecked: actionitem.setText("Uninstall") else: actionitem.setText("") self.stateChanged.emit() def __update_details(self): index = self.__selected_row() if index == -1: self.__details.setText("") else: item = self.__model.item(index, 1) item = item.data(Qt.UserRole) assert isinstance(item, (Installed, Available)) text = self._detailed_text(item) self.__details.setText(text) def _detailed_text(self, item): # type: (Item) -> str if isinstance(item, Installed): remote, dist = item.installable, item.local if remote is None: meta = get_dist_meta(dist) description = meta.get("Description", "") or \ meta.get('Summary', "") else: description = remote.description else: description = item.installable.description try: assert isinstance(description, str) html = docutils.core.publish_string( trim(description), writer_name="html", settings_overrides={ "output-encoding": "utf-8", } ).decode("utf-8") except docutils.utils.SystemMessage: html = "<pre>{}<pre>".format(escape(description)) except Exception: html = "<pre>{}<pre>".format(escape(description)) return html def sizeHint(self): return QSize(480, 420)