class VariantDetailsWidget(QtGui.QWidget, ContextViewMixin): def __init__(self, context_model=None, parent=None): super(VariantDetailsWidget, self).__init__(parent) ContextViewMixin.__init__(self, context_model) self.variant = None self.edit = StreamableTextEdit() self.edit.setStyleSheet("font: 9pt 'Courier'") self.view_graph_btn = ViewGraphButton(context_model) self._update_graph_btn_visibility() btn_pane = create_pane([None, self.view_graph_btn], True, compact=True) create_pane([self.edit, btn_pane], False, compact=True, parent_widget=self) self.clear() def clear(self): self.edit.clear() self.setEnabled(False) def set_variant(self, variant): if variant == self.variant: return if variant is None: self.clear() else: self.setEnabled(True) self.edit.clear() variant.print_info(self.edit, skip_attributes=("changelog", )) self.edit.moveCursor(QtGui.QTextCursor.Start) self.view_graph_btn.set_variant(variant) self.variant = variant def _update_graph_btn_visibility(self): self.view_graph_btn.setVisible(bool(self.context())) def _contextChanged(self, flags=0): self._update_graph_btn_visibility()
class VariantDetailsWidget(QtGui.QWidget, ContextViewMixin): def __init__(self, context_model=None, parent=None): super(VariantDetailsWidget, self).__init__(parent) ContextViewMixin.__init__(self, context_model) self.variant = None self.edit = StreamableTextEdit() self.edit.setStyleSheet("font: 9pt 'Courier'") self.view_graph_btn = ViewGraphButton(context_model) self._update_graph_btn_visibility() btn_pane = create_pane([None, self.view_graph_btn], True, compact=True) create_pane([self.edit, btn_pane], False, compact=True, parent_widget=self) self.clear() def clear(self): self.edit.clear() self.setEnabled(False) def set_variant(self, variant): if variant == self.variant: return if variant is None: self.clear() else: self.setEnabled(True) self.edit.clear() variant.print_info(self.edit, skip_attributes=("changelog",)) self.edit.moveCursor(QtGui.QTextCursor.Start) self.view_graph_btn.set_variant(variant) self.variant = variant def _update_graph_btn_visibility(self): self.view_graph_btn.setVisible(bool(self.context())) def _contextChanged(self, flags=0): self._update_graph_btn_visibility()
class ResolveDialog(QtGui.QDialog, StoreSizeMixin): def __init__(self, context_model, parent=None, advanced=False): config_key = ("layout/window/advanced_resolve" if advanced else "layout/window/resolve") super(ResolveDialog, self).__init__(parent) StoreSizeMixin.__init__(self, app.config, config_key) self.setWindowTitle("Resolve") self.setContentsMargins(0, 0, 0, 0) self.context_model = context_model self.advanced = advanced self.resolver = None self.thread = None self.started = False self._finished = False self.busy_cursor = QtGui.QCursor(QtCore.Qt.WaitCursor) self.edit = StreamableTextEdit() self.edit.setStyleSheet("font: 9pt 'Courier'") self.bar = QtGui.QProgressBar() self.bar.setRange(0, 10) self.save_context_btn = QtGui.QPushButton("Save Context As...") self.graph_btn = QtGui.QPushButton("View Graph...") self.ok_btn = QtGui.QPushButton("Ok") self.start_again_btn = QtGui.QPushButton("Start Again") self.cancel_btn = QtGui.QPushButton("Cancel") self.resolve_btn = QtGui.QPushButton("Resolve") self.ok_btn.hide() self.graph_btn.hide() self.start_again_btn.hide() self.save_context_btn.hide() btn_pane = create_pane([None, self.save_context_btn, self.graph_btn, self.start_again_btn, self.ok_btn, self.cancel_btn, self.resolve_btn], not self.advanced) layout = QtGui.QVBoxLayout() layout.addWidget(self.bar) layout.addWidget(self.edit, 1) self.resolve_group = None self.max_fails_combo = None self.verbosity_combo = None self.show_package_loads_checkbox = None # this is solely to execute _start_resolve() as soon as the dialog opens self.timer = QtCore.QTimer() self.timer.setInterval(1) self.timer.setSingleShot(True) self.timer.timeout.connect(self._on_dialog_open) if self.advanced: self.resolve_group = QtGui.QGroupBox("resolve settings") label = QtGui.QLabel("maximum fails:") self.max_fails_combo = QtGui.QComboBox() self.max_fails_combo.setEditable(True) self.max_fails_combo.addItem("-") self.max_fails_combo.addItem("1") self.max_fails_combo.addItem("2") self.max_fails_combo.addItem("3") app.config.attach(self.max_fails_combo, "resolve/max_fails") max_fails_pane = create_pane([None, label, self.max_fails_combo], True) label = QtGui.QLabel("verbosity:") self.verbosity_combo = QtGui.QComboBox() self.verbosity_combo.addItem("0") self.verbosity_combo.addItem("1") self.verbosity_combo.addItem("2") app.config.attach(self.verbosity_combo, "resolve/verbosity") verbosity_pane = create_pane([None, label, self.verbosity_combo], True) self.show_package_loads_checkbox = QtGui.QCheckBox("show package loads") self.show_package_loads_checkbox.setLayoutDirection(QtCore.Qt.RightToLeft) app.config.attach(self.show_package_loads_checkbox, "resolve/show_package_loads") show_loads_pane = create_pane([None, self.show_package_loads_checkbox], True) self.timestamp_widget = TimestampWidget(self.context_model) context = self.context_model.context() if context and context.requested_timestamp: self.timestamp_widget.set_time(context.requested_timestamp) left_pane = create_pane([self.timestamp_widget, None], False, compact=True) right_pane = create_pane([max_fails_pane, verbosity_pane, show_loads_pane, None], False, compact=True) create_pane([left_pane, right_pane], True, parent_widget=self.resolve_group) pane = create_pane([self.resolve_group, None, btn_pane], True) self.cancel_btn.hide() layout.addWidget(pane) else: self.resolve_btn.hide() layout.addWidget(btn_pane) self.setLayout(layout) self.cancel_btn.clicked.connect(self._cancel_resolve) self.resolve_btn.clicked.connect(self._start_resolve) self.graph_btn.clicked.connect(self._view_graph) self.save_context_btn.clicked.connect(self._save_context) self.start_again_btn.clicked.connect(self._reset) self.ok_btn.clicked.connect(self.close) def resolve(self): # validate the request before opening dialog for req_str in self.context_model.request: try: Requirement(req_str) except Exception as e: title = "Invalid package request - %r" % req_str QtGui.QMessageBox.critical(self, title, str(e)) return self._reset() self.timer.start() self.exec_() if self.started: self.resolver.stop() if self.thread: self.thread.quit() self.thread.wait() return self.resolver.success() return False def get_context(self): if self.resolver: return self.resolver.context return None def reject(self): if self._finished or not self.started: super(ResolveDialog, self).reject() else: self._cancel_resolve() def closeEvent(self, event): if self._finished or not self.started: super(ResolveDialog, self).closeEvent(event) StoreSizeMixin.closeEvent(self, event) else: self._cancel_resolve() event.ignore() def _on_dialog_open(self): if not self.advanced: self._start_resolve() def _reset(self): self.setWindowTitle("Resolve") self.cancel_btn.setText("Cancel") self.cancel_btn.hide() self.ok_btn.hide() self.start_again_btn.hide() self.graph_btn.hide() self.save_context_btn.hide() self.resolve_btn.show() self._set_progress(False) if self.advanced: self.resolve_group.setEnabled(True) self.edit.clear() request_str = " ".join(str(x) for x in self.context_model.request) self._log("Resolving: %s...\n" % request_str) def _log(self, msg, color=None): if color: old_color = self.edit.textColor() self.edit.setTextColor(QtGui.QColor(color)) self.edit.append(msg) self.edit.moveCursor(QtGui.QTextCursor.End) if color: self.edit.setTextColor(old_color) def _start_resolve(self): max_fails = self._get_max_fails() if max_fails is None: return self.setWindowTitle("Resolving...") self.resolve_btn.hide() self.cancel_btn.show() self._set_progress(None) self.started = True verbosity = 0 show_package_loads = True timestamp = None if self.advanced: verbosity = app.config.get("resolve/verbosity") show_package_loads = app.config.get("resolve/show_package_loads") timestamp = self.timestamp_widget.datetime() if timestamp is not None: timestamp = timestamp.toTime_t() self.resolver = ResolveThread( self.context_model, verbosity=verbosity, max_fails=max_fails, timestamp=timestamp, show_package_loads=show_package_loads, buf=self.edit) if config.gui_threads: self.resolver.finished.connect(self._resolve_finished) self.thread = QtCore.QThread() self.resolver.moveToThread(self.thread) self.thread.started.connect(self.resolver.run) self.thread.start() else: self.resolver.run() self._resolve_finished() def _cancel_resolve(self): if self.started: self.cancel_btn.setText("Cancelling...") self.cancel_btn.setEnabled(False) self.resolver.stop() def _resolve_finished(self): self._finished = True self.cancel_btn.hide() self.ok_btn.show() self._set_progress(True) if self.advanced: self.start_again_btn.show() self.resolve_group.setEnabled(False) if self.resolver.error_message: msg = "\nTHE RESOLVE FAILED:\n%s" % self.resolver.error_message self._log(msg, "red") return if self.resolver.context.has_graph: self.graph_btn.setEnabled(True) self.save_context_btn.setEnabled(True) self.graph_btn.show() self.save_context_btn.show() if self.resolver.success(): if self.advanced: sbuf = StringIO.StringIO() self.resolver.context.print_info(buf=sbuf) msg = "\nTHE RESOLVE SUCCEEDED:\n\n" msg += sbuf.getvalue() self._log(msg, "green") else: self.close() else: msg = "\nTHE RESOLVE FAILED" desc = self.resolver.context.failure_description if desc: msg += ":\n%s" % desc self._log(msg, "red") def _get_max_fails(self): if self.max_fails_combo is None: return -1 txt = str(self.max_fails_combo.currentText()) if txt == "-": return -1 try: i = int(txt) except: i = -1 if i < 0: title = "Invalid max fails value" body = "Must be a positive integer." QtGui.QMessageBox.critical(self, title, body) self.max_fails_combo.setCurrentIndex(0) return None return i def _save_context(self): filepath = QtGui.QFileDialog.getSaveFileName( self, "Save Context", filter="Context files (*.rxt)") if filepath: self.resolver.context.save(filepath) self._log("\nSaved context to: %s" % filepath) def _view_graph(self): graph_str = self.resolver.context.graph(as_dot=True) view_graph(graph_str, self) def _set_progress(self, done=True): if done is True: self.bar.setMaximum(10) self.bar.setValue(10) elif done is False: self.bar.setMaximum(10) self.bar.setValue(0) elif done is None: self.bar.setRange(0, 0)
class ContextDetailsWidget(QtWidgets.QTabWidget, ContextViewMixin): def __init__(self, context_model=None, parent=None): super(ContextDetailsWidget, self).__init__(parent) ContextViewMixin.__init__(self, context_model) self.code_pending = True self.overview_edit = StreamableTextEdit() self.overview_edit.setStyleSheet("font: 12pt 'Courier'") self.graph_btn = ViewGraphButton(context_model) btn_pane = create_pane([None, self.graph_btn], True) overview_pane = create_pane([self.overview_edit, btn_pane], False) self.code_edit = SearchableTextEdit() self.code_edit.setStyleSheet("font: 12pt 'Courier'") self.code_combo = QtWidgets.QComboBox() # strip out 'sh' and 'csh', they only differ from bash and tcsh in shell # startup behaviour, which is irrelevant here code_types = set(get_shell_types()) - set([system.shell, "sh", "csh"]) code_types = [system.shell] + sorted(code_types) + ["python dict"] for code_type in code_types: self.code_combo.addItem(code_type) label = QtWidgets.QLabel("Format:") btn_pane = create_pane([None, label, self.code_combo], True) code_pane = create_pane([self.code_edit, btn_pane], False) self.environ_widget = ContextEnvironWidget() self.addTab(overview_pane, "overview") self.addTab(code_pane, "shell code") self.addTab(self.environ_widget, "environment") self.code_combo.currentIndexChanged.connect(self._update_code) self.currentChanged.connect(self._currentTabChanged) self.refresh() def refresh(self): self.overview_edit.clear() self.setCurrentIndex(0) context = self.context() if not context: self.setEnabled(False) return self.code_pending = True context.print_info(buf=self.overview_edit, verbosity=1) self.overview_edit.moveCursor(QtGui.QTextCursor.Start) self.environ_widget.set_context(context) def search(self): tab_index = self.currentIndex() if tab_index == 0: self.overview_edit.search() elif tab_index == 1: self.code_edit.search() def _contextChanged(self, flags=0): if not (flags & ContextModel.CONTEXT_CHANGED): return self.refresh() def _currentTabChanged(self, index): if index == 1 and self.code_pending: self._update_code() def _update_code(self): self.code_edit.clear() context = self.context() if not context: return shell = str(self.code_combo.currentText()) if shell == "python dict": environ = context.get_environ() code = pprint.pformat(environ) else: code = context.get_shell_code(shell=shell) self.code_edit.insertPlainText(code) self.code_edit.moveCursor(QtGui.QTextCursor.Start) self.code_pending = False
class ResolveDialog(QtGui.QDialog, StoreSizeMixin): def __init__(self, context_model, parent=None, advanced=False): config_key = ("layout/window/advanced_resolve" if advanced else "layout/window/resolve") super(ResolveDialog, self).__init__(parent) StoreSizeMixin.__init__(self, app.config, config_key) self.setWindowTitle("Resolve") self.setContentsMargins(0, 0, 0, 0) self.context_model = context_model self.advanced = advanced self.resolver = None self.thread = None self.started = False self._finished = False #self.busy_cursor = QtGui.QCursor(QtCore.Qt.WaitCursor) self.edit = StreamableTextEdit() self.edit.setStyleSheet("font: 9pt 'Courier'") self.bar = QtGui.QProgressBar() self.bar.setRange(0, 10) self.save_context_btn = QtGui.QPushButton("Save Context As...") self.graph_btn = QtGui.QPushButton("View Graph...") self.ok_btn = QtGui.QPushButton("Ok") self.start_again_btn = QtGui.QPushButton("Start Again") self.cancel_btn = QtGui.QPushButton("Cancel") self.resolve_btn = QtGui.QPushButton("Resolve") self.ok_btn.hide() self.graph_btn.hide() self.start_again_btn.hide() self.save_context_btn.hide() btn_pane = create_pane([None, self.save_context_btn, self.graph_btn, self.start_again_btn, self.ok_btn, self.cancel_btn, self.resolve_btn], not self.advanced) layout = QtGui.QVBoxLayout() layout.addWidget(self.bar) layout.addWidget(self.edit, 1) self.resolve_group = None self.max_fails_combo = None self.verbosity_combo = None self.show_package_loads_checkbox = None # this is solely to execute _start_resolve() as soon as the dialog opens self.timer = QtCore.QTimer() self.timer.setInterval(1) self.timer.setSingleShot(True) self.timer.timeout.connect(self._on_dialog_open) if self.advanced: self.resolve_group = QtGui.QGroupBox("resolve settings") label = QtGui.QLabel("maximum fails:") self.max_fails_combo = QtGui.QComboBox() self.max_fails_combo.setEditable(True) self.max_fails_combo.addItem("-") self.max_fails_combo.addItem("1") self.max_fails_combo.addItem("2") self.max_fails_combo.addItem("3") app.config.attach(self.max_fails_combo, "resolve/max_fails") max_fails_pane = create_pane([None, label, self.max_fails_combo], True) label = QtGui.QLabel("verbosity:") self.verbosity_combo = QtGui.QComboBox() self.verbosity_combo.addItem("0") self.verbosity_combo.addItem("1") self.verbosity_combo.addItem("2") app.config.attach(self.verbosity_combo, "resolve/verbosity") verbosity_pane = create_pane([None, label, self.verbosity_combo], True) self.show_package_loads_checkbox = QtGui.QCheckBox("show package loads") self.show_package_loads_checkbox.setLayoutDirection(QtCore.Qt.RightToLeft) app.config.attach(self.show_package_loads_checkbox, "resolve/show_package_loads") show_loads_pane = create_pane([None, self.show_package_loads_checkbox], True) self.timestamp_widget = TimestampWidget(self.context_model) context = self.context_model.context() if context and context.requested_timestamp: self.timestamp_widget.set_time(context.requested_timestamp) left_pane = create_pane([self.timestamp_widget, None], False, compact=True) right_pane = create_pane([max_fails_pane, verbosity_pane, show_loads_pane, None], False, compact=True) create_pane([left_pane, right_pane], True, parent_widget=self.resolve_group) pane = create_pane([self.resolve_group, None, btn_pane], True) self.cancel_btn.hide() layout.addWidget(pane) else: self.resolve_btn.hide() layout.addWidget(btn_pane) self.setLayout(layout) self.cancel_btn.clicked.connect(self._cancel_resolve) self.resolve_btn.clicked.connect(self._start_resolve) self.graph_btn.clicked.connect(self._view_graph) self.save_context_btn.clicked.connect(self._save_context) self.start_again_btn.clicked.connect(self._reset) self.ok_btn.clicked.connect(self.close) def resolve(self): # validate the request before opening dialog for req_str in self.context_model.request: try: Requirement(req_str) except Exception as e: title = "Invalid package request - %r" % req_str QtGui.QMessageBox.critical(self, title, str(e)) return self._reset() self.timer.start() self.exec_() if self.started: self.resolver.stop() if self.thread: self.thread.quit() self.thread.wait() return self.resolver.success() return False def get_context(self): if self.resolver: return self.resolver.context return None def reject(self): if self._finished or not self.started: super(ResolveDialog, self).reject() else: self._cancel_resolve() def closeEvent(self, event): if self._finished or not self.started: super(ResolveDialog, self).closeEvent(event) StoreSizeMixin.closeEvent(self, event) else: self._cancel_resolve() event.ignore() def _on_dialog_open(self): if not self.advanced: self._start_resolve() def _reset(self): self.setWindowTitle("Resolve") self.cancel_btn.setText("Cancel") self.cancel_btn.hide() self.ok_btn.hide() self.start_again_btn.hide() self.graph_btn.hide() self.save_context_btn.hide() self.resolve_btn.show() self._set_progress(False) if self.advanced: self.resolve_group.setEnabled(True) self.edit.clear() request_str = " ".join(str(x) for x in self.context_model.request) self._log("Resolving: %s...\n" % request_str) def _log(self, msg, color=None): if color: old_color = self.edit.textColor() self.edit.setTextColor(QtGui.QColor(color)) self.edit.append(msg) self.edit.moveCursor(QtGui.QTextCursor.End) if color: self.edit.setTextColor(old_color) def _start_resolve(self): max_fails = self._get_max_fails() if max_fails is None: return self.setWindowTitle("Resolving...") self.resolve_btn.hide() self.cancel_btn.show() self._set_progress(None) self.started = True verbosity = 0 show_package_loads = True timestamp = None if self.advanced: verbosity = app.config.get("resolve/verbosity") show_package_loads = app.config.get("resolve/show_package_loads") timestamp = self.timestamp_widget.datetime() if timestamp is not None: timestamp = timestamp.toTime_t() self.resolver = ResolveThread( self.context_model, verbosity=verbosity, max_fails=max_fails, timestamp=timestamp, show_package_loads=show_package_loads, buf=self.edit) if config.gui_threads: self.resolver.finished.connect(self._resolve_finished) self.thread = QtCore.QThread() self.resolver.moveToThread(self.thread) self.thread.started.connect(self.resolver.run) self.thread.start() else: self.resolver.run() self._resolve_finished() def _cancel_resolve(self): if self.started: self.cancel_btn.setText("Cancelling...") self.cancel_btn.setEnabled(False) self.resolver.stop() def _resolve_finished(self): self._finished = True self.cancel_btn.hide() self.ok_btn.show() self._set_progress(True) if self.advanced: self.start_again_btn.show() self.resolve_group.setEnabled(False) if self.resolver.error_message: msg = "\nTHE RESOLVE FAILED:\n%s" % self.resolver.error_message self._log(msg, "red") return if self.resolver.context.has_graph: self.graph_btn.setEnabled(True) self.save_context_btn.setEnabled(True) self.graph_btn.show() self.save_context_btn.show() if self.resolver.success(): if self.advanced: sbuf = StringIO.StringIO() self.resolver.context.print_info(buf=sbuf) msg = "\nTHE RESOLVE SUCCEEDED:\n\n" msg += sbuf.getvalue() self._log(msg, "green") else: self.close() else: msg = "\nTHE RESOLVE FAILED" desc = self.resolver.context.failure_description if desc: msg += ":\n%s" % desc self._log(msg, "red") def _get_max_fails(self): if self.max_fails_combo is None: return -1 txt = str(self.max_fails_combo.currentText()) if txt == "-": return -1 try: i = int(txt) except: i = -1 if i < 0: title = "Invalid max fails value" body = "Must be a positive integer." QtGui.QMessageBox.critical(self, title, body) self.max_fails_combo.setCurrentIndex(0) return None return i def _save_context(self): filepath = QtGui.QFileDialog.getSaveFileName( self, "Save Context", filter="Context files (*.rxt)") if filepath: self.resolver.context.save(filepath) self._log("\nSaved context to: %s" % filepath) def _view_graph(self): graph_str = self.resolver.context.graph(as_dot=True) view_graph(graph_str, self) def _set_progress(self, done=True): if done is True: self.bar.setMaximum(10) self.bar.setValue(10) elif done is False: self.bar.setMaximum(10) self.bar.setValue(0) elif done is None: self.bar.setRange(0, 0)
class ContextDetailsWidget(QtGui.QTabWidget, ContextViewMixin): def __init__(self, context_model=None, parent=None): super(ContextDetailsWidget, self).__init__(parent) ContextViewMixin.__init__(self, context_model) self.code_pending = True self.overview_edit = StreamableTextEdit() self.overview_edit.setStyleSheet("font: 9pt 'Courier'") self.graph_btn = ViewGraphButton(context_model) btn_pane = create_pane([None, self.graph_btn], True) overview_pane = create_pane([self.overview_edit, btn_pane], False) self.code_edit = SearchableTextEdit() self.code_edit.setStyleSheet("font: 9pt 'Courier'") self.code_combo = QtGui.QComboBox() # strip out 'sh' and 'csh', they only differ from bash and tcsh in shell # startup behaviour, which is irrelevant here code_types = set(get_shell_types()) - set([system.shell, "sh", "csh"]) code_types = [system.shell] + sorted(code_types) + ["python dict"] for code_type in code_types: self.code_combo.addItem(code_type) label = QtGui.QLabel("Format:") btn_pane = create_pane([None, label, self.code_combo], True) code_pane = create_pane([self.code_edit, btn_pane], False) self.environ_widget = ContextEnvironWidget() self.addTab(overview_pane, "overview") self.addTab(code_pane, "shell code") self.addTab(self.environ_widget, "environment") self.code_combo.currentIndexChanged.connect(self._update_code) self.currentChanged.connect(self._currentTabChanged) self.refresh() def refresh(self): self.overview_edit.clear() self.setCurrentIndex(0) context = self.context() if not context: self.setEnabled(False) return self.code_pending = True context.print_info(buf=self.overview_edit, verbosity=1) self.overview_edit.moveCursor(QtGui.QTextCursor.Start) self.environ_widget.set_context(context) def search(self): tab_index = self.currentIndex() if tab_index == 0: self.overview_edit.search() elif tab_index == 1: self.code_edit.search() def _contextChanged(self, flags=0): if not (flags & ContextModel.CONTEXT_CHANGED): return self.refresh() def _currentTabChanged(self, index): if index == 1 and self.code_pending: self._update_code() def _update_code(self): self.code_edit.clear() context = self.context() if not context: return shell = str(self.code_combo.currentText()) if shell == "python dict": environ = context.get_environ() code = pprint.pformat(environ) else: code = context.get_shell_code(shell=shell) self.code_edit.insertPlainText(code) self.code_edit.moveCursor(QtGui.QTextCursor.Start) self.code_pending = False