class DistributionSelector(QWidget): """Python distribution selector widget""" TITLE = 'Select a Python distribution path' # Signals after PyQt4 old SIGNAL removal selected_distribution = Signal(str) def __init__(self, parent): super(DistributionSelector, self).__init__(parent) self.browse_btn = None self.label = None self.line_edit = None self.setup_widget() def set_distribution(self, path): """Set distribution directory""" self.line_edit.setText(path) def setup_widget(self): """Setup workspace selector widget""" self.label = QLabel() self.line_edit = QLineEdit() self.line_edit.setAlignment(Qt.AlignRight) self.line_edit.setReadOnly(True) # self.line_edit.setDisabled(True) self.browse_btn = QPushButton(get_std_icon('DirOpenIcon'), "", self) self.browse_btn.setToolTip(self.TITLE) # PyQt4 old SIGNAL:self.connect(self.browse_btn, SIGNAL("clicked()"), # PyQt4 old SIGNAL: self.select_directory) self.browse_btn.clicked.connect(self.select_directory) layout = QHBoxLayout() layout.addWidget(self.label) layout.addWidget(self.line_edit) layout.addWidget(self.browse_btn) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) def select_directory(self): """Select directory""" basedir = to_text_string(self.line_edit.text()) if not osp.isdir(basedir): basedir = getcwd() while True: directory = getexistingdirectory(self, self.TITLE, basedir) if not directory: break if not utils.is_python_distribution(directory): QMessageBox.warning( self, self.TITLE, "The following directory is not a Python distribution.", QMessageBox.Ok) basedir = directory continue directory = osp.abspath(osp.normpath(directory)) self.set_distribution(directory) # PyQt4 old SIGNAL: self.emit(SIGNAL('selected_distribution(QString)'), directory) self.selected_distribution.emit(directory) break
class MacApplication(QApplication): """Subclass to be able to open external files with our Mac app""" open_external_file = Signal(str) def __init__(self, *args): QApplication.__init__(self, *args) def event(self, event): if event.type() == QEvent.FileOpen: fname = str(event.file()) # PyQt4 old SIGNAL: self.emit(SIGNAL('open_external_file(QString)'), fname) self.open_external_file.emit(fname) return QApplication.event(self, event)
class PackagesModel(QAbstractTableModel): # Signals after PyQt4 old SIGNAL removal dataChanged = Signal(QModelIndex, QModelIndex) def __init__(self): QAbstractTableModel.__init__(self) self.packages = [] self.checked = set() self.actions = {} def sortByName(self): self.packages = sorted(self.packages, key=lambda x: x.name) self.reset() def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled column = index.column() if column in (NAME, VERSION, ACTION, DESCRIPTION): return Qt.ItemFlags(QAbstractTableModel.flags(self, index)) else: return Qt.ItemFlags( QAbstractTableModel.flags(self, index) | Qt.ItemIsUserCheckable | Qt.ItemIsEditable) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or not (0 <= index.row() < len(self.packages)): return to_qvariant() package = self.packages[index.row()] column = index.column() if role == Qt.CheckStateRole and column == CHECK: return to_qvariant(package in self.checked) elif role == Qt.DisplayRole: if column == NAME: return to_qvariant(package.name) elif column == VERSION: return to_qvariant(package.version) elif column == ACTION: action = self.actions.get(package) if action is not None: return to_qvariant(action) elif column == DESCRIPTION: return to_qvariant(package.description) elif role == Qt.TextAlignmentRole: if column == ACTION: return to_qvariant(int(Qt.AlignRight | Qt.AlignVCenter)) else: return to_qvariant(int(Qt.AlignLeft | Qt.AlignVCenter)) elif role == Qt.BackgroundColorRole: if package in self.checked: color = QColor(Qt.darkGreen) color.setAlphaF(.1) return to_qvariant(color) else: color = QColor(Qt.lightGray) color.setAlphaF(.3) return to_qvariant(color) return to_qvariant() def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.TextAlignmentRole: if orientation == Qt.Horizontal: return to_qvariant(int(Qt.AlignHCenter | Qt.AlignVCenter)) return to_qvariant(int(Qt.AlignRight | Qt.AlignVCenter)) if role != Qt.DisplayRole: return to_qvariant() if orientation == Qt.Horizontal: if section == NAME: return to_qvariant("Name") elif section == VERSION: return to_qvariant("Version") elif section == ACTION: return to_qvariant("Action") elif section == DESCRIPTION: return to_qvariant("Description") return to_qvariant() def rowCount(self, index=QModelIndex()): return len(self.packages) def columnCount(self, index=QModelIndex()): return len(COLUMNS) def setData(self, index, value, role=Qt.EditRole): if index.isValid() and 0 <= index.row() < len(self.packages)\ and role == Qt.CheckStateRole: package = self.packages[index.row()] if package in self.checked: self.checked.remove(package) else: self.checked.add(package) # PyQt4 old SIGNAL: self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), # PyQt4 old SIGNAL: index, index) self.dataChanged.emit(index, index) return True return False
class PackagesTable(QTableView): # Signals after PyQt4 old SIGNAL removal, to be emitted after package_added event package_added = Signal() def __init__(self, parent, process, winname): QTableView.__init__(self, parent) assert process in ('install', 'uninstall') self.process = process self.model = PackagesModel() self.setModel(self.model) self.winname = winname self.repair = False self.resizeColumnToContents(0) self.setAcceptDrops(process == 'install') if process == 'uninstall': self.hideColumn(0) self.distribution = None self.setSelectionBehavior(QAbstractItemView.SelectRows) self.verticalHeader().hide() self.setShowGrid(False) def reset_model(self): # self.model.reset() is deprecated in Qt5 self.model.beginResetModel() self.model.endResetModel() self.horizontalHeader().setStretchLastSection(True) for colnb in (ACTION, CHECK, NAME, VERSION): self.resizeColumnToContents(colnb) def get_selected_packages(self): """Return selected packages""" return [ pack for pack in self.model.packages if pack in self.model.checked ] def add_packages(self, fnames): """Add packages""" notsupported = [] notcompatible = [] dist = self.distribution for fname in fnames: bname = osp.basename(fname) try: package = wppm.Package(fname) if package.is_compatible_with(dist): self.add_package(package) else: notcompatible.append(bname) except NotImplementedError: notsupported.append(bname) # PyQt4 old SIGNAL: self.emit(SIGNAL('package_added()')) self.package_added.emit() if notsupported: QMessageBox.warning( self, "Warning", "The following packages filenaming are <b>not " "recognized</b> by %s:\n\n%s" % (self.winname, "<br>".join(notsupported)), QMessageBox.Ok) if notcompatible: QMessageBox.warning( self, "Warning", "The following packages " "are <b>not compatible</b> with " "Python <u>%s %dbit</u>:\n\n%s" % (dist.version, dist.architecture, "<br>".join(notcompatible)), QMessageBox.Ok) def add_package(self, package): for pack in self.model.packages: if pack.name == package.name: return self.model.packages.append(package) self.model.packages.sort(key=lambda x: x.name) self.model.checked.add(package) self.reset_model() def remove_package(self, package): self.model.packages = [ pack for pack in self.model.packages if pack.fname != package.fname ] if package in self.model.checked: self.model.checked.remove(package) if package in self.model.actions: self.model.actions.pop(package) self.reset_model() def refresh_distribution(self, dist): self.distribution = dist if self.process == 'install': for package in self.model.packages: pack = dist.find_package(package.name) if pack is None: action = INSTALL_ACTION elif pack.version == package.version: if self.repair: action = REPAIR_ACTION else: action = NO_REPAIR_ACTION else: action = UPGRADE_ACTION + pack.version self.model.actions[package] = action else: self.model.packages = self.distribution.get_installed_packages() for package in self.model.packages: self.model.actions[package] = NONE_ACTION self.reset_model() def select_all(self): allpk = set(self.model.packages) if self.model.checked == allpk: self.model.checked = set() else: self.model.checked = allpk self.model.reset() def dragMoveEvent(self, event): """Reimplement Qt method, just to avoid default drag'n drop implementation of QTableView to handle events""" event.acceptProposedAction() def dragEnterEvent(self, event): """Reimplement Qt method Inform Qt about the types of data that the widget accepts""" source = event.mimeData() if source.hasUrls() and mimedata2url(source): event.acceptProposedAction() def dropEvent(self, event): """Reimplement Qt method Unpack dropped data and handle it""" source = event.mimeData() fnames = [path for path in mimedata2url(source) if osp.isfile(path)] self.add_packages(fnames) event.acceptProposedAction()