def create_controls(self): table = QTableView(self) self.model = PluginsModel(self.plugin_manager) table.setModel(self.model) h = table.horizontalHeader() h.setSectionResizeMode(QHeaderView.ResizeToContents) table.setHorizontalHeader(h) h = table.verticalHeader() h.setSectionResizeMode(QHeaderView.ResizeToContents) table.setVerticalHeader(h) self.table = table width = 80 for i in range(3): width += table.columnWidth(i) btns = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal) self.edit_btn = QPushButton("Edit") btns.addButton(self.edit_btn, QDialogButtonBox.ActionRole) self.edit_btn.clicked.connect(self.edit_plugin) btns.accepted.connect(self.accept) btns.rejected.connect(self.reject) vbox = QVBoxLayout() vbox.addWidget(table) vbox.addWidget(btns) self.setLayout(vbox) s = self.size() s.setHeight(table.rowHeight(0) * 10) s.setWidth(width) self.resize(s)
class ClosePackageManagerDialog(QDialog): """ """ def __init__(self, *args, **kwargs): super(ClosePackageManagerDialog, self).__init__(*args, **kwargs) self.label_icon = QLabel() self.label_about = QLabel('Conda is still busy.\n\n' 'Do you want to cancel the process?') self.button_ok = QPushButton('Yes') self.button_cancel = QPushButton('No') self.buttonbox = QDialogButtonBox(Qt.Horizontal) # Widget setup self.buttonbox.addButton(self.button_ok, QDialogButtonBox.ActionRole) self.buttonbox.addButton(self.button_cancel, QDialogButtonBox.ActionRole) # self.label_icon.setPixmap(QPixmap(images.ANACONDA_ICON_64_PATH)) self.setWindowTitle("Cancel Process") # Layouts h_layout = QHBoxLayout() h_layout.addWidget(self.label_icon, 0, Qt.AlignTop) h_layout.addSpacing(10) h_layout.addWidget(self.label_about) main_layout = QVBoxLayout() main_layout.addLayout(h_layout) main_layout.addSpacing(20) main_layout.addWidget(self.buttonbox) self.setLayout(main_layout) # Signals self.button_ok.clicked.connect(self.accept) self.button_cancel.clicked.connect(self.reject)
def _init_layout(self): self.setMinimumWidth(600) self.setMinimumHeight(400) dialog_buttons = QDialogButtonBox(QDialogButtonBox.Ok) dialog_buttons.accepted.connect(self.accept) self._copy_all_button = dialog_buttons.addButton( "Copy All", QDialogButtonBox.ActionRole) self._copy_all_button.clicked.connect(self._copy_all) self._follow_button = dialog_buttons.addButton( "Follow", QDialogButtonBox.ActionRole) self._follow_button.setCheckable(True) self._follow_button.toggled.connect(self._enable_follow_mode) self._enable_follow_mode(self._follow_mode) layout = QVBoxLayout(self) layout.addWidget(self._view) layout.addWidget(dialog_buttons)
def add_button_box(self, stdbtns): """Create dialog button box and add it to the dialog layout""" bbox = QDialogButtonBox(stdbtns) run_btn = bbox.addButton(_("Run"), QDialogButtonBox.AcceptRole) run_btn.clicked.connect(self.run_btn_clicked) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) btnlayout = QHBoxLayout() btnlayout.addStretch(1) btnlayout.addWidget(bbox) self.layout().addLayout(btnlayout)
def __init__(self, parent, history): super().__init__(parent=parent) self.setWindowTitle("History") layout = QVBoxLayout() text = QPlainTextEdit() font = QFont() font.setFamily("monospace") font.setStyleHint(QFont.Monospace) text.setFont(font) highlighter = PythonHighlighter(text.document()) # noqa: F841 text.setReadOnly(True) text.setPlainText(history) layout.addWidget(text) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) clipboardbutton = QPushButton("Copy to clipboard") buttonbox.addButton(clipboardbutton, QDialogButtonBox.ActionRole) clipboard = QGuiApplication.clipboard() clipboardbutton.clicked.connect( lambda: clipboard.setText(history + "\n")) layout.addWidget(buttonbox) self.setLayout(layout) buttonbox.accepted.connect(self.accept) self.resize(700, 500)
class ProjectDialog(QDialog): """Project creation dialog.""" sig_project_creation_requested = Signal(str, str, object) """ This signal is emitted to request the Projects plugin the creation of a project. Parameters ---------- project_path: str Location of project. project_type: str Type of project as defined by project types. project_packages: object Package to install. Currently not in use. """ def __init__(self, parent, project_types): """Project creation dialog.""" super(ProjectDialog, self).__init__(parent=parent) self.plugin = parent self._project_types = project_types self.project_data = {} # Variables current_python_version = '.'.join([to_text_string(sys.version_info[0]), to_text_string(sys.version_info[1])]) python_versions = ['2.7', '3.4', '3.5'] if current_python_version not in python_versions: python_versions.append(current_python_version) python_versions = sorted(python_versions) self.project_name = None self.location = get_home_dir() # Widgets self.groupbox = QGroupBox() self.radio_new_dir = QRadioButton(_("New directory")) self.radio_from_dir = QRadioButton(_("Existing directory")) self.label_project_name = QLabel(_('Project name')) self.label_location = QLabel(_('Location')) self.label_project_type = QLabel(_('Project type')) self.label_python_version = QLabel(_('Python version')) self.text_project_name = QLineEdit() self.text_location = QLineEdit(get_home_dir()) self.combo_project_type = QComboBox() self.combo_python_version = QComboBox() self.label_information = QLabel("") self.button_select_location = QToolButton() self.button_cancel = QPushButton(_('Cancel')) self.button_create = QPushButton(_('Create')) self.bbox = QDialogButtonBox(Qt.Horizontal) self.bbox.addButton(self.button_cancel, QDialogButtonBox.ActionRole) self.bbox.addButton(self.button_create, QDialogButtonBox.ActionRole) # Widget setup self.combo_python_version.addItems(python_versions) self.radio_new_dir.setChecked(True) self.text_location.setEnabled(True) self.text_location.setReadOnly(True) self.button_select_location.setIcon(get_std_icon('DirOpenIcon')) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) self.button_create.setEnabled(False) for (id_, name) in [(pt_id, pt.get_name()) for pt_id, pt in project_types.items()]: self.combo_project_type.addItem(name, id_) self.combo_python_version.setCurrentIndex( python_versions.index(current_python_version)) self.setWindowTitle(_('Create new project')) self.setFixedWidth(500) self.label_python_version.setVisible(False) self.combo_python_version.setVisible(False) # Layouts layout_top = QHBoxLayout() layout_top.addWidget(self.radio_new_dir) layout_top.addWidget(self.radio_from_dir) layout_top.addStretch(1) self.groupbox.setLayout(layout_top) layout_grid = QGridLayout() layout_grid.addWidget(self.label_project_name, 0, 0) layout_grid.addWidget(self.text_project_name, 0, 1, 1, 2) layout_grid.addWidget(self.label_location, 1, 0) layout_grid.addWidget(self.text_location, 1, 1) layout_grid.addWidget(self.button_select_location, 1, 2) layout_grid.addWidget(self.label_project_type, 2, 0) layout_grid.addWidget(self.combo_project_type, 2, 1, 1, 2) layout_grid.addWidget(self.label_python_version, 3, 0) layout_grid.addWidget(self.combo_python_version, 3, 1, 1, 2) layout_grid.addWidget(self.label_information, 4, 0, 1, 3) layout = QVBoxLayout() layout.addWidget(self.groupbox) layout.addSpacing(10) layout.addLayout(layout_grid) layout.addStretch() layout.addSpacing(20) layout.addWidget(self.bbox) self.setLayout(layout) # Signals and slots self.button_select_location.clicked.connect(self.select_location) self.button_create.clicked.connect(self.create_project) self.button_cancel.clicked.connect(self.close) self.radio_from_dir.clicked.connect(self.update_location) self.radio_new_dir.clicked.connect(self.update_location) self.text_project_name.textChanged.connect(self.update_location) def select_location(self): """Select directory.""" location = osp.normpath( getexistingdirectory( self, _("Select directory"), self.location, ) ) if location: if is_writable(location): self.location = location self.update_location() def update_location(self, text=''): """Update text of location.""" self.text_project_name.setEnabled(self.radio_new_dir.isChecked()) name = self.text_project_name.text().strip() if name and self.radio_new_dir.isChecked(): path = osp.join(self.location, name) self.button_create.setDisabled(os.path.isdir(path)) elif self.radio_from_dir.isChecked(): self.button_create.setEnabled(True) path = self.location else: self.button_create.setEnabled(False) path = self.location self.text_location.setText(path) # Validate name with the method from the currently selected project project_type_id = self.combo_project_type.currentData() validate_func = self._project_types[project_type_id].validate_name validated, msg = validate_func(path, name) msg = "" if validated else msg self.label_information.setText(msg) self.button_create.setEnabled(validated) def create_project(self): """Create project.""" self.project_data = { "root_path": self.text_location.text(), "project_type": self.combo_project_type.currentData(), } self.sig_project_creation_requested.emit( self.text_location.text(), self.combo_project_type.currentData(), [], ) self.accept()
class FormulaPane(BaseCodePane): def __init__(self, parent, title='', code='', editor_type=MxCodeEditor): super().__init__(parent, title, code, editor_type) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.saveButton = QPushButton(_("Save")) self.discardButton = QPushButton(_("Discard")) self.buttonBox.addButton(self.saveButton, QDialogButtonBox.AcceptRole) self.buttonBox.addButton( self.discardButton, QDialogButtonBox.RejectRole) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.bottomLayout = QHBoxLayout() self.bottomLayout.setContentsMargins(5, 0, 5, 5) self.status = QLabel() self.bottomLayout.addWidget(self.status) self.bottomLayout.addWidget(self.buttonBox) self.layout.addLayout(self.bottomLayout) self.clearCode() def setCode(self, source, reconnect=True): self.editor.set_text(source) self.resetModification() if reconnect: self.editor.modificationChanged.connect(self.activateActions) self.editor.setReadOnly(False) def clearCode(self): self.editor.set_text("") try: self.editor.modificationChanged.disconnect(self.activateActions) except TypeError: pass self.editor.setReadOnly(True) self.resetModification() def resetModification(self): self.editor.document().setModified(False) self.updateStatus() self.saveButton.setEnabled(False) self.discardButton.setEnabled(False) def updateStatus(self): modified = self.editor.document().isModified() if modified: self.status.setText("Modified") else: self.status.setText("") @Slot(bool) def activateActions(self, status): if status: self.saveButton.setEnabled(True) self.discardButton.setEnabled(True) self.updateStatus() def accept(self): self.parent().formula_updated() def reject(self): self.parent().shell.reload_mxproperty()
class ProjectDialog(QDialog): """Project creation dialog.""" # path, type, packages sig_project_creation_requested = Signal(object, object, object) def __init__(self, parent): """Project creation dialog.""" super(ProjectDialog, self).__init__(parent=parent) # Variables current_python_version = '.'.join([ to_text_string(sys.version_info[0]), to_text_string(sys.version_info[1]) ]) python_versions = ['2.7', '3.4', '3.5'] if current_python_version not in python_versions: python_versions.append(current_python_version) python_versions = sorted(python_versions) self.project_name = None self.location = get_home_dir() # Widgets self.groupbox = QGroupBox() self.radio_new_dir = QRadioButton(_("New directory")) self.radio_from_dir = QRadioButton(_("Existing directory")) self.label_project_name = QLabel(_('Project name')) self.label_location = QLabel(_('Location')) self.label_project_type = QLabel(_('Project type')) self.label_python_version = QLabel(_('Python version')) self.text_project_name = QLineEdit() self.text_location = QLineEdit(get_home_dir()) self.combo_project_type = QComboBox() self.combo_python_version = QComboBox() self.button_select_location = QToolButton() self.button_cancel = QPushButton(_('Cancel')) self.button_create = QPushButton(_('Create')) self.bbox = QDialogButtonBox(Qt.Horizontal) self.bbox.addButton(self.button_cancel, QDialogButtonBox.ActionRole) self.bbox.addButton(self.button_create, QDialogButtonBox.ActionRole) # Widget setup self.combo_python_version.addItems(python_versions) self.radio_new_dir.setChecked(True) self.text_location.setEnabled(True) self.text_location.setReadOnly(True) self.button_select_location.setIcon(get_std_icon('DirOpenIcon')) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) self.button_create.setEnabled(False) self.combo_project_type.addItems(self._get_project_types()) self.combo_python_version.setCurrentIndex( python_versions.index(current_python_version)) self.setWindowTitle(_('Create new project')) self.setFixedWidth(500) self.label_python_version.setVisible(False) self.combo_python_version.setVisible(False) # Layouts layout_top = QHBoxLayout() layout_top.addWidget(self.radio_new_dir) layout_top.addWidget(self.radio_from_dir) layout_top.addStretch(1) self.groupbox.setLayout(layout_top) layout_grid = QGridLayout() layout_grid.addWidget(self.label_project_name, 0, 0) layout_grid.addWidget(self.text_project_name, 0, 1, 1, 2) layout_grid.addWidget(self.label_location, 1, 0) layout_grid.addWidget(self.text_location, 1, 1) layout_grid.addWidget(self.button_select_location, 1, 2) layout_grid.addWidget(self.label_project_type, 2, 0) layout_grid.addWidget(self.combo_project_type, 2, 1, 1, 2) layout_grid.addWidget(self.label_python_version, 3, 0) layout_grid.addWidget(self.combo_python_version, 3, 1, 1, 2) layout = QVBoxLayout() layout.addWidget(self.groupbox) layout.addSpacing(10) layout.addLayout(layout_grid) layout.addStretch() layout.addSpacing(20) layout.addWidget(self.bbox) self.setLayout(layout) # Signals and slots self.button_select_location.clicked.connect(self.select_location) self.button_create.clicked.connect(self.create_project) self.button_cancel.clicked.connect(self.close) self.radio_from_dir.clicked.connect(self.update_location) self.radio_new_dir.clicked.connect(self.update_location) self.text_project_name.textChanged.connect(self.update_location) def _get_project_types(self): """Get all available project types.""" project_types = get_available_project_types() projects = [] for project in project_types: projects.append(project.PROJECT_TYPE_NAME) return projects def select_location(self): """Select directory.""" location = getexistingdirectory(self, _("Select directory"), self.location) if location: if is_writable(location): self.location = location self.update_location() def update_location(self, text=''): """Update text of location.""" self.text_project_name.setEnabled(self.radio_new_dir.isChecked()) name = self.text_project_name.text().strip() if name and self.radio_new_dir.isChecked(): path = osp.join(self.location, name) self.button_create.setDisabled(os.path.isdir(path)) elif self.radio_from_dir.isChecked(): self.button_create.setEnabled(True) path = self.location else: self.button_create.setEnabled(False) path = self.location self.text_location.setText(path) def create_project(self): """Create project.""" packages = [ 'python={0}'.format(self.combo_python_version.currentText()) ] self.sig_project_creation_requested.emit( self.text_location.text(), self.combo_project_type.currentText(), packages) self.accept()
class CondaPackagesWidget(QWidget): """ Conda Packages Widget. """ # Location of updated repo.json files from continuum/binstar CONDA_CONF_PATH = get_conf_path('repo') # Location of continuum/anaconda default repos shipped with conda-manager DATA_PATH = get_module_data_path() # file inside DATA_PATH with metadata for conda packages DATABASE_FILE = 'packages.ini' sig_worker_ready = Signal() sig_packages_ready = Signal() sig_environment_created = Signal(object, object) sig_environment_removed = Signal(object, object) sig_environment_cloned = Signal(object, object) sig_channels_updated = Signal(tuple, tuple) # channels, active_channels sig_process_cancelled = Signal() sig_next_focus = Signal() sig_packages_busy = Signal() def __init__(self, parent, name=None, prefix=None, channels=(), active_channels=(), conda_url='https://conda.anaconda.org', conda_api_url='https://api.anaconda.org', setup=True, data_directory=None, extra_metadata={}): super(CondaPackagesWidget, self).__init__(parent) # Check arguments: active channels, must be witbhin channels for ch in active_channels: if ch not in channels: raise Exception("'active_channels' must be also within " "'channels'") if data_directory is None: data_directory = self.CONDA_CONF_PATH self._parent = parent self._current_action_name = '' self._hide_widgets = False self._metadata = extra_metadata # From repo.continuum self._metadata_links = {} # Bundled metadata self.api = ManagerAPI() self.busy = False self.data_directory = data_directory self.conda_url = conda_url self.conda_api_url = conda_api_url self.name = name self.package_blacklist = [] self.prefix = prefix self.root_prefix = self.api.ROOT_PREFIX self.style_sheet = None self.message = '' self.apply_actions_dialog = None self.conda_errors = [] self.message_box_error = None self.token = None if channels: self._channels = channels self._active_channels = active_channels else: self._channels = self.api.conda_get_condarc_channels() self._active_channels = self._channels[:] # Widgets self.cancel_dialog = ClosePackageManagerDialog self.bbox = QDialogButtonBox(Qt.Horizontal) self.button_cancel = ButtonPackageCancel('Cancel') self.button_channels = ButtonPackageChannels(_('Channels')) self.button_ok = ButtonPackageOk(_('Ok')) self.button_update = ButtonPackageUpdate(_('Update package index...')) self.button_apply = ButtonPackageApply(_('Apply')) self.button_clear = ButtonPackageClear(_('Clear')) self.combobox_filter = DropdownPackageFilter(self) self.frame_top = FramePackageTop() self.frame_bottom = FramePackageTop() self.progress_bar = ProgressBarPackage(self) self.status_bar = LabelPackageStatus(self) self.table = TableCondaPackages(self) self.textbox_search = LineEditSearch(self) self.widgets = [ self.button_update, self.button_channels, self.combobox_filter, self.textbox_search, self.table, self.button_ok, self.button_apply, self.button_clear ] self.table_first_row = FirstRowWidget( widget_before=self.textbox_search) self.table_last_row = LastRowWidget(widgets_after=[ self.button_apply, self.button_clear, self.button_cancel, self.combobox_filter ]) # Widgets setup for button in [ self.button_cancel, self.button_apply, self.button_clear, self.button_ok, self.button_update, self.button_channels ]: button.setDefault(True) max_height = self.status_bar.fontMetrics().height() max_width = self.textbox_search.fontMetrics().width('M' * 23) self.bbox.addButton(self.button_ok, QDialogButtonBox.ActionRole) self.button_ok.setAutoDefault(True) self.button_ok.setDefault(True) self.button_ok.setMaximumSize(QSize(0, 0)) self.button_ok.setVisible(False) self.button_channels.setCheckable(True) self.combobox_filter.addItems([k for k in C.COMBOBOX_VALUES_ORDERED]) self.combobox_filter.setMinimumWidth(120) self.progress_bar.setMaximumHeight(max_height * 1.2) self.progress_bar.setMaximumWidth(max_height * 12) self.progress_bar.setTextVisible(False) self.progress_bar.setVisible(False) self.setMinimumSize(QSize(480, 300)) self.setWindowTitle(_("Conda Package Manager")) self.status_bar.setFixedHeight(max_height * 1.5) self.textbox_search.setMaximumWidth(max_width) self.textbox_search.setPlaceholderText('Search Packages') self.table_first_row.setMaximumHeight(0) self.table_last_row.setMaximumHeight(0) self.table_last_row.setVisible(False) self.table_first_row.setVisible(False) # Layout top_layout = QHBoxLayout() top_layout.addWidget(self.combobox_filter) top_layout.addWidget(self.button_channels) top_layout.addWidget(self.button_update) top_layout.addWidget(self.textbox_search) top_layout.addStretch() top_layout.setContentsMargins(0, 0, 0, 0) self.frame_top.setLayout(top_layout) middle_layout = QVBoxLayout() middle_layout.addWidget(self.table_first_row) middle_layout.addWidget(self.table) middle_layout.addWidget(self.table_last_row) bottom_layout = QHBoxLayout() bottom_layout.addWidget(self.status_bar) bottom_layout.addStretch() bottom_layout.addWidget(self.progress_bar) bottom_layout.addWidget(self.button_cancel) bottom_layout.addWidget(self.button_apply) bottom_layout.addWidget(self.button_clear) bottom_layout.setContentsMargins(0, 0, 0, 0) self.frame_bottom.setLayout(bottom_layout) layout = QVBoxLayout(self) layout.addWidget(self.frame_top) layout.addLayout(middle_layout) layout.addWidget(self.frame_bottom) self.setLayout(layout) self.setTabOrder(self.combobox_filter, self.button_channels) self.setTabOrder(self.button_channels, self.button_update) self.setTabOrder(self.button_update, self.textbox_search) self.setTabOrder(self.textbox_search, self.table_first_row) self.setTabOrder(self.table, self.table_last_row) self.setTabOrder(self.table_last_row, self.button_apply) self.setTabOrder(self.button_apply, self.button_clear) self.setTabOrder(self.button_clear, self.button_cancel) # Signals and slots self.api.sig_repodata_updated.connect(self._repodata_updated) self.combobox_filter.currentIndexChanged.connect(self.filter_package) self.button_apply.clicked.connect(self.apply_multiple_actions) self.button_clear.clicked.connect(self.clear_actions) self.button_cancel.clicked.connect(self.cancel_process) self.button_channels.clicked.connect(self.show_channels_dialog) self.button_update.clicked.connect(self.update_package_index) self.textbox_search.textChanged.connect(self.search_package) self.table.sig_conda_action_requested.connect(self._run_conda_action) self.table.sig_actions_updated.connect(self.update_actions) self.table.sig_pip_action_requested.connect(self._run_pip_action) self.table.sig_status_updated.connect(self.update_status) self.table.sig_next_focus.connect(self.table_last_row.handle_tab) self.table.sig_previous_focus.connect( lambda: self.table_first_row.widget_before.setFocus()) self.table_first_row.sig_enter_first.connect(self._handle_tab_focus) self.table_last_row.sig_enter_last.connect(self._handle_backtab_focus) # Setup self.api.client_set_domain(conda_api_url) self.api.set_data_directory(self.data_directory) self._load_bundled_metadata() self.update_actions(0) if setup: self.set_environment(name=name, prefix=prefix) self.setup() # --- Helpers # ------------------------------------------------------------------------- def _handle_tab_focus(self): self.table.setFocus() if self.table.proxy_model: index = self.table.proxy_model.index(0, 0) self.table.setCurrentIndex(index) def _handle_backtab_focus(self): self.table.setFocus() if self.table.proxy_model: row = self.table.proxy_model.rowCount() - 1 index = self.table.proxy_model.index(row, 0) self.table.setCurrentIndex(index) # --- Callbacks # ------------------------------------------------------------------------- def _load_bundled_metadata(self): """ """ logger.debug('') parser = cp.ConfigParser() db_file = CondaPackagesWidget.DATABASE_FILE with open(osp.join(self.DATA_PATH, db_file)) as f: parser.readfp(f) for name in parser.sections(): metadata = {} for key, data in parser.items(name): metadata[key] = data self._metadata_links[name] = metadata def _setup_packages(self, worker, data, error): """ """ if error: logger.error(error) else: logger.debug('') combobox_index = self.combobox_filter.currentIndex() status = C.PACKAGE_STATUS[combobox_index] packages = worker.packages # Remove blacklisted packages for package in self.package_blacklist: if package in packages: packages.pop(package) for i, row in enumerate(data): if package == data[i][C.COL_NAME]: data.pop(i) self.table.setup_model(packages, data, self._metadata_links) self.combobox_filter.setCurrentIndex(combobox_index) self.filter_package(status) if self._current_model_index: self.table.setCurrentIndex(self._current_model_index) self.table.verticalScrollBar().setValue(self._current_table_scroll) if error: self.update_status(error, False) self.sig_packages_ready.emit() self.table.setFocus() def get_logged_user_list_channels(self): channels = [] for ch in self._active_channels: if self.conda_url in ch and 'repo.continuum' not in ch: channel = ch.split('/')[-1] channels.append(channel) return channels def _prepare_model_data(self, worker=None, output=None, error=None): """ """ if error: logger.error(error) else: logger.debug('') packages, apps = output # worker = self.api.pip_list(prefix=self.prefix) # worker.sig_finished.connect(self._pip_list_ready) logins = self.get_logged_user_list_channels() worker = self.api.client_multi_packages(logins=logins, access='private') worker.sig_finished.connect(self._user_private_packages_ready) worker.packages = packages worker.apps = apps def _user_private_packages_ready(self, worker, output, error): if error: logger.error(error) else: logger.debug('') packages = worker.packages apps = worker.apps worker = self.api.pip_list(prefix=self.prefix) worker.sig_finished.connect(self._pip_list_ready) worker.packages = packages worker.apps = apps # private_packages = {} # if output: # all_private_packages = output # for item in all_private_packages: # name = item.get('name', '') # public = item.get('public', True) # package_types = item.get('package_types', []) # latest_version = item.get('latest_version', '') # if name and not public and 'conda' in package_types: # private_packages[name] = {'versions': item.get('versions', []), # 'app_entry': {}, # 'type': {}, # 'size': {}, # 'latest_version': latest_version, # } worker.private_packages = output def _pip_list_ready(self, worker, pip_packages, error): """ """ if error: logger.error(error) else: logger.debug('') packages = worker.packages private_packages = worker.private_packages linked_packages = self.api.conda_linked(prefix=self.prefix) data = self.api.client_prepare_packages_data(packages, linked_packages, pip_packages, private_packages) combobox_index = self.combobox_filter.currentIndex() status = C.PACKAGE_STATUS[combobox_index] # Remove blacklisted packages for package in self.package_blacklist: if package in packages: packages.pop(package) for i, row in enumerate(data): if package == data[i][C.COL_NAME]: data.pop(i) self.table.setup_model(packages, data, self._metadata_links) self.combobox_filter.setCurrentIndex(combobox_index) self.filter_package(status) if self._current_model_index: self.table.setCurrentIndex(self._current_model_index) self.table.verticalScrollBar().setValue(self._current_table_scroll) if error: self.update_status(str(error), False) self.sig_packages_ready.emit() self.table.setFocus() def _repodata_updated(self, paths): """ """ worker = self.api.client_load_repodata(paths, extra_data={}, metadata=self._metadata) worker.paths = paths worker.sig_finished.connect(self._prepare_model_data) def _metadata_updated(self, worker, path, error): """ """ if error: logger.error(error) else: logger.debug('') if path and osp.isfile(path): with open(path, 'r') as f: data = f.read() try: self._metadata = json.loads(data) except Exception: self._metadata = {} else: self._metadata = {} self.api.update_repodata(self._channels) # --- # ------------------------------------------------------------------------- def _run_multiple_actions(self, worker=None, output=None, error=None): """ """ logger.error(str(error)) if output and isinstance(output, dict): conda_error_type = output.get('error_type', None) conda_error = output.get('error', None) if conda_error_type or conda_error: self.conda_errors.append((conda_error_type, conda_error)) logger.error((conda_error_type, conda_error)) if self._multiple_process: status, func = self._multiple_process.popleft() self.update_status(status) worker = func() worker.sig_finished.connect(self._run_multiple_actions) worker.sig_partial.connect(self._partial_output_ready) else: if self.conda_errors and self.message_box_error: text = "The following errors occured:" error = '' for conda_error in self.conda_errors: error += str(conda_error[0]) + ':\n' error += str(conda_error[1]) + '\n\n' dlg = self.message_box_error(text=text, error=error, title='Conda process error') dlg.setMinimumWidth(400) dlg.exec_() self.update_status('', hide=False) self.setup() def _pip_process_ready(self, worker, output, error): """ """ if error is not None: status = _('there was an error') self.update_status(hide=False, message=status) else: self.update_status(hide=True) self.setup() def _conda_process_ready(self, worker, output, error): """ """ if error is not None: status = _('there was an error') self.update_status(hide=False, message=status) else: self.update_status(hide=True) conda_error = None conda_error_type = None if output and isinstance(output, dict): conda_error_type = output.get('error_type') conda_error = output.get('error') if conda_error_type or conda_error: logger.error((conda_error_type, conda_error)) dic = self._temporal_action_dic if dic['action'] == C.ACTION_CREATE: self.sig_environment_created.emit(conda_error, conda_error_type) elif dic['action'] == C.ACTION_CLONE: self.sig_environment_cloned.emit(conda_error, conda_error_type) elif dic['action'] == C.ACTION_REMOVE_ENV: self.sig_environment_removed.emit(conda_error, conda_error_type) self.setup() def _partial_output_ready(self, worker, output, error): """ """ message = None progress = (0, 0) if isinstance(output, dict): progress = (output.get('progress', None), output.get('maxval', None)) name = output.get('name', None) fetch = output.get('fetch', None) if fetch: message = "Downloading <b>{0}</b>...".format(fetch) if name: self._current_action_name = name message = "Linking <b>{0}</b>...".format(name) logger.debug(message) self.update_status(message, progress=progress) def _run_pip_action(self, package_name, action): """ DEPRECATED """ prefix = self.prefix if prefix == self.root_prefix: name = 'root' elif self.api.conda_environment_exists(prefix=prefix): name = osp.basename(prefix) else: name = prefix if action == C.ACTION_REMOVE: msgbox = QMessageBox.question( self, "Remove pip package: " "{0}".format(package_name), "Do you want to proceed?", QMessageBox.Yes | QMessageBox.No) if msgbox == QMessageBox.Yes: self.update_status() worker = self.api.pip_remove(prefix=self.prefix, pkgs=[package_name]) worker.sig_finished.connect(self._pip_process_ready) status = (_('Removing pip package <b>') + package_name + '</b>' + _(' from <i>') + name + '</i>') self.update_status(hide=True, message=status, progress=[0, 0]) def _run_conda_action(self, package_name, action, version, versions, packages_sizes): """ DEPRECATED """ prefix = self.prefix dlg = CondaPackageActionDialog(self, prefix, package_name, action, version, versions, packages_sizes, self._active_channels) if dlg.exec_(): dic = {} self.status = 'Processing' self.update_status(hide=True) self.repaint() ver1 = dlg.label_version.text() ver2 = dlg.combobox_version.currentText() pkg = u'{0}={1}{2}'.format(package_name, ver1, ver2) dep = dlg.checkbox.checkState() state = dlg.checkbox.isEnabled() dlg.close() dic['pkg'] = pkg dic['dep'] = not (dep == 0 and state) dic['action'] = None self._run_conda_process(action, dic) def _run_conda_process(self, action, dic): """ DEPRECTAED """ self._temporal_action_dic = dic # prefix = self.prefix # # if prefix == self.root_prefix: # name = 'root' # elif self.api.conda_environment_exists(prefix=prefix): # name = osp.basename(prefix) # else: # name = prefix if 'pkgs' in dic and 'dep' in dic: dep = dic['dep'] pkgs = dic['pkgs'] if not isinstance(pkgs, list): pkgs = [pkgs] # if (action == C.ACTION_INSTALL or action == C.ACTION_UPGRADE or # action == C.ACTION_DOWNGRADE): # status = _('Installing <b>') + dic['pkg'] + '</b>' # status = status + _(' into <i>') + name + '</i>' # worker = self.api.conda_install(prefix=prefix, pkgs=pkgs, dep=dep, # channels=self._active_channels) # elif action == C.ACTION_REMOVE: # status = (_('Removing <b>') + dic['pkg'] + '</b>' + # _(' from <i>') + name + '</i>') # worker = self.api.conda_remove(pkgs[0], prefix=prefix) # --- Environment management actions name = dic['name'] if action == C.ACTION_CREATE: status = _('Creating environment <b>') + name + '</b>' worker = self.api.conda_create(name=name, pkgs=pkgs, channels=self._active_channels) elif action == C.ACTION_CLONE: clone = dic['clone'] status = (_('Cloning ') + '<i>' + clone + _('</i> into <b>') + name + '</b>') worker = self.api.conda_clone(clone, name=name) elif action == C.ACTION_REMOVE_ENV: status = _('Removing environment <b>') + name + '</b>' worker = self.api.conda_remove(name=name, all_=True) worker.sig_finished.connect(self._conda_process_ready) worker.sig_partial.connect(self._partial_output_ready) self.update_status(hide=True, message=status, progress=None) self._temporal_action_dic = dic return worker # Public API # ------------------------------------------------------------------------- def prepare_model_data(self, packages, apps): """ """ logger.debug('') self._prepare_model_data(output=(packages, apps)) # These should be private methods.... def enable_widgets(self): """ """ self.table.hide_columns() def disable_widgets(self): """ """ self.table.hide_action_columns() def accept_channels_dialog(self): self.button_channels.setFocus() self.button_channels.toggle() def update_actions(self, number_of_actions): """ """ self.button_apply.setVisible(bool(number_of_actions)) self.button_clear.setVisible(bool(number_of_actions)) # --- Non UI API # ------------------------------------------------------------------------- def setup(self, check_updates=False, blacklist=[], metadata={}): """ Setup packages. Main triger method to download repodata, load repodata, prepare and updating the data model. Parameters ---------- check_updates : bool If `True`, checks that the latest repodata is available on the listed channels. If `False`, the data will be loaded from the downloaded files without checking for newer versions. blacklist: list of str List of conda package names to be excluded from the actual package manager view. """ self.sig_packages_busy.emit() if self.busy: logger.debug('Busy...') return else: logger.debug('') if blacklist: self.package_blacklist = [p.lower() for p in blacklist] if metadata: self._metadata = metadata self._current_model_index = self.table.currentIndex() self._current_table_scroll = self.table.verticalScrollBar().value() self.update_status('Updating package index', True) if check_updates: worker = self.api.update_metadata() worker.sig_finished.connect(self._metadata_updated) else: paths = self.api.repodata_files(channels=self._active_channels) self._repodata_updated(paths) def update_domains(self, anaconda_api_url=None, conda_url=None): """ """ logger.debug(str((anaconda_api_url, conda_url))) update = False if anaconda_api_url: if self.conda_api_url != anaconda_api_url: update = True self.conda_api_url = anaconda_api_url self.api.client_set_domain(anaconda_api_url) if conda_url: if self.conda_url != conda_url: update = True self.conda_url = conda_url if update: pass def set_environment(self, name=None, prefix=None): """ This does not update the package manager! """ logger.debug(str((name, prefix))) if prefix and self.api.conda_environment_exists(prefix=prefix): self.prefix = prefix elif name and self.api.conda_environment_exists(name=name): self.prefix = self.get_prefix_envname(name) else: self.prefix = self.root_prefix def set_token(self, token): self.token = token def update_channels(self, channels, active_channels): """ """ logger.debug(str((channels, active_channels))) if sorted(self._active_channels) != sorted(active_channels) or \ sorted(self._channels) != sorted(channels): self._channels = channels self._active_channels = active_channels self.sig_channels_updated.emit(tuple(channels), tuple(active_channels)) self.setup(check_updates=True) def update_style_sheet(self, style_sheet=None, extra_dialogs={}, palette={}): if style_sheet: self.style_sheet = style_sheet self.table.update_style_palette(palette=palette) self.textbox_search.update_style_sheet(style_sheet) self.setStyleSheet(style_sheet) if extra_dialogs: cancel_dialog = extra_dialogs.get('cancel_dialog', None) apply_actions_dialog = extra_dialogs.get('apply_actions_dialog', None) message_box_error = extra_dialogs.get('message_box_error', None) if cancel_dialog: self.cancel_dialog = cancel_dialog if apply_actions_dialog: self.apply_actions_dialog = apply_actions_dialog if message_box_error: self.message_box_error = message_box_error # --- UI API # ------------------------------------------------------------------------- def filter_package(self, value): """ """ self.table.filter_status_changed(value) def show_channels_dialog(self): """ Show the channels dialog. """ button_channels = self.button_channels self.dlg = DialogChannels(self, channels=self._channels, active_channels=self._active_channels, conda_url=self.conda_url) self.dlg.update_style_sheet(style_sheet=self.style_sheet) button_channels.setDisabled(True) self.dlg.sig_channels_updated.connect(self.update_channels) self.dlg.rejected.connect(lambda: button_channels.setEnabled(True)) self.dlg.rejected.connect(button_channels.toggle) self.dlg.rejected.connect(button_channels.setFocus) self.dlg.accepted.connect(self.accept_channels_dialog) geo_tl = button_channels.geometry().topLeft() tl = button_channels.parentWidget().mapToGlobal(geo_tl) x = tl.x() + 2 y = tl.y() + button_channels.height() self.dlg.move(x, y) self.dlg.show() self.dlg.button_add.setFocus() def update_package_index(self): """ """ self.setup(check_updates=True) def search_package(self, text): """ """ self.table.search_string_changed(text) def apply_multiple_actions(self): """ """ logger.debug('') self.conda_errors = [] prefix = self.prefix if prefix == self.root_prefix: name = 'root' elif self.api.conda_environment_exists(prefix=prefix): name = osp.basename(prefix) else: name = prefix actions = self.table.get_actions() if actions is None: return self._multiple_process = deque() pip_actions = actions[C.PIP_PACKAGE] conda_actions = actions[C.CONDA_PACKAGE] pip_remove = pip_actions.get(C.ACTION_REMOVE, []) conda_remove = conda_actions.get(C.ACTION_REMOVE, []) conda_install = conda_actions.get(C.ACTION_INSTALL, []) conda_upgrade = conda_actions.get(C.ACTION_UPGRADE, []) conda_downgrade = conda_actions.get(C.ACTION_DOWNGRADE, []) message = '' template_1 = '<li><b>{0}={1}</b></li>' template_2 = '<li><b>{0}: {1} -> {2}</b></li>' if pip_remove: temp = [ template_1.format(i['name'], i['version_to']) for i in pip_remove ] message += ('The following pip packages will be removed: ' '<ul>' + ''.join(temp) + '</ul>') if conda_remove: temp = [ template_1.format(i['name'], i['version_to']) for i in conda_remove ] message += ('<br>The following conda packages will be removed: ' '<ul>' + ''.join(temp) + '</ul>') if conda_install: temp = [ template_1.format(i['name'], i['version_to']) for i in conda_install ] message += ('<br>The following conda packages will be installed: ' '<ul>' + ''.join(temp) + '</ul>') if conda_downgrade: temp = [ template_2.format(i['name'], i['version_from'], i['version_to']) for i in conda_downgrade ] message += ('<br>The following conda packages will be downgraded: ' '<ul>' + ''.join(temp) + '</ul>') if conda_upgrade: temp = [ template_2.format(i['name'], i['version_from'], i['version_to']) for i in conda_upgrade ] message += ('<br>The following conda packages will be upgraded: ' '<ul>' + ''.join(temp) + '</ul>') message += '<br>' if self.apply_actions_dialog: dlg = self.apply_actions_dialog(message, parent=self) dlg.update_style_sheet(style_sheet=self.style_sheet) reply = dlg.exec_() else: reply = QMessageBox.question(self, 'Proceed with the following actions?', message, buttons=QMessageBox.Ok | QMessageBox.Cancel) if reply: # Pip remove for pkg in pip_remove: status = (_('Removing pip package <b>') + pkg['name'] + '</b>' + _(' from <i>') + name + '</i>') pkgs = [pkg['name']] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.pip_remove(prefix=prefix, pkgs=pkgs) self._multiple_process.append([status, trigger()]) # Process conda actions if conda_remove: status = (_('Removing conda packages <b>') + '</b>' + _(' from <i>') + name + '</i>') pkgs = [i['name'] for i in conda_remove] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_remove(pkgs=pkgs, prefix=prefix) self._multiple_process.append([status, trigger()]) if conda_install: pkgs = [ '{0}={1}'.format(i['name'], i['version_to']) for i in conda_install ] status = (_('Installing conda packages <b>') + '</b>' + _(' on <i>') + name + '</i>') def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_install(prefix=prefix, pkgs=pkgs, channels=self. _active_channels, token=self.token) self._multiple_process.append([status, trigger()]) # Conda downgrade if conda_downgrade: status = (_('Downgrading conda packages <b>') + '</b>' + _(' on <i>') + name + '</i>') pkgs = [ '{0}={1}'.format(i['name'], i['version_to']) for i in conda_downgrade ] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_install(prefix=prefix, pkgs=pkgs, channels=self. _active_channels, token=self.token) self._multiple_process.append([status, trigger()]) # Conda update if conda_upgrade: status = (_('Upgrading conda packages <b>') + '</b>' + _(' on <i>') + name + '</i>') pkgs = [ '{0}={1}'.format(i['name'], i['version_to']) for i in conda_upgrade ] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_install(prefix=prefix, pkgs=pkgs, channels=self. _active_channels, token=self.token) self._multiple_process.append([status, trigger()]) self._run_multiple_actions() def clear_actions(self): """ """ self.table.clear_actions() def cancel_process(self): """ Allow user to cancel an ongoing process. """ logger.debug(str('process canceled by user.')) if self.busy: dlg = self.cancel_dialog() reply = dlg.exec_() if reply: self.update_status(hide=False, message='Process cancelled') self.api.conda_terminate() self.api.download_requests_terminate() self.api.conda_clear_lock() self.table.clear_actions() self.sig_process_cancelled.emit() else: QDialog.reject(self) def update_status(self, message=None, hide=True, progress=None, env=False): """ Update status bar, progress bar display and widget visibility message : str Message to display in status bar. hide : bool Enable/Disable widgets. progress : [int, int] Show status bar progress. [0, 0] means spinning statusbar. """ self.busy = hide for widget in self.widgets: widget.setDisabled(hide) self.table.verticalScrollBar().setValue(self._current_table_scroll) self.button_apply.setVisible(False) self.button_clear.setVisible(False) self.progress_bar.setVisible(hide) self.button_cancel.setVisible(hide) if message is not None: self.message = message if self.prefix == self.root_prefix: short_env = 'root' # elif self.api.environment_exists(prefix=self.prefix): # short_env = osp.basename(self.prefix) else: short_env = self.prefix if env: self.message = '{0} (<b>{1}</b>)'.format( self.message, short_env, ) self.status_bar.setText(self.message) if progress is not None: current_progress, max_progress = 0, 0 if progress[1]: max_progress = progress[1] if progress[0]: current_progress = progress[0] self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(max_progress) self.progress_bar.setValue(current_progress) else: self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(0) # --- Conda helpers # ------------------------------------------------------------------------- def get_environment_prefix(self): """ Returns the active environment prefix. """ return self._prefix def get_environment_name(self): """ Returns the active environment name if it is located in the default conda environments directory, otherwise it returns the prefix. """ name = osp.basename(self._prefix) if not (name and self.api.environment_exists(name=name)): name = self._prefix return name def get_environments(self): """ Get a list of conda environments located in the default conda environments directory. """ return self.api.conda_get_envs() def get_prefix_envname(self, name): """ Returns the prefix for a given environment by name. """ return self.api.conda_get_prefix_envname(name) def get_package_versions(self, name): """ """ return self.table.source_model.get_package_versions(name) # --- Conda actions # ------------------------------------------------------------------------- def create_environment(self, name=None, prefix=None, packages=['python']): """ """ # If environment exists already? GUI should take care of this # BUT the api call should simply set that env as the env dic = {} dic['name'] = name dic['prefix'] = prefix dic['pkgs'] = packages dic['dep'] = True # Not really needed but for the moment! dic['action'] = C.ACTION_CREATE return self._run_conda_process(dic['action'], dic) def clone_environment(self, name=None, prefix=None, clone=None): dic = {} dic['name'] = name dic['prefix'] = prefix dic['clone'] = clone dic['pkgs'] = None dic['dep'] = True # Not really needed but for the moment! dic['action'] = C.ACTION_CLONE return self._run_conda_process(dic['action'], dic) def remove_environment(self, name=None, prefix=None): dic = {} dic['name'] = name dic['pkgs'] = None dic['dep'] = True # Not really needed but for the moment! dic['action'] = C.ACTION_REMOVE_ENV return self._run_conda_process(dic['action'], dic)
class MetadataDialog(QDialog): """Dialog for calibrating images. User can select from a list of catalogs (pulled from the active ensemble), preview, and calibrate the image data. """ default_parameters_dict = [{ 'name': 'Sample Name', 'label': 'sample_name', 'type': 'str', 'renamable': False, 'removable': False, }] _default_parameter_state = ScalableGroup(children=default_parameters_dict, name='blah', type='group').saveState() _qsettings_key = 'xicam.Acquire.metadata' _parameter_state = QSettings().value(_qsettings_key, defaultValue=_default_parameter_state) parameter = ScalableGroup(name='blah') parameter.restoreState(_parameter_state) def __init__(self, reserved: Iterable[str] = None, parent=None, window_flags=Qt.WindowFlags()): super(MetadataDialog, self).__init__(parent, window_flags) self.reserved = set(reserved or []) self.parameter_tree = pt.ParameterTree(showHeader=False) self.parameter_tree.setParameters(self.parameter, showTop=False) calibrate_button = QPushButton("&Acquire") self.buttons = QDialogButtonBox(Qt.Horizontal) # Add calibration button that accepts the dialog (closes with 1 status) self.buttons.addButton(calibrate_button, QDialogButtonBox.AcceptRole) # Add a cancel button that will reject the dialog (closes with 0 status) self.buttons.addButton(QDialogButtonBox.Cancel) self.buttons.rejected.connect(self.reject) self.buttons.accepted.connect(self.accept) outer_layout = QVBoxLayout() outer_layout.addWidget(self.parameter_tree) outer_layout.addWidget(self.buttons) outer_layout.setSpacing(0) outer_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(outer_layout) def get_metadata(self): return self.parameter.saveState('user')['children'] def accept(self): intersection = set(self.get_metadata().keys()).intersection( self.reserved) if intersection: msg.notifyMessage( f'The field name "{list(intersection)[0]}" is reserved and cannot be used.' ) else: super(MetadataDialog, self).accept() QSettings().setValue(self._qsettings_key, self.parameter.saveState())
class AuthenticationDialog(DialogBase): FORGOT_USERNAME_URL = 'https://anaconda.org/account/forgot_username' FORGOT_PASWORD_URL = 'https://anaconda.org/account/forgot_password' REGISTER_URL = 'https://anaconda.org' def __init__(self, api, parent=None): super(AuthenticationDialog, self).__init__(parent) self.api = api self._parent = parent self.token = None self.error = None self.tracker = GATracker() # Widgets self.label_username = QLabel('Username:'******'Password:'******'You can register ') self.label_signin_text = QLabel('<hr><br><b>Already a member? ' 'Sign in!</b><br>') # For styling purposes the label next to a ButtonLink is also a button # so they align adequately self.button_register_text = ButtonLabel('You can register by ' 'visiting the') self.button_register = ButtonLink('Anaconda Cloud') self.button_register_after_text = ButtonLabel('website.') self.label_information = QLabel(''' <strong>Anaconda Cloud</strong> is where packages, notebooks, and <br> environments are shared. It provides powerful <br> collaboration and package management for open <br> source and private projects.<br> ''') self.label_message = QLabel('') self.button_forgot_username = ButtonLink('I forgot my username') self.button_forgot_password = ButtonLink('I forgot my password') self.button_login = QPushButton('Login') self.button_cancel = ButtonCancel('Cancel') self.bbox = QDialogButtonBox(Qt.Horizontal) # Widgets setup self.bbox.addButton(self.button_cancel, QDialogButtonBox.RejectRole) self.bbox.addButton(self.button_login, QDialogButtonBox.AcceptRole) self.text_username.setAttribute(Qt.WA_MacShowFocusRect, False) self.text_password.setAttribute(Qt.WA_MacShowFocusRect, False) self.setMinimumWidth(260) self.setWindowTitle('Sign in') # This allows to completely style the dialog with css using the frame self.text_password.setEchoMode(QLineEdit.Password) self.label_message.setVisible(False) # Layout grid_layout = QGridLayout() grid_layout.addWidget(self.label_username, 0, 0) grid_layout.addWidget(self.text_username, 0, 1) grid_layout.addWidget(self.label_password, 1, 0) grid_layout.addWidget(self.text_password, 1, 1) main_layout = QVBoxLayout() main_layout.addWidget(self.label_information) register_layout = QHBoxLayout() register_layout.addWidget(self.button_register_text, 0) register_layout.addWidget(self.button_register, 0, Qt.AlignLeft) register_layout.addWidget(self.button_register_after_text, 0, Qt.AlignLeft) register_layout.addStretch() register_layout.setContentsMargins(0, 0, 0, 0) main_layout.addLayout(register_layout) main_layout.addWidget(self.label_signin_text) main_layout.addLayout(grid_layout) main_layout.addSpacing(5) main_layout.addWidget(self.label_message) main_layout.addWidget(self.button_forgot_username, 0, Qt.AlignRight) main_layout.addWidget(self.button_forgot_password, 0, Qt.AlignRight) main_layout.addSpacing(15) main_layout.addWidget(self.bbox) self.setLayout(main_layout) # Signals self.button_forgot_username.clicked.connect( lambda: self.open_url(self.FORGOT_USERNAME_URL)) self.button_forgot_password.clicked.connect( lambda: self.open_url(self.FORGOT_PASWORD_URL)) self.button_register.clicked.connect( lambda: self.open_url(self.REGISTER_URL)) self.text_username.textEdited.connect(self.check_text) self.text_password.textEdited.connect(self.check_text) self.button_login.clicked.connect(self.login) self.button_cancel.clicked.connect(self.reject) # Setup self.check_text() self.update_style_sheet() self.text_username.setFocus() @property def username(self): return self.text_username.text() def update_style_sheet(self, style_sheet=None): if style_sheet is None: style_sheet = load_style_sheet() self.setStyleSheet(style_sheet) def check_text(self): """ Check that `username` and `password` are not empty and disabel/enable buttons accordingly. """ username = self.text_username.text() password = self.text_password.text() if len(username) == 0 or len(password) == 0: self.button_login.setDisabled(True) else: self.button_login.setDisabled(False) def login(self): self.button_login.setEnabled(False) QApplication.setOverrideCursor(Qt.WaitCursor) self.label_message.setText('') worker = self.api.client_login(self.text_username.text(), self.text_password.text(), 'Anaconda Navigator', '') worker.sig_finished.connect(self._finished) def _finished(self, worker, output, error): """ Method called when the anaconda-client Api has finished a process that runs in a separate worker thread. """ token = output if token: self.token = token self.accept() elif error: username = self.text_username.text() bold_username = '******'.format(username) # The error might come in (error_message, http_error) format try: error_message = eval(str(error))[0] except Exception: error_message = str(error) error_message = error_message.lower().capitalize() error_message = error_message.split(', ')[0] error_text = '<i>{0}</i>'.format(error_message) error_text = error_text.replace(username, bold_username) self.label_message.setText(error_text) self.label_message.setVisible(True) if error_message: domain = self.api.client_domain() label = '{0}/{1}: {2}'.format(domain, username, error_message.lower()) self.tracker.track_event('authenticate', 'login failed', label=label) self.text_password.setFocus() self.text_password.selectAll() self.button_login.setDisabled(False) self.check_text() QApplication.restoreOverrideCursor() def open_url(self, url): self.tracker.track_event('content', 'click', url) QDesktopServices.openUrl(QUrl(url))
class AlarmTreeEditorDisplay(Display): def __init__(self, parent=None): super(AlarmTreeEditorDisplay, self).__init__(parent=parent) self.app = QApplication.instance() # set up the ui self.setup_ui() # allow add and remove row self.add_button.clicked.connect(self.insertChild) self.remove_button.clicked.connect(self.removeItem) self.remove_button.setEnabled(True) # connect save changes self.button_box.accepted.connect(self.save_property_changes) # upon tree view selection, change the item view self.tree_view.selectionModel().selectionChanged.connect( self.handle_selection) self.tree_view.tree_model.dataChanged.connect(self.item_change) self.file_dialog = QFileDialog() self.open_config_action = QAction("Open", self) self.open_config_action.triggered.connect(self.open_file) self.toolbar.addAction(self.open_config_action) self.save_config_action = QAction("Save", self) self.save_config_action.triggered.connect(self.save_configuration) self.toolbar.addAction(self.save_config_action) # update configuration name self.tree_label.editingFinished.connect(self._update_config_name) # default open size self.resize(800, 600) self.config_tool = PhoebusConfigTool() def setup_ui(self): self.main_layout = QGridLayout() self.setLayout(self.main_layout) # add toolbar self.toolbar = QToolBar() self.main_layout.setMenuBar(self.toolbar) # create the tree view layout and add/remove buttons self.tree_view_layout = QVBoxLayout() self.tree_view = PyDMAlarmTree(self, config_name="UNITITLED", edit_mode=True) self.tree_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.tree_view.setSelectionMode(QAbstractItemView.SingleSelection) self.tree_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.tree_view.setHeaderHidden(True) # Drag/drop self.tree_view.setDragDropMode(QAbstractItemView.InternalMove) self.tree_view.setDragEnabled(True) self.tree_view.setAcceptDrops(True) # view sizing self.tree_view.setColumnWidth(0, 160) self.tree_view.setColumnWidth(1, 160) self.tree_view.setColumnWidth(2, 160) # lable for tree view configuration_indicator = QLabel("Configuration:") self.tree_label = QLineEdit("Untitled") self.tree_label_layout = QHBoxLayout() self.tree_label_layout.addWidget(configuration_indicator) self.tree_label_layout.addWidget(self.tree_label) self.tree_view_layout.addLayout(self.tree_label_layout) self.tree_view_layout.addWidget(self.tree_view) # add/ remove buttons self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("New", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove", self) self.add_remove_layout.addWidget(self.remove_button) self.tree_view_layout.addLayout(self.add_remove_layout) # add the tree view to the window self.main_layout.addLayout(self.tree_view_layout, 0, 0) self.property_layout = QVBoxLayout() self.property_layout.addWidget(QLabel("Alarm Properties")) # crate property view self.property_data_layout = QStackedLayout() self.property_layout.addLayout(self.property_data_layout) self.property_widget_config = QWidget() self.property_widget_config.setWindowTitle("config") # create group widget self.property_widget_group = QWidget() self.property_widget_group.setWindowTitle("group") self.property_view_layout_group = QGridLayout() # add label self.label_edit_group = QLineEdit() self.label_label_group = QLabel("NAME") # add guidance self.guidance_edit_group = QLineEdit() self.guidance_label_group = QLabel("GUIDANCE") self.property_view_layout_group.addWidget(self.label_label_group, 1, 0) self.property_view_layout_group.addWidget(self.label_edit_group, 1, 1) self.property_view_layout_group.addWidget(self.guidance_label_group, 2, 0) self.property_view_layout_group.addWidget(self.guidance_edit_group, 2, 1) spacer = QSpacerItem(40, 200, QSizePolicy.Expanding, QSizePolicy.Minimum) self.property_view_layout_group.addItem(spacer, 3, 0) self.property_view_layout_group.addItem(spacer, 4, 0) self.property_view_layout_group.addItem(spacer, 5, 0) self.property_view_layout_group.addItem(spacer, 6, 0) self.property_view_layout_group.addItem(spacer, 7, 0) self.property_view_layout_group.addItem(spacer, 8, 0) # create pv widget self.property_widget_pv = QWidget() self.property_widget_pv.setWindowTitle("pv") self.property_view_layout_pv = QGridLayout() # add label self.label_edit_pv = QLineEdit() self.label_label_pv = QLabel("NAME") # add guidance self.guidance_edit_pv = QLineEdit() self.guidance_label_pv = QLabel("GUIDANCE") self.property_view_layout_pv.addWidget(self.label_label_pv, 1, 0) self.property_view_layout_pv.addWidget(self.label_edit_pv, 1, 1, 1, 3) self.property_view_layout_pv.addWidget(self.guidance_label_pv, 2, 0) self.property_view_layout_pv.addWidget(self.guidance_edit_pv, 2, 1, 1, 3) # add description self.description_edit = QLineEdit() self.description_label = QLabel("DESCRIPTION") self.property_view_layout_pv.addWidget(self.description_label, 3, 0) self.property_view_layout_pv.addWidget(self.description_edit, 3, 1, 1, 3) # add delay self.delay_edit = QLineEdit() self.delay_label = QLabel("DELAY") self.property_view_layout_pv.addWidget(self.delay_label, 4, 0) self.property_view_layout_pv.addWidget(self.delay_edit, 4, 1, 1, 3) self.delay_edit.setValidator(QIntValidator()) # add count self.count_edit = QLineEdit() self.count_label = QLabel("COUNT") self.property_view_layout_pv.addWidget(self.count_label, 5, 0) self.property_view_layout_pv.addWidget(self.count_edit, 5, 1, 1, 3) self.count_edit.setValidator(QIntValidator()) # add filter/force pv self.filter_edit = QLineEdit() self.filter_label = QLabel("ENABLING FILTER") self.property_view_layout_pv.addWidget(self.filter_label, 6, 0) self.property_view_layout_pv.addWidget(self.filter_edit, 6, 1, 1, 3) # enabled, latching, annunciating self.enabled_check = QCheckBox("ENABLED") self.annunciating_check = QCheckBox("ANNUNCIATING") self.latching_check = QCheckBox("LATCHING") self.property_view_layout_pv.addWidget(self.enabled_check, 7, 0) self.property_view_layout_pv.addWidget(self.annunciating_check, 7, 1) self.property_view_layout_pv.addWidget(self.latching_check, 7, 2) self.property_view_layout_pv.addItem(spacer, 8, 0) # create save button self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Save Properties", QDialogButtonBox.AcceptRole) self.property_layout.addWidget(self.button_box) # self.property_layout.addLayout(self.property_view_layout) self.property_widget_pv.setLayout(self.property_view_layout_pv) self.property_widget_group.setLayout(self.property_view_layout_group) self.property_data_layout.addWidget(self.property_widget_config) self.property_data_layout.addWidget(self.property_widget_pv) self.property_data_layout.addWidget(self.property_widget_group) self.main_layout.addLayout(self.property_layout, 0, 1) self.setWindowTitle("Alarm Tree Editor") self.tree_view.expandAll() def minimumSizeHint(self): # This is the default recommended size # for this screen return QSize(400, 200) def insertChild(self): index = self.tree_view.selectionModel().currentIndex() model = self.tree_view.model() if model.columnCount(index) == 0: if not model.insertColumn(0, index): return if not model.insertRow(0, index): return for column in range(model.columnCount(index)): child = model.index(0, column, index) model.set_data(child, label="NEW_ITEM", role=Qt.EditRole) def removeItem(self): index = self.tree_view.selectionModel().currentIndex() self.tree_view.model().removeRow(index.row(), index.parent()) @Slot() def save_property_changes(self): index = self.tree_view.selectionModel().currentIndex() item = self.tree_view.model().getItem(index) if item.is_group: guidance = self.guidance_edit_group.text() label = self.label_edit_group.text() else: guidance = self.guidance_edit_pv.text() label = self.label_edit_pv.text() self.tree_view.model().set_data( index, label=label, description=self.description_edit.text(), delay=self.delay_edit.text(), count=self.count_edit.text(), enabled=self.enabled_check.isChecked(), annunciating=self.annunciating_check.isChecked(), latching=self.latching_check.isChecked(), alarm_filter=self.filter_edit.text(), guidance=guidance, role=Qt.EditRole, ) @Slot() def handle_selection(self): self.remove_button.setEnabled( self.tree_view.selectionModel().hasSelection()) index = self.tree_view.selectionModel().currentIndex() item = self.tree_view.model().getItem(index) if item.is_group: self.guidance_edit_group.setText(item.guidance) self.label_edit_group.setText(item.label) else: self.guidance_edit_pv.setText(item.guidance) self.label_edit_pv.setText(item.label) if item.is_group: # black for configuration screen if not item.parent: self.property_data_layout.setCurrentWidget( self.property_widget_config) # otherwise show group screen and set all disables else: self.property_data_layout.setCurrentWidget( self.property_widget_group) self.description_edit.setEnabled(False) self.description_edit.setVisible(False) self.description_label.setVisible(False) self.count_edit.setEnabled(False) self.count_edit.setVisible(False) self.count_label.setVisible(False) self.delay_edit.setEnabled(False) self.delay_edit.setVisible(False) self.delay_label.setVisible(False) self.latching_check.setEnabled(False) self.latching_check.setVisible(False) self.annunciating_check.setEnabled(False) self.annunciating_check.setVisible(False) self.filter_edit.setEnabled(False) self.filter_edit.setVisible(False) self.filter_label.setVisible(False) # set pv enabled else: self.property_data_layout.setCurrentWidget(self.property_widget_pv) self.description_edit.setEnabled(True) self.description_edit.setVisible(True) self.description_label.setVisible(True) self.count_edit.setEnabled(True) self.count_edit.setVisible(True) self.count_label.setVisible(True) self.delay_edit.setEnabled(True) self.delay_edit.setVisible(True) self.delay_label.setVisible(True) self.latching_check.setEnabled(True) self.latching_check.setVisible(True) self.annunciating_check.setEnabled(True) self.annunciating_check.setVisible(True) self.filter_edit.setEnabled(True) self.filter_edit.setVisible(True) self.filter_label.setVisible(True) if item.enabled: self.enabled_check.setChecked(True) else: self.enabled_check.setChecked(False) if item.latching: self.latching_check.setChecked(True) else: self.latching_check.setChecked(False) if item.annunciating: self.annunciating_check.setChecked(True) else: self.annunciating_check.setChecked(False) @Slot() def item_change(self): index = self.tree_view.selectionModel().currentIndex() item = self.tree_view.model().getItem(index) if item.is_group: self.guidance_edit_group.setText(item.guidance) self.label_edit_group.setText(item.label) else: self.guidance_edit_pv.setText(item.guidance) self.label_edit_pv.setText(item.label) if item.is_group: if not item.parent(): self.property_data_layout.setCurrentWidget( self.property_widget_config) else: self.property_data_layout.setCurrentWidget( self.property_widget_group) self.description_edit.setEnabled(False) self.description_edit.setVisible(False) self.description_label.setVisible(False) self.count_edit.setEnabled(False) self.count_edit.setVisible(False) self.count_label.setVisible(False) self.delay_edit.setEnabled(False) self.delay_edit.setVisible(False) self.delay_label.setVisible(False) self.latching_check.setEnabled(False) self.latching_check.setVisible(False) self.annunciating_check.setEnabled(False) self.annunciating_check.setVisible(False) self.filter_edit.setEnabled(False) self.filter_edit.setVisible(False) self.filter_label.setVisible(False) else: self.delay_edit.setText(item.delay) self.count_edit.setText(item.count) if item.enabled: self.enabled_check.setChecked(True) else: self.enabled_check.setChecked(False) self.property_data_layout.setCurrentWidget(self.property_widget_pv) self.description_edit.setEnabled(True) self.description_edit.setVisible(True) self.description_label.setVisible(True) self.count_edit.setEnabled(True) self.count_edit.setVisible(True) self.count_label.setVisible(True) self.delay_edit.setEnabled(True) self.delay_edit.setVisible(True) self.delay_label.setVisible(True) self.latching_check.setEnabled(True) self.latching_check.setVisible(True) self.annunciating_check.setEnabled(True) self.annunciating_check.setVisible(True) self.filter_edit.setEnabled(True) self.filter_edit.setVisible(True) self.filter_label.setVisible(True) if item.latching: self.latching_check.setChecked(True) else: self.latching_check.setChecked(False) if item.annunciating: self.annunciating_check.setChecked(True) else: self.annunciating_check.setChecked(False) def ui_filepath(self): # No UI file is being used return None @Slot(bool) def open_file(self, checked): modifiers = QApplication.keyboardModifiers() try: curr_file = self.current_file() folder = os.path.dirname(curr_file) except Exception: folder = os.getcwd() filename = QFileDialog.getOpenFileName( self, "Open File...", folder, "XML (*.xml);; ALH Config (*.alhConfig)") filename = filename[0] if isinstance(filename, (list, tuple)) else filename if filename: filename = str(filename) # if alh file selected, open conversion prompt if filename[-9:] == "alhConfig": self.legacy_window = LegacyWindow(filename) self.legacy_window.exec_() if self.legacy_window.converted_filename: self.import_configuration( self.legacy_window.converted_filename) else: self.import_configuration(filename) def import_configuration(self, filename): nodes = self.config_tool.parse_config(filename) self.tree_view.model().import_hierarchy(nodes) self.tree_label.setText(self.tree_view.model()._nodes[0].label) @Slot() def save_configuration(self): modifiers = QApplication.keyboardModifiers() try: curr_file = self.current_file() folder = os.path.dirname(curr_file) except Exception: folder = os.getcwd() filename = QFileDialog.getSaveFileName(self, "Save File...", folder, "Configration files (*.xml)") filename = filename[0] if isinstance(filename, (list, tuple)) else filename self.config_tool.save_configuration(self.tree_view.model()._root_item, filename) def _update_config_name(self): name = self.tree_label.text() self.tree_view.model()._nodes[0].label = name def _import_legacy_file(self): convert_alh_to_phoebus()
class ServerInfoDialog(BaseDialog): """Dialog window showing information about notebook servers.""" def __init__(self, server_info, parent=None): """ Construct a RecoveryDialog. Parameters ---------- servers : list of ServerProcess Information to be displayed. This parameter is read only. parent : QWidget, optional Parent of the dialog window. The default is None. """ super().__init__(parent) self.servers = server_info self.setWindowTitle(_('Notebook server info')) self.layout = QVBoxLayout(self) self.formlayout = QFormLayout() self.layout.addLayout(self.formlayout) self.process_combo = QComboBox(self) self.process_combo.currentIndexChanged.connect(self.select_process) self.formlayout.addRow(_('Process ID:'), self.process_combo) self.dir_lineedit = QLineEdit(self) self.dir_lineedit.setReadOnly(True) self.formlayout.addRow(_('Notebook dir:'), self.dir_lineedit) self.interpreter_lineedit = QLineEdit(self) self.interpreter_lineedit.setReadOnly(True) self.formlayout.addRow(_('Python interpreter:'), self.interpreter_lineedit) self.state_lineedit = QLineEdit(self) self.state_lineedit.setReadOnly(True) self.formlayout.addRow(_('State:'), self.state_lineedit) self.log_textedit = QTextEdit(self) self.log_textedit.setReadOnly(True) self.layout.addWidget(self.log_textedit) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok, self) self.buttonbox.accepted.connect(self.accept) self.refresh_button = QPushButton(_('Refresh'), self) self.refresh_button.clicked.connect(self.refresh_data) self.buttonbox.addButton(self.refresh_button, QDialogButtonBox.ActionRole) self.layout.addWidget(self.buttonbox) self.refresh_data() def refresh_data(self): self.process_combo.clear() for server in self.servers: self.process_combo.addItem(str(server.process.processId())) self.select_process(0) def select_process(self, index): self.dir_lineedit.setText(self.servers[index].notebook_dir) self.interpreter_lineedit.setText(self.servers[index].interpreter) self.state_lineedit.setText( SERVER_STATE_DESCRIPTIONS[self.servers[index].state]) self.log_textedit.setPlainText(self.servers[index].output)
class BasePlotCurveEditorDialog(QDialog): """QDialog that is used in Qt Designer to edit the properties of the curves in a waveform plot. This dialog is shown when you double-click the plot, or when you right click it and choose 'edit curves'. This thing is mostly just a wrapper for a table view, with a couple buttons to add and remove curves, and a button to save the changes.""" TABLE_MODEL_CLASS = BasePlotCurvesModel def __init__(self, plot, parent=None): super(BasePlotCurveEditorDialog, self).__init__(parent) self.plot = plot self.setup_ui() self.table_model = self.TABLE_MODEL_CLASS(self.plot) self.table_view.setModel(self.table_model) self.table_model.plot = plot # self.table_view.resizeColumnsToContents() self.add_button.clicked.connect(self.addCurve) self.remove_button.clicked.connect(self.removeSelectedCurve) self.remove_button.setEnabled(False) self.table_view.selectionModel().selectionChanged.connect( self.handleSelectionChange) self.table_view.doubleClicked.connect(self.handleDoubleClick) self.resize(800, 300) def setup_ui(self): self.vertical_layout = QVBoxLayout(self) self.table_view = QTableView(self) self.table_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.table_view.setProperty("showDropIndicator", False) self.table_view.setDragDropOverwriteMode(False) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSortingEnabled(False) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.verticalHeader().setVisible(False) self.table_view.setColumnWidth(0, 160) self.table_view.setColumnWidth(1, 160) self.table_view.setColumnWidth(2, 160) self.vertical_layout.addWidget(self.table_view) self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("Add Curve", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove Curve", self) self.add_remove_layout.addWidget(self.remove_button) self.vertical_layout.addLayout(self.add_remove_layout) self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Done", QDialogButtonBox.AcceptRole) self.vertical_layout.addWidget(self.button_box) self.button_box.accepted.connect(self.saveChanges) self.button_box.rejected.connect(self.reject) self.setWindowTitle("Waveform Curve Editor") def setup_delegate_columns(self, index=2): symbol_delegate = SymbolColumnDelegate(self) self.table_view.setItemDelegateForColumn(index + 3, symbol_delegate) line_delegate = LineColumnDelegate(self) self.table_view.setItemDelegateForColumn(index + 1, line_delegate) color_delegate = ColorColumnDelegate(self) self.table_view.setItemDelegateForColumn(index, color_delegate) @Slot() def addCurve(self): self.table_model.append() @Slot() def removeSelectedCurve(self): self.table_model.removeAtIndex(self.table_view.currentIndex()) @Slot(QItemSelection, QItemSelection) def handleSelectionChange(self, selected, deselected): self.remove_button.setEnabled( self.table_view.selectionModel().hasSelection()) @Slot(QModelIndex) def handleDoubleClick(self, index): if self.table_model.needsColorDialog(index): # The table model returns a QBrush for BackgroundRole, not a QColor init_color = self.table_model.data(index, Qt.BackgroundRole).color() color = QColorDialog.getColor(init_color, self) if color.isValid(): self.table_model.setData(index, color, role=Qt.EditRole) @Slot() def saveChanges(self): formWindow = QDesignerFormWindowInterface.findFormWindow(self.plot) if formWindow: formWindow.cursor().setProperty("curves", self.plot.curves) self.accept()
class FirstSplash(DialogBase): """ Startup splash to display the first time that Navigator runs. """ def __init__(self, parent=None, analytics=True): super(FirstSplash, self).__init__() text = """ Thanks for installing Anaconda! Anaconda Navigator helps you easily start important Python applications and manage the packages in your local Anaconda installation. It also connects you to online resources for learning and engaging with the Python, SciPy, and PyData community. To help us improve Anaconda Navigator, fix bugs, and make it even easier for everyone to use Python, we gather anonymized usage information, just like most web browsers and mobile apps. To opt out of this, please uncheck below (You can always change this setting in the Preferences menu). """ # Widgets self.button_ok = QPushButton('Ok') self.button_ok_dont_show = QPushButton("Ok, and don't show again") self.buttonbox = QDialogButtonBox(Qt.Horizontal) self.checkbox_track = QCheckBox("Yes, I'd like to help improve " "Anaconda.") self.label_about = QLabel(text) self.label_icon = QLabel() # Widget setup self.buttonbox.addButton(self.button_ok, QDialogButtonBox.ActionRole) self.buttonbox.addButton(self.button_ok_dont_show, QDialogButtonBox.ActionRole) self.checkbox_track.setChecked(analytics) self.checkbox_track.setChecked( CONF.get('main', 'provide_analytics', True)) self.frame_title_bar.hide() self.label_icon.setPixmap(QPixmap(ANACONDA_NAVIGATOR_LOGO)) # Layouts layout = QVBoxLayout() layout.addWidget(self.label_icon, 0, Qt.AlignCenter) layout.addWidget(self.label_about) layout.addWidget(self.checkbox_track, 0, Qt.AlignCenter) layout.addSpacing(20) layout.addWidget(self.buttonbox) self.setLayout(layout) # Signals self.button_ok.clicked.connect(lambda: self.accept(show_startup=True)) self.button_ok_dont_show.clicked.connect( lambda: self.accept(show_startup=False)) def accept(self, show_startup): """ Qt override. """ CONF.set('main', 'provide_analytics', self.checkbox_track.checkState() == Qt.Checked) CONF.set('main', 'show_startup', show_startup) DialogBase.accept(self) def reject(self): """ Qt override. Force user to select one of the two options bellow and disalow canceling the dialog (pressing escape) """ pass
class CondaPackagesWidget(QWidget): """ Conda Packages Widget. """ # Location of updated repo.json files from continuum/binstar CONDA_CONF_PATH = get_conf_path('repo') # Location of continuum/anaconda default repos shipped with conda-manager DATA_PATH = get_module_data_path() # file inside DATA_PATH with metadata for conda packages DATABASE_FILE = 'packages.ini' sig_worker_ready = Signal() sig_packages_ready = Signal() sig_environment_created = Signal(object, object) sig_environment_removed = Signal(object, object) sig_environment_cloned = Signal(object, object) sig_channels_updated = Signal(tuple, tuple) # channels, active_channels sig_process_cancelled = Signal() sig_next_focus = Signal() sig_packages_busy = Signal() def __init__(self, parent, name=None, prefix=None, channels=(), active_channels=(), conda_url='https://conda.anaconda.org', conda_api_url='https://api.anaconda.org', setup=True, data_directory=None, extra_metadata={}): super(CondaPackagesWidget, self).__init__(parent) # Check arguments: active channels, must be witbhin channels for ch in active_channels: if ch not in channels: raise Exception("'active_channels' must be also within " "'channels'") if data_directory is None: data_directory = self.CONDA_CONF_PATH self._parent = parent self._current_action_name = '' self._hide_widgets = False self._metadata = extra_metadata # From repo.continuum self._metadata_links = {} # Bundled metadata self.api = ManagerAPI() self.busy = False self.data_directory = data_directory self.conda_url = conda_url self.conda_api_url = conda_api_url self.name = name self.package_blacklist = [] self.prefix = prefix self.root_prefix = self.api.ROOT_PREFIX self.style_sheet = None self.message = '' self.apply_actions_dialog = None self.conda_errors = [] self.message_box_error = None self.token = None if channels: self._channels = channels self._active_channels = active_channels else: self._channels = self.api.conda_get_condarc_channels() self._active_channels = self._channels[:] # Widgets self.cancel_dialog = ClosePackageManagerDialog self.bbox = QDialogButtonBox(Qt.Horizontal) self.button_cancel = ButtonPackageCancel('Cancel') self.button_channels = ButtonPackageChannels(_('Channels')) self.button_ok = ButtonPackageOk(_('Ok')) self.button_update = ButtonPackageUpdate(_('Update package index...')) self.button_apply = ButtonPackageApply(_('Apply')) self.button_clear = ButtonPackageClear(_('Clear')) self.combobox_filter = DropdownPackageFilter(self) self.frame_top = FramePackageTop() self.frame_bottom = FramePackageTop() self.progress_bar = ProgressBarPackage(self) self.status_bar = LabelPackageStatus(self) self.table = TableCondaPackages(self) self.textbox_search = LineEditSearch(self) self.widgets = [self.button_update, self.button_channels, self.combobox_filter, self.textbox_search, self.table, self.button_ok, self.button_apply, self.button_clear] self.table_first_row = FirstRowWidget( widget_before=self.textbox_search) self.table_last_row = LastRowWidget( widgets_after=[self.button_apply, self.button_clear, self.button_cancel, self.combobox_filter]) # Widgets setup for button in [self.button_cancel, self.button_apply, self.button_clear, self.button_ok, self.button_update, self.button_channels]: button.setDefault(True) max_height = self.status_bar.fontMetrics().height() max_width = self.textbox_search.fontMetrics().width('M'*23) self.bbox.addButton(self.button_ok, QDialogButtonBox.ActionRole) self.button_ok.setAutoDefault(True) self.button_ok.setDefault(True) self.button_ok.setMaximumSize(QSize(0, 0)) self.button_ok.setVisible(False) self.button_channels.setCheckable(True) self.combobox_filter.addItems([k for k in C.COMBOBOX_VALUES_ORDERED]) self.combobox_filter.setMinimumWidth(120) self.progress_bar.setMaximumHeight(max_height*1.2) self.progress_bar.setMaximumWidth(max_height*12) self.progress_bar.setTextVisible(False) self.progress_bar.setVisible(False) self.setMinimumSize(QSize(480, 300)) self.setWindowTitle(_("Conda Package Manager")) self.status_bar.setFixedHeight(max_height*1.5) self.textbox_search.setMaximumWidth(max_width) self.textbox_search.setPlaceholderText('Search Packages') self.table_first_row.setMaximumHeight(0) self.table_last_row.setMaximumHeight(0) self.table_last_row.setVisible(False) self.table_first_row.setVisible(False) # Layout top_layout = QHBoxLayout() top_layout.addWidget(self.combobox_filter) top_layout.addWidget(self.button_channels) top_layout.addWidget(self.button_update) top_layout.addWidget(self.textbox_search) top_layout.addStretch() top_layout.setContentsMargins(0, 0, 0, 0) self.frame_top.setLayout(top_layout) middle_layout = QVBoxLayout() middle_layout.addWidget(self.table_first_row) middle_layout.addWidget(self.table) middle_layout.addWidget(self.table_last_row) bottom_layout = QHBoxLayout() bottom_layout.addWidget(self.status_bar) bottom_layout.addStretch() bottom_layout.addWidget(self.progress_bar) bottom_layout.addWidget(self.button_cancel) bottom_layout.addWidget(self.button_apply) bottom_layout.addWidget(self.button_clear) bottom_layout.setContentsMargins(0, 0, 0, 0) self.frame_bottom.setLayout(bottom_layout) layout = QVBoxLayout(self) layout.addWidget(self.frame_top) layout.addLayout(middle_layout) layout.addWidget(self.frame_bottom) self.setLayout(layout) self.setTabOrder(self.combobox_filter, self.button_channels) self.setTabOrder(self.button_channels, self.button_update) self.setTabOrder(self.button_update, self.textbox_search) self.setTabOrder(self.textbox_search, self.table_first_row) self.setTabOrder(self.table, self.table_last_row) self.setTabOrder(self.table_last_row, self.button_apply) self.setTabOrder(self.button_apply, self.button_clear) self.setTabOrder(self.button_clear, self.button_cancel) # Signals and slots self.api.sig_repodata_updated.connect(self._repodata_updated) self.combobox_filter.currentIndexChanged.connect(self.filter_package) self.button_apply.clicked.connect(self.apply_multiple_actions) self.button_clear.clicked.connect(self.clear_actions) self.button_cancel.clicked.connect(self.cancel_process) self.button_channels.clicked.connect(self.show_channels_dialog) self.button_update.clicked.connect(self.update_package_index) self.textbox_search.textChanged.connect(self.search_package) self.table.sig_conda_action_requested.connect(self._run_conda_action) self.table.sig_actions_updated.connect(self.update_actions) self.table.sig_pip_action_requested.connect(self._run_pip_action) self.table.sig_status_updated.connect(self.update_status) self.table.sig_next_focus.connect(self.table_last_row.handle_tab) self.table.sig_previous_focus.connect( lambda: self.table_first_row.widget_before.setFocus()) self.table_first_row.sig_enter_first.connect(self._handle_tab_focus) self.table_last_row.sig_enter_last.connect(self._handle_backtab_focus) # Setup self.api.client_set_domain(conda_api_url) self.api.set_data_directory(self.data_directory) self._load_bundled_metadata() self.update_actions(0) if setup: self.set_environment(name=name, prefix=prefix) self.setup() # --- Helpers # ------------------------------------------------------------------------- def _handle_tab_focus(self): self.table.setFocus() if self.table.proxy_model: index = self.table.proxy_model.index(0, 0) self.table.setCurrentIndex(index) def _handle_backtab_focus(self): self.table.setFocus() if self.table.proxy_model: row = self.table.proxy_model.rowCount() - 1 index = self.table.proxy_model.index(row, 0) self.table.setCurrentIndex(index) # --- Callbacks # ------------------------------------------------------------------------- def _load_bundled_metadata(self): """ """ logger.debug('') parser = cp.ConfigParser() db_file = CondaPackagesWidget.DATABASE_FILE with open(osp.join(self.DATA_PATH, db_file)) as f: parser.readfp(f) for name in parser.sections(): metadata = {} for key, data in parser.items(name): metadata[key] = data self._metadata_links[name] = metadata def _setup_packages(self, worker, data, error): """ """ if error: logger.error(error) else: logger.debug('') combobox_index = self.combobox_filter.currentIndex() status = C.PACKAGE_STATUS[combobox_index] packages = worker.packages # Remove blacklisted packages for package in self.package_blacklist: if package in packages: packages.pop(package) for i, row in enumerate(data): if package == data[i][C.COL_NAME]: data.pop(i) self.table.setup_model(packages, data, self._metadata_links) self.combobox_filter.setCurrentIndex(combobox_index) self.filter_package(status) if self._current_model_index: self.table.setCurrentIndex(self._current_model_index) self.table.verticalScrollBar().setValue(self._current_table_scroll) if error: self.update_status(error, False) self.sig_packages_ready.emit() self.table.setFocus() def get_logged_user_list_channels(self): channels = [] for ch in self._active_channels: if self.conda_url in ch and 'repo.continuum' not in ch: channel = ch.split('/')[-1] channels.append(channel) return channels def _prepare_model_data(self, worker=None, output=None, error=None): """ """ if error: logger.error(error) else: logger.debug('') packages, apps = output # worker = self.api.pip_list(prefix=self.prefix) # worker.sig_finished.connect(self._pip_list_ready) logins = self.get_logged_user_list_channels() worker = self.api.client_multi_packages(logins=logins, access='private') worker.sig_finished.connect(self._user_private_packages_ready) worker.packages = packages worker.apps = apps def _user_private_packages_ready(self, worker, output, error): if error: logger.error(error) else: logger.debug('') packages = worker.packages apps = worker.apps worker = self.api.pip_list(prefix=self.prefix) worker.sig_finished.connect(self._pip_list_ready) worker.packages = packages worker.apps = apps # private_packages = {} # if output: # all_private_packages = output # for item in all_private_packages: # name = item.get('name', '') # public = item.get('public', True) # package_types = item.get('package_types', []) # latest_version = item.get('latest_version', '') # if name and not public and 'conda' in package_types: # private_packages[name] = {'versions': item.get('versions', []), # 'app_entry': {}, # 'type': {}, # 'size': {}, # 'latest_version': latest_version, # } worker.private_packages = output def _pip_list_ready(self, worker, pip_packages, error): """ """ if error: logger.error(error) else: logger.debug('') packages = worker.packages private_packages = worker.private_packages linked_packages = self.api.conda_linked(prefix=self.prefix) data = self.api.client_prepare_packages_data(packages, linked_packages, pip_packages, private_packages) combobox_index = self.combobox_filter.currentIndex() status = C.PACKAGE_STATUS[combobox_index] # Remove blacklisted packages for package in self.package_blacklist: if package in packages: packages.pop(package) for i, row in enumerate(data): if package == data[i][C.COL_NAME]: data.pop(i) self.table.setup_model(packages, data, self._metadata_links) self.combobox_filter.setCurrentIndex(combobox_index) self.filter_package(status) if self._current_model_index: self.table.setCurrentIndex(self._current_model_index) self.table.verticalScrollBar().setValue(self._current_table_scroll) if error: self.update_status(str(error), False) self.sig_packages_ready.emit() self.table.setFocus() def _repodata_updated(self, paths): """ """ worker = self.api.client_load_repodata(paths, extra_data={}, metadata=self._metadata) worker.paths = paths worker.sig_finished.connect(self._prepare_model_data) def _metadata_updated(self, worker, path, error): """ """ if error: logger.error(error) else: logger.debug('') if path and osp.isfile(path): with open(path, 'r') as f: data = f.read() try: self._metadata = json.loads(data) except Exception: self._metadata = {} else: self._metadata = {} self.api.update_repodata(self._channels) # --- # ------------------------------------------------------------------------- def _run_multiple_actions(self, worker=None, output=None, error=None): """ """ logger.error(str(error)) if output and isinstance(output, dict): conda_error_type = output.get('error_type', None) conda_error = output.get('error', None) if conda_error_type or conda_error: self.conda_errors.append((conda_error_type, conda_error)) logger.error((conda_error_type, conda_error)) if self._multiple_process: status, func = self._multiple_process.popleft() self.update_status(status) worker = func() worker.sig_finished.connect(self._run_multiple_actions) worker.sig_partial.connect(self._partial_output_ready) else: if self.conda_errors and self.message_box_error: text = "The following errors occured:" error = '' for conda_error in self.conda_errors: error += str(conda_error[0]) + ':\n' error += str(conda_error[1]) + '\n\n' dlg = self.message_box_error(text=text, error=error, title='Conda process error') dlg.setMinimumWidth(400) dlg.exec_() self.update_status('', hide=False) self.setup() def _pip_process_ready(self, worker, output, error): """ """ if error is not None: status = _('there was an error') self.update_status(hide=False, message=status) else: self.update_status(hide=True) self.setup() def _conda_process_ready(self, worker, output, error): """ """ if error is not None: status = _('there was an error') self.update_status(hide=False, message=status) else: self.update_status(hide=True) conda_error = None conda_error_type = None if output and isinstance(output, dict): conda_error_type = output.get('error_type') conda_error = output.get('error') if conda_error_type or conda_error: logger.error((conda_error_type, conda_error)) dic = self._temporal_action_dic if dic['action'] == C.ACTION_CREATE: self.sig_environment_created.emit(conda_error, conda_error_type) elif dic['action'] == C.ACTION_CLONE: self.sig_environment_cloned.emit(conda_error, conda_error_type) elif dic['action'] == C.ACTION_REMOVE_ENV: self.sig_environment_removed.emit(conda_error, conda_error_type) self.setup() def _partial_output_ready(self, worker, output, error): """ """ message = None progress = (0, 0) if isinstance(output, dict): progress = (output.get('progress', None), output.get('maxval', None)) name = output.get('name', None) fetch = output.get('fetch', None) if fetch: message = "Downloading <b>{0}</b>...".format(fetch) if name: self._current_action_name = name message = "Linking <b>{0}</b>...".format(name) logger.debug(message) self.update_status(message, progress=progress) def _run_pip_action(self, package_name, action): """ DEPRECATED """ prefix = self.prefix if prefix == self.root_prefix: name = 'root' elif self.api.conda_environment_exists(prefix=prefix): name = osp.basename(prefix) else: name = prefix if action == C.ACTION_REMOVE: msgbox = QMessageBox.question(self, "Remove pip package: " "{0}".format(package_name), "Do you want to proceed?", QMessageBox.Yes | QMessageBox.No) if msgbox == QMessageBox.Yes: self.update_status() worker = self.api.pip_remove(prefix=self.prefix, pkgs=[package_name]) worker.sig_finished.connect(self._pip_process_ready) status = (_('Removing pip package <b>') + package_name + '</b>' + _(' from <i>') + name + '</i>') self.update_status(hide=True, message=status, progress=[0, 0]) def _run_conda_action(self, package_name, action, version, versions, packages_sizes): """ DEPRECATED """ prefix = self.prefix dlg = CondaPackageActionDialog(self, prefix, package_name, action, version, versions, packages_sizes, self._active_channels) if dlg.exec_(): dic = {} self.status = 'Processing' self.update_status(hide=True) self.repaint() ver1 = dlg.label_version.text() ver2 = dlg.combobox_version.currentText() pkg = u'{0}={1}{2}'.format(package_name, ver1, ver2) dep = dlg.checkbox.checkState() state = dlg.checkbox.isEnabled() dlg.close() dic['pkg'] = pkg dic['dep'] = not (dep == 0 and state) dic['action'] = None self._run_conda_process(action, dic) def _run_conda_process(self, action, dic): """ DEPRECTAED """ self._temporal_action_dic = dic # prefix = self.prefix # # if prefix == self.root_prefix: # name = 'root' # elif self.api.conda_environment_exists(prefix=prefix): # name = osp.basename(prefix) # else: # name = prefix if 'pkgs' in dic and 'dep' in dic: dep = dic['dep'] pkgs = dic['pkgs'] if not isinstance(pkgs, list): pkgs = [pkgs] # if (action == C.ACTION_INSTALL or action == C.ACTION_UPGRADE or # action == C.ACTION_DOWNGRADE): # status = _('Installing <b>') + dic['pkg'] + '</b>' # status = status + _(' into <i>') + name + '</i>' # worker = self.api.conda_install(prefix=prefix, pkgs=pkgs, dep=dep, # channels=self._active_channels) # elif action == C.ACTION_REMOVE: # status = (_('Removing <b>') + dic['pkg'] + '</b>' + # _(' from <i>') + name + '</i>') # worker = self.api.conda_remove(pkgs[0], prefix=prefix) # --- Environment management actions name = dic['name'] if action == C.ACTION_CREATE: status = _('Creating environment <b>') + name + '</b>' worker = self.api.conda_create(name=name, pkgs=pkgs, channels=self._active_channels) elif action == C.ACTION_CLONE: clone = dic['clone'] status = (_('Cloning ') + '<i>' + clone + _('</i> into <b>') + name + '</b>') worker = self.api.conda_clone(clone, name=name) elif action == C.ACTION_REMOVE_ENV: status = _('Removing environment <b>') + name + '</b>' worker = self.api.conda_remove(name=name, all_=True) worker.sig_finished.connect(self._conda_process_ready) worker.sig_partial.connect(self._partial_output_ready) self.update_status(hide=True, message=status, progress=None) self._temporal_action_dic = dic return worker # Public API # ------------------------------------------------------------------------- def prepare_model_data(self, packages, apps): """ """ logger.debug('') self._prepare_model_data(output=(packages, apps)) # These should be private methods.... def enable_widgets(self): """ """ self.table.hide_columns() def disable_widgets(self): """ """ self.table.hide_action_columns() def accept_channels_dialog(self): self.button_channels.setFocus() self.button_channels.toggle() def update_actions(self, number_of_actions): """ """ self.button_apply.setVisible(bool(number_of_actions)) self.button_clear.setVisible(bool(number_of_actions)) # --- Non UI API # ------------------------------------------------------------------------- def setup(self, check_updates=False, blacklist=[], metadata={}): """ Setup packages. Main triger method to download repodata, load repodata, prepare and updating the data model. Parameters ---------- check_updates : bool If `True`, checks that the latest repodata is available on the listed channels. If `False`, the data will be loaded from the downloaded files without checking for newer versions. blacklist: list of str List of conda package names to be excluded from the actual package manager view. """ self.sig_packages_busy.emit() if self.busy: logger.debug('Busy...') return else: logger.debug('') if blacklist: self.package_blacklist = [p.lower() for p in blacklist] if metadata: self._metadata = metadata self._current_model_index = self.table.currentIndex() self._current_table_scroll = self.table.verticalScrollBar().value() self.update_status('Updating package index', True) if check_updates: worker = self.api.update_metadata() worker.sig_finished.connect(self._metadata_updated) else: paths = self.api.repodata_files(channels=self._active_channels) self._repodata_updated(paths) def update_domains(self, anaconda_api_url=None, conda_url=None): """ """ logger.debug(str((anaconda_api_url, conda_url))) update = False if anaconda_api_url: if self.conda_api_url != anaconda_api_url: update = True self.conda_api_url = anaconda_api_url self.api.client_set_domain(anaconda_api_url) if conda_url: if self.conda_url != conda_url: update = True self.conda_url = conda_url if update: pass def set_environment(self, name=None, prefix=None): """ This does not update the package manager! """ logger.debug(str((name, prefix))) if prefix and self.api.conda_environment_exists(prefix=prefix): self.prefix = prefix elif name and self.api.conda_environment_exists(name=name): self.prefix = self.get_prefix_envname(name) else: self.prefix = self.root_prefix def set_token(self, token): self.token = token def update_channels(self, channels, active_channels): """ """ logger.debug(str((channels, active_channels))) if sorted(self._active_channels) != sorted(active_channels) or \ sorted(self._channels) != sorted(channels): self._channels = channels self._active_channels = active_channels self.sig_channels_updated.emit(tuple(channels), tuple(active_channels)) self.setup(check_updates=True) def update_style_sheet(self, style_sheet=None, extra_dialogs={}, palette={}): if style_sheet: self.style_sheet = style_sheet self.table.update_style_palette(palette=palette) self.textbox_search.update_style_sheet(style_sheet) self.setStyleSheet(style_sheet) if extra_dialogs: cancel_dialog = extra_dialogs.get('cancel_dialog', None) apply_actions_dialog = extra_dialogs.get('apply_actions_dialog', None) message_box_error = extra_dialogs.get('message_box_error', None) if cancel_dialog: self.cancel_dialog = cancel_dialog if apply_actions_dialog: self.apply_actions_dialog = apply_actions_dialog if message_box_error: self.message_box_error = message_box_error # --- UI API # ------------------------------------------------------------------------- def filter_package(self, value): """ """ self.table.filter_status_changed(value) def show_channels_dialog(self): """ Show the channels dialog. """ button_channels = self.button_channels self.dlg = DialogChannels(self, channels=self._channels, active_channels=self._active_channels, conda_url=self.conda_url) self.dlg.update_style_sheet(style_sheet=self.style_sheet) button_channels.setDisabled(True) self.dlg.sig_channels_updated.connect(self.update_channels) self.dlg.rejected.connect(lambda: button_channels.setEnabled(True)) self.dlg.rejected.connect(button_channels.toggle) self.dlg.rejected.connect(button_channels.setFocus) self.dlg.accepted.connect(self.accept_channels_dialog) geo_tl = button_channels.geometry().topLeft() tl = button_channels.parentWidget().mapToGlobal(geo_tl) x = tl.x() + 2 y = tl.y() + button_channels.height() self.dlg.move(x, y) self.dlg.show() self.dlg.button_add.setFocus() def update_package_index(self): """ """ self.setup(check_updates=True) def search_package(self, text): """ """ self.table.search_string_changed(text) def apply_multiple_actions(self): """ """ logger.debug('') self.conda_errors = [] prefix = self.prefix if prefix == self.root_prefix: name = 'root' elif self.api.conda_environment_exists(prefix=prefix): name = osp.basename(prefix) else: name = prefix actions = self.table.get_actions() if actions is None: return self._multiple_process = deque() pip_actions = actions[C.PIP_PACKAGE] conda_actions = actions[C.CONDA_PACKAGE] pip_remove = pip_actions.get(C.ACTION_REMOVE, []) conda_remove = conda_actions.get(C.ACTION_REMOVE, []) conda_install = conda_actions.get(C.ACTION_INSTALL, []) conda_upgrade = conda_actions.get(C.ACTION_UPGRADE, []) conda_downgrade = conda_actions.get(C.ACTION_DOWNGRADE, []) message = '' template_1 = '<li><b>{0}={1}</b></li>' template_2 = '<li><b>{0}: {1} -> {2}</b></li>' if pip_remove: temp = [template_1.format(i['name'], i['version_to']) for i in pip_remove] message += ('The following pip packages will be removed: ' '<ul>' + ''.join(temp) + '</ul>') if conda_remove: temp = [template_1.format(i['name'], i['version_to']) for i in conda_remove] message += ('<br>The following conda packages will be removed: ' '<ul>' + ''.join(temp) + '</ul>') if conda_install: temp = [template_1.format(i['name'], i['version_to']) for i in conda_install] message += ('<br>The following conda packages will be installed: ' '<ul>' + ''.join(temp) + '</ul>') if conda_downgrade: temp = [template_2.format( i['name'], i['version_from'], i['version_to']) for i in conda_downgrade] message += ('<br>The following conda packages will be downgraded: ' '<ul>' + ''.join(temp) + '</ul>') if conda_upgrade: temp = [template_2.format( i['name'], i['version_from'], i['version_to']) for i in conda_upgrade] message += ('<br>The following conda packages will be upgraded: ' '<ul>' + ''.join(temp) + '</ul>') message += '<br>' if self.apply_actions_dialog: dlg = self.apply_actions_dialog(message, parent=self) dlg.update_style_sheet(style_sheet=self.style_sheet) reply = dlg.exec_() else: reply = QMessageBox.question(self, 'Proceed with the following actions?', message, buttons=QMessageBox.Ok | QMessageBox.Cancel) if reply: # Pip remove for pkg in pip_remove: status = (_('Removing pip package <b>') + pkg['name'] + '</b>' + _(' from <i>') + name + '</i>') pkgs = [pkg['name']] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.pip_remove(prefix=prefix, pkgs=pkgs) self._multiple_process.append([status, trigger()]) # Process conda actions if conda_remove: status = (_('Removing conda packages <b>') + '</b>' + _(' from <i>') + name + '</i>') pkgs = [i['name'] for i in conda_remove] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_remove(pkgs=pkgs, prefix=prefix) self._multiple_process.append([status, trigger()]) if conda_install: pkgs = ['{0}={1}'.format(i['name'], i['version_to']) for i in conda_install] status = (_('Installing conda packages <b>') + '</b>' + _(' on <i>') + name + '</i>') def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_install( prefix=prefix, pkgs=pkgs, channels=self._active_channels, token=self.token) self._multiple_process.append([status, trigger()]) # Conda downgrade if conda_downgrade: status = (_('Downgrading conda packages <b>') + '</b>' + _(' on <i>') + name + '</i>') pkgs = ['{0}={1}'.format(i['name'], i['version_to']) for i in conda_downgrade] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_install( prefix=prefix, pkgs=pkgs, channels=self._active_channels, token=self.token) self._multiple_process.append([status, trigger()]) # Conda update if conda_upgrade: status = (_('Upgrading conda packages <b>') + '</b>' + _(' on <i>') + name + '</i>') pkgs = ['{0}={1}'.format(i['name'], i['version_to']) for i in conda_upgrade] def trigger(prefix=prefix, pkgs=pkgs): return lambda: self.api.conda_install( prefix=prefix, pkgs=pkgs, channels=self._active_channels, token=self.token) self._multiple_process.append([status, trigger()]) self._run_multiple_actions() def clear_actions(self): """ """ self.table.clear_actions() def cancel_process(self): """ Allow user to cancel an ongoing process. """ logger.debug(str('process canceled by user.')) if self.busy: dlg = self.cancel_dialog() reply = dlg.exec_() if reply: self.update_status(hide=False, message='Process cancelled') self.api.conda_terminate() self.api.download_requests_terminate() self.api.conda_clear_lock() self.table.clear_actions() self.sig_process_cancelled.emit() else: QDialog.reject(self) def update_status(self, message=None, hide=True, progress=None, env=False): """ Update status bar, progress bar display and widget visibility message : str Message to display in status bar. hide : bool Enable/Disable widgets. progress : [int, int] Show status bar progress. [0, 0] means spinning statusbar. """ self.busy = hide for widget in self.widgets: widget.setDisabled(hide) self.table.verticalScrollBar().setValue(self._current_table_scroll) self.button_apply.setVisible(False) self.button_clear.setVisible(False) self.progress_bar.setVisible(hide) self.button_cancel.setVisible(hide) if message is not None: self.message = message if self.prefix == self.root_prefix: short_env = 'root' # elif self.api.environment_exists(prefix=self.prefix): # short_env = osp.basename(self.prefix) else: short_env = self.prefix if env: self.message = '{0} (<b>{1}</b>)'.format( self.message, short_env, ) self.status_bar.setText(self.message) if progress is not None: current_progress, max_progress = 0, 0 if progress[1]: max_progress = progress[1] if progress[0]: current_progress = progress[0] self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(max_progress) self.progress_bar.setValue(current_progress) else: self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(0) # --- Conda helpers # ------------------------------------------------------------------------- def get_environment_prefix(self): """ Returns the active environment prefix. """ return self._prefix def get_environment_name(self): """ Returns the active environment name if it is located in the default conda environments directory, otherwise it returns the prefix. """ name = osp.basename(self._prefix) if not (name and self.api.environment_exists(name=name)): name = self._prefix return name def get_environments(self): """ Get a list of conda environments located in the default conda environments directory. """ return self.api.conda_get_envs() def get_prefix_envname(self, name): """ Returns the prefix for a given environment by name. """ return self.api.conda_get_prefix_envname(name) def get_package_versions(self, name): """ """ return self.table.source_model.get_package_versions(name) # --- Conda actions # ------------------------------------------------------------------------- def create_environment(self, name=None, prefix=None, packages=['python']): """ """ # If environment exists already? GUI should take care of this # BUT the api call should simply set that env as the env dic = {} dic['name'] = name dic['prefix'] = prefix dic['pkgs'] = packages dic['dep'] = True # Not really needed but for the moment! dic['action'] = C.ACTION_CREATE return self._run_conda_process(dic['action'], dic) def clone_environment(self, name=None, prefix=None, clone=None): dic = {} dic['name'] = name dic['prefix'] = prefix dic['clone'] = clone dic['pkgs'] = None dic['dep'] = True # Not really needed but for the moment! dic['action'] = C.ACTION_CLONE return self._run_conda_process(dic['action'], dic) def remove_environment(self, name=None, prefix=None): dic = {} dic['name'] = name dic['pkgs'] = None dic['dep'] = True # Not really needed but for the moment! dic['action'] = C.ACTION_REMOVE_ENV return self._run_conda_process(dic['action'], dic)
def __init__(self, parent=None): super().__init__(parent) self._dialog_data = {} self.setWindowTitle("Find Elements in Sample") # Check this flag after the dialog is exited with 'True' value # If this flag is True, then run element search, if False, # then simply save the changed parameter values. self.find_elements_requested = False self.validator = QDoubleValidator() self.le_e_calib_a0 = LineEditExtended() self.le_e_calib_a0.setValidator(self.validator) self.pb_e_calib_a0_default = QPushButton("Default") self.pb_e_calib_a0_default.setAutoDefault(False) self.le_e_calib_a1 = LineEditExtended() self.le_e_calib_a1.setValidator(self.validator) self.pb_e_calib_a1_default = QPushButton("Default") self.pb_e_calib_a1_default.setAutoDefault(False) self.le_e_calib_a2 = LineEditExtended() self.le_e_calib_a2.setValidator(self.validator) self.pb_e_calib_a2_default = QPushButton("Default") self.pb_e_calib_a2_default.setAutoDefault(False) self.group_energy_axis_calib = QGroupBox( "Polynomial Approximation of Energy Axis") set_tooltip( self.group_energy_axis_calib, "Parameters of polynomial approximation of <b>energy axis</b>. " "The values of the bins for photon energies are approximated " "using 2nd degree polynomial <b>E(n) = a0 + a1 * n + a2 * n^2</b>, " "where <b>n</b> is bin number (typically in the range 0..4096).", ) grid = QGridLayout() grid.addWidget(QLabel("Bias (a0):"), 0, 0) grid.addWidget(self.le_e_calib_a0, 0, 1) grid.addWidget(self.pb_e_calib_a0_default, 0, 2) grid.addWidget(QLabel("Linear (a1):"), 1, 0) grid.addWidget(self.le_e_calib_a1, 1, 1) grid.addWidget(self.pb_e_calib_a1_default, 1, 2) grid.addWidget(QLabel("Quadratic (a2):"), 2, 0) grid.addWidget(self.le_e_calib_a2, 2, 1) grid.addWidget(self.pb_e_calib_a2_default, 2, 2) self.group_energy_axis_calib.setLayout(grid) self.le_fwhm_b1 = LineEditExtended() self.le_fwhm_b1.setValidator(self.validator) self.pb_fwhm_b1_default = QPushButton("Default") self.pb_fwhm_b1_default.setAutoDefault(False) self.le_fwhm_b2 = LineEditExtended() self.le_fwhm_b2.setValidator(self.validator) self.pb_fwhm_b2_default = QPushButton("Default") self.pb_fwhm_b2_default.setAutoDefault(False) self.group_fwhm = QGroupBox("Peak FWHM Settings") set_tooltip( self.group_fwhm, "Parameters used to estimate <b>FWHM</b> of peaks based on energy: " "<b>b1</b> - 'offset', <b>b2</b> - 'fanoprime'", ) grid = QGridLayout() grid.addWidget(QLabel("Coefficient b1:"), 0, 0) grid.addWidget(self.le_fwhm_b1, 0, 1) grid.addWidget(self.pb_fwhm_b1_default, 0, 2) grid.addWidget(QLabel("Coefficient b2:"), 1, 0) grid.addWidget(self.le_fwhm_b2, 1, 1) grid.addWidget(self.pb_fwhm_b2_default, 1, 2) self.group_fwhm.setLayout(grid) self.le_incident_energy = LineEditExtended() self.le_incident_energy.setValidator(self.validator) set_tooltip(self.le_incident_energy, "<b>Incident energy</b> in keV.") self.pb_incident_energy_default = QPushButton("Default") self.pb_incident_energy_default.setAutoDefault(False) self.le_range_low = LineEditExtended() self.le_range_low.setValidator(self.validator) set_tooltip(self.le_range_low, "<b>Lower boundary</b> of the selected range in keV.") self.pb_range_low_default = QPushButton("Default") self.pb_range_low_default.setAutoDefault(False) self.le_range_high = LineEditExtended() self.le_range_high.setValidator(self.validator) set_tooltip(self.le_range_high, "<b>Upper boundary</b> of the selected range in keV.") self.pb_range_high_default = QPushButton("Default") self.pb_range_high_default.setAutoDefault(False) self.group_energy_range = QGroupBox( "Incident Energy and Selected Range") grid = QGridLayout() grid.addWidget(QLabel("Incident energy, keV"), 0, 0) grid.addWidget(self.le_incident_energy, 0, 1) grid.addWidget(self.pb_incident_energy_default, 0, 2) grid.addWidget(QLabel("Range (low), keV"), 1, 0) grid.addWidget(self.le_range_low, 1, 1) grid.addWidget(self.pb_range_low_default, 1, 2) grid.addWidget(QLabel("Range (high), keV"), 2, 0) grid.addWidget(self.le_range_high, 2, 1) grid.addWidget(self.pb_range_high_default, 2, 2) self.group_energy_range.setLayout(grid) self.pb_find_elements = QPushButton("Find &Elements") self.pb_find_elements.clicked.connect(self.pb_find_elements_clicked) self.pb_apply_settings = QPushButton("&Apply Settings") # 'Close' button box button_box = QDialogButtonBox(QDialogButtonBox.Cancel) button_box.addButton(self.pb_find_elements, QDialogButtonBox.YesRole) button_box.addButton(self.pb_apply_settings, QDialogButtonBox.AcceptRole) button_box.button(QDialogButtonBox.Cancel).setDefault(True) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self.pb_cancel = button_box.button(QDialogButtonBox.Cancel) self.pb_cancel.setAutoDefault(False) self.pb_apply_settings.setAutoDefault(True) vbox = QVBoxLayout() vbox.addWidget(self.group_energy_axis_calib) vbox.addWidget(self.group_fwhm) vbox.addWidget(self.group_energy_range) vbox.addWidget(button_box) self.setLayout(vbox) self.le_e_calib_a0.editingFinished.connect( self.le_e_calib_a0_editing_finished) self.le_e_calib_a1.editingFinished.connect( self.le_e_calib_a1_editing_finished) self.le_e_calib_a2.editingFinished.connect( self.le_e_calib_a2_editing_finished) self.le_fwhm_b1.editingFinished.connect( self.le_fwhm_b1_editing_finished) self.le_fwhm_b2.editingFinished.connect( self.le_fwhm_b2_editing_finished) self.le_incident_energy.editingFinished.connect( self.le_incident_energy_editing_finished) self.le_range_low.editingFinished.connect( self.le_range_low_editing_finished) self.le_range_high.editingFinished.connect( self.le_range_high_editing_finished) self.le_e_calib_a0.focusOut.connect(self.le_e_calib_a0_focus_out) self.le_e_calib_a1.focusOut.connect(self.le_e_calib_a1_focus_out) self.le_e_calib_a2.focusOut.connect(self.le_e_calib_a2_focus_out) self.le_fwhm_b1.focusOut.connect(self.le_fwhm_b1_focus_out) self.le_fwhm_b2.focusOut.connect(self.le_fwhm_b2_focus_out) self.le_incident_energy.focusOut.connect( self.le_incident_energy_focus_out) self.le_range_low.focusOut.connect(self.le_range_low_focus_out) self.le_range_high.focusOut.connect(self.le_range_high_focus_out) self.le_e_calib_a0.textChanged.connect(self.le_e_calib_a0_text_changed) self.le_e_calib_a1.textChanged.connect(self.le_e_calib_a1_text_changed) self.le_e_calib_a2.textChanged.connect(self.le_e_calib_a2_text_changed) self.le_fwhm_b1.textChanged.connect(self.le_fwhm_b1_text_changed) self.le_fwhm_b2.textChanged.connect(self.le_fwhm_b2_text_changed) self.le_incident_energy.textChanged.connect( self.le_incident_energy_text_changed) self.le_range_low.textChanged.connect(self.le_range_low_text_changed) self.le_range_high.textChanged.connect(self.le_range_high_text_changed) self.pb_e_calib_a0_default.clicked.connect( self.pb_e_calib_a0_default_clicked) self.pb_e_calib_a1_default.clicked.connect( self.pb_e_calib_a1_default_clicked) self.pb_e_calib_a2_default.clicked.connect( self.pb_e_calib_a2_default_clicked) self.pb_fwhm_b1_default.clicked.connect( self.pb_fwhm_b1_default_clicked) self.pb_fwhm_b2_default.clicked.connect( self.pb_fwhm_b2_default_clicked) self.pb_incident_energy_default.clicked.connect( self.pb_incident_energy_default_clicked) self.pb_range_low_default.clicked.connect( self.pb_range_low_default_clicked) self.pb_range_high_default.clicked.connect( self.pb_range_high_default_clicked)
class SelectFromListDialog(QDialog): """Dialog box for selecting multiple items. The original order of items are preserved in the candidate list. """ def __init__(self, parent=None, allItems=(), selectedItems=()): QDialog.__init__( self, parent, flags=Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.allItems = allItems self.fromKeys = list(range(len(allItems))) self.selectedKeys = [] for item in selectedItems: key = allItems.index(item) self.fromKeys.remove(key) self.selectedKeys.append(key) self.setAttribute(Qt.WA_DeleteOnClose) fromLabel = QLabel(_("Select from")) self.fromList = QListWidget(self) self.fromList.addItems(allItems[key] for key in self.fromKeys) selectedLabel = QLabel(_("Selected")) self.selectedList = QListWidget(self) self.selectedList.addItems(allItems[key] for key in self.selectedKeys) self.selectButton = QPushButton(_("Select")) self.deselectButton = QPushButton(_("Deselect")) self.selectBox = QDialogButtonBox(Qt.Vertical) self.selectBox.addButton(self.selectButton, QDialogButtonBox.ActionRole) self.selectBox.addButton(self.deselectButton, QDialogButtonBox.ActionRole) self.selectButton.clicked.connect(self.on_select) self.deselectButton.clicked.connect(self.on_deselect) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Cancel|QDialogButtonBox.Ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) mainLayout = QGridLayout(self) mainLayout.addWidget(fromLabel, 0, 0) mainLayout.addWidget(selectedLabel, 0, 2) mainLayout.addWidget(self.fromList, 1, 0) mainLayout.addWidget(self.selectBox, 1, 1) mainLayout.addWidget(self.selectedList, 1, 2) mainLayout.addWidget(self.buttonBox, 2, 0, 1, 3) mainLayout.setAlignment(self.selectBox, Qt.AlignCenter) self.setLayout(mainLayout) def on_select(self): if len(self.fromList.selectedItems()): idx = self.fromList.currentRow() key = self.fromKeys.pop(idx) self.selectedKeys.append(key) item = self.fromList.takeItem(idx) self.selectedList.addItem(item) def on_deselect(self): if len(self.selectedList.selectedItems()): idx = self.selectedList.currentRow() item = self.selectedList.takeItem(idx) key = self.selectedKeys.pop(idx) idx = next((i for i, v in enumerate(self.fromKeys) if v > key), len(self.fromKeys)) self.fromKeys.insert(idx, key) self.fromList.insertItem(idx, item)
class ProjectDialog(QDialog): """Project creation dialog.""" # path, type, packages sig_project_creation_requested = Signal(object, object, object) def __init__(self, parent): """Project creation dialog.""" super(ProjectDialog, self).__init__(parent=parent) # Variables current_python_version = '.'.join([to_text_string(sys.version_info[0]), to_text_string(sys.version_info[1])]) python_versions = ['2.7', '3.4', '3.5'] if current_python_version not in python_versions: python_versions.append(current_python_version) python_versions = sorted(python_versions) self.project_name = None self.location = get_home_dir() # Widgets self.groupbox = QGroupBox() self.radio_new_dir = QRadioButton(_("New directory")) self.radio_from_dir = QRadioButton(_("Existing directory")) self.label_project_name = QLabel(_('Project name')) self.label_location = QLabel(_('Location')) self.label_project_type = QLabel(_('Project type')) self.label_python_version = QLabel(_('Python version')) self.text_project_name = QLineEdit() self.text_location = QLineEdit(get_home_dir()) self.combo_project_type = QComboBox() self.combo_python_version = QComboBox() self.button_select_location = QToolButton() self.button_cancel = QPushButton(_('Cancel')) self.button_create = QPushButton(_('Create')) self.bbox = QDialogButtonBox(Qt.Horizontal) self.bbox.addButton(self.button_cancel, QDialogButtonBox.ActionRole) self.bbox.addButton(self.button_create, QDialogButtonBox.ActionRole) # Widget setup self.combo_python_version.addItems(python_versions) self.radio_new_dir.setChecked(True) self.text_location.setEnabled(True) self.text_location.setReadOnly(True) self.button_select_location.setIcon(get_std_icon('DirOpenIcon')) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) self.button_create.setEnabled(False) self.combo_project_type.addItems(self._get_project_types()) self.combo_python_version.setCurrentIndex( python_versions.index(current_python_version)) self.setWindowTitle(_('Create new project')) self.setFixedWidth(500) self.label_python_version.setVisible(False) self.combo_python_version.setVisible(False) # Layouts layout_top = QHBoxLayout() layout_top.addWidget(self.radio_new_dir) layout_top.addWidget(self.radio_from_dir) layout_top.addStretch(1) self.groupbox.setLayout(layout_top) layout_grid = QGridLayout() layout_grid.addWidget(self.label_project_name, 0, 0) layout_grid.addWidget(self.text_project_name, 0, 1, 1, 2) layout_grid.addWidget(self.label_location, 1, 0) layout_grid.addWidget(self.text_location, 1, 1) layout_grid.addWidget(self.button_select_location, 1, 2) layout_grid.addWidget(self.label_project_type, 2, 0) layout_grid.addWidget(self.combo_project_type, 2, 1, 1, 2) layout_grid.addWidget(self.label_python_version, 3, 0) layout_grid.addWidget(self.combo_python_version, 3, 1, 1, 2) layout = QVBoxLayout() layout.addWidget(self.groupbox) layout.addSpacing(10) layout.addLayout(layout_grid) layout.addStretch() layout.addSpacing(20) layout.addWidget(self.bbox) self.setLayout(layout) # Signals and slots self.button_select_location.clicked.connect(self.select_location) self.button_create.clicked.connect(self.create_project) self.button_cancel.clicked.connect(self.close) self.radio_from_dir.clicked.connect(self.update_location) self.radio_new_dir.clicked.connect(self.update_location) self.text_project_name.textChanged.connect(self.update_location) def _get_project_types(self): """Get all available project types.""" project_types = get_available_project_types() projects = [] for project in project_types: projects.append(project.PROJECT_TYPE_NAME) return projects def select_location(self): """Select directory.""" location = osp.normpath(getexistingdirectory(self, _("Select directory"), self.location)) if location: if is_writable(location): self.location = location self.update_location() def update_location(self, text=''): """Update text of location.""" self.text_project_name.setEnabled(self.radio_new_dir.isChecked()) name = self.text_project_name.text().strip() if name and self.radio_new_dir.isChecked(): path = osp.join(self.location, name) self.button_create.setDisabled(os.path.isdir(path)) elif self.radio_from_dir.isChecked(): self.button_create.setEnabled(True) path = self.location else: self.button_create.setEnabled(False) path = self.location self.text_location.setText(path) def create_project(self): """Create project.""" packages = ['python={0}'.format(self.combo_python_version.currentText())] self.sig_project_creation_requested.emit( self.text_location.text(), self.combo_project_type.currentText(), packages) self.accept()
class ProjectDialog(QDialog): """Project creation dialog.""" sig_project_creation_requested = Signal(str, str, object) """ This signal is emitted to request the Projects plugin the creation of a project. Parameters ---------- project_path: str Location of project. project_type: str Type of project as defined by project types. project_packages: object Package to install. Currently not in use. """ def __init__(self, parent, project_types): """Project creation dialog.""" super(ProjectDialog, self).__init__(parent=parent) self.plugin = parent self._project_types = project_types self.project_data = {} self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.project_name = None self.location = get_home_dir() # Widgets projects_url = "http://docs.spyder-ide.org/current/panes/projects.html" self.description_label = QLabel( _("Select a new or existing directory to create a new Spyder " "project in it. To learn more about projects, take a look at " "our <a href=\"{0}\">documentation</a>.").format(projects_url)) self.description_label.setOpenExternalLinks(True) self.description_label.setWordWrap(True) self.groupbox = QGroupBox() self.radio_new_dir = QRadioButton(_("New directory")) self.radio_from_dir = QRadioButton(_("Existing directory")) self.label_project_name = QLabel(_('Project name')) self.label_location = QLabel(_('Location')) self.label_project_type = QLabel(_('Project type')) self.text_project_name = QLineEdit() self.text_location = QLineEdit(get_home_dir()) self.combo_project_type = QComboBox() self.label_information = QLabel("") self.label_information.hide() self.button_select_location = create_toolbutton( self, triggered=self.select_location, icon=ima.icon('DirOpenIcon'), tip=_("Select directory")) self.button_cancel = QPushButton(_('Cancel')) self.button_create = QPushButton(_('Create')) self.bbox = QDialogButtonBox(Qt.Horizontal) self.bbox.addButton(self.button_cancel, QDialogButtonBox.ActionRole) self.bbox.addButton(self.button_create, QDialogButtonBox.ActionRole) # Widget setup self.radio_new_dir.setChecked(True) self.text_location.setEnabled(True) self.text_location.setReadOnly(True) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) self.button_create.setEnabled(False) for (id_, name) in [(pt_id, pt.get_name()) for pt_id, pt in project_types.items()]: self.combo_project_type.addItem(name, id_) self.setWindowTitle(_('Create new project')) # Layouts layout_top = QHBoxLayout() layout_top.addWidget(self.radio_new_dir) layout_top.addSpacing(15) layout_top.addWidget(self.radio_from_dir) layout_top.addSpacing(200) self.groupbox.setLayout(layout_top) layout_grid = QGridLayout() layout_grid.addWidget(self.label_project_name, 0, 0) layout_grid.addWidget(self.text_project_name, 0, 1, 1, 2) layout_grid.addWidget(self.label_location, 1, 0) layout_grid.addWidget(self.text_location, 1, 1) layout_grid.addWidget(self.button_select_location, 1, 2) layout_grid.addWidget(self.label_project_type, 2, 0) layout_grid.addWidget(self.combo_project_type, 2, 1, 1, 2) layout_grid.addWidget(self.label_information, 3, 0, 1, 3) layout = QVBoxLayout() layout.addWidget(self.description_label) layout.addSpacing(3) layout.addWidget(self.groupbox) layout.addSpacing(8) layout.addLayout(layout_grid) layout.addSpacing(8) layout.addWidget(self.bbox) layout.setSizeConstraint(layout.SetFixedSize) self.setLayout(layout) # Signals and slots self.button_create.clicked.connect(self.create_project) self.button_cancel.clicked.connect(self.close) self.radio_from_dir.clicked.connect(self.update_location) self.radio_new_dir.clicked.connect(self.update_location) self.text_project_name.textChanged.connect(self.update_location) def select_location(self): """Select directory.""" location = osp.normpath( getexistingdirectory( self, _("Select directory"), self.location, )) if location and location != '.': if is_writable(location): self.location = location self.text_project_name.setText(osp.basename(location)) self.update_location() def update_location(self, text=''): """Update text of location and validate it.""" msg = '' path_validation = False path = self.location name = self.text_project_name.text().strip() # Setup self.text_project_name.setEnabled(self.radio_new_dir.isChecked()) self.label_information.setText('') self.label_information.hide() if name and self.radio_new_dir.isChecked(): # Allow to create projects only on new directories. path = osp.join(self.location, name) path_validation = not osp.isdir(path) if not path_validation: msg = _("This directory already exists!") elif self.radio_from_dir.isChecked(): # Allow to create projects in current directories that are not # Spyder projects. path = self.location path_validation = not osp.isdir(osp.join(path, '.spyproject')) if not path_validation: msg = _("This directory is already a Spyder project!") # Set path in text_location self.text_location.setText(path) # Validate project name with the method from the currently selected # project. project_type_id = self.combo_project_type.currentData() validate_func = self._project_types[project_type_id].validate_name project_name_validation, project_msg = validate_func(path, name) if not project_name_validation: if msg: msg = msg + '\n\n' + project_msg else: msg = project_msg # Set message if msg: self.label_information.show() self.label_information.setText('\n' + msg) # Allow to create project if validation was successful validated = path_validation and project_name_validation self.button_create.setEnabled(validated) # Set default state of buttons according to validation # Fixes spyder-ide/spyder#16745 if validated: self.button_create.setDefault(True) self.button_create.setAutoDefault(True) else: self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) def create_project(self): """Create project.""" self.project_data = { "root_path": self.text_location.text(), "project_type": self.combo_project_type.currentData(), } self.sig_project_creation_requested.emit( self.text_location.text(), self.combo_project_type.currentData(), [], ) self.accept()
class CondaPackagesWidget(QWidget): """Conda Packages Widget.""" sig_ready = Signal() sig_next_focus = Signal() # conda_packages_action_dict, pip_packages_action_dict sig_packages_action_requested = Signal(object, object) # button_widget, sender sig_channels_requested = Signal(object, object) # sender sig_update_index_requested = Signal(object) sig_cancel_requested = Signal(object) def __init__(self, parent, config=CONF): """Conda Packages Widget.""" super(CondaPackagesWidget, self).__init__(parent) self._parent = parent self._current_model_index = None self._current_action_name = '' self._current_table_scroll = None self._hide_widgets = False self.api = AnacondaAPI() self.prefix = None self.style_sheet = None self.message = '' self.config = config # Widgets self.bbox = QDialogButtonBox(Qt.Horizontal) self.button_cancel = ButtonPackageCancel('Cancel') self.button_channels = ButtonPackageChannels(_('Channels')) self.button_ok = ButtonPackageOk(_('Ok')) self.button_update = ButtonPackageUpdate(_('Update index...')) self.button_apply = ButtonPackageApply(_('Apply')) self.button_clear = ButtonPackageClear(_('Clear')) self.combobox_filter = ComboBoxPackageFilter(self) self.frame_top = FrameTabHeader() self.frame_bottom = FrameTabFooter() self.progress_bar = ProgressBarPackage(self) self.label_status = LabelPackageStatus(self) self.label_status_action = LabelPackageStatusAction(self) self.table = TableCondaPackages(self) self.textbox_search = LineEditSearch(self) self.widgets = [ self.button_update, self.button_channels, self.combobox_filter, self.textbox_search, self.table, self.button_ok, self.button_apply, self.button_clear ] self.table_first_row = FirstRowWidget( widget_before=self.textbox_search) self.table_last_row = LastRowWidget(widgets_after=[ self.button_apply, self.button_clear, self.button_cancel, ]) # Widgets setup max_height = self.label_status.fontMetrics().height() max_width = self.textbox_search.fontMetrics().width('M' * 23) self.bbox.addButton(self.button_ok, QDialogButtonBox.ActionRole) self.button_ok.setMaximumSize(QSize(0, 0)) self.button_ok.setVisible(False) self.button_channels.setCheckable(True) combo_items = [k for k in C.COMBOBOX_VALUES_ORDERED] self.combobox_filter.addItems(combo_items) self.combobox_filter.setMinimumWidth(120) self.progress_bar.setMaximumHeight(max_height * 1.2) self.progress_bar.setMaximumWidth(max_height * 12) self.progress_bar.setTextVisible(False) self.progress_bar.setVisible(False) self.setMinimumSize(QSize(480, 300)) self.setWindowTitle(_("Conda Package Manager")) self.label_status.setFixedHeight(max_height * 1.5) self.textbox_search.setMaximumWidth(max_width) self.textbox_search.setPlaceholderText('Search Packages') self.table_first_row.setMaximumHeight(0) self.table_last_row.setMaximumHeight(0) self.table_last_row.setVisible(False) self.table_first_row.setVisible(False) # Layout top_layout = QHBoxLayout() top_layout.addWidget(self.combobox_filter, 0, Qt.AlignCenter) top_layout.addWidget(SpacerHorizontal()) top_layout.addWidget(self.button_channels, 0, Qt.AlignCenter) top_layout.addWidget(SpacerHorizontal()) top_layout.addWidget(self.button_update, 0, Qt.AlignCenter) top_layout.addWidget(SpacerHorizontal()) top_layout.addWidget(self.textbox_search, 0, Qt.AlignCenter) top_layout.addStretch() self.frame_top.setLayout(top_layout) middle_layout = QVBoxLayout() middle_layout.addWidget(self.table_first_row) middle_layout.addWidget(self.table) middle_layout.addWidget(self.table_last_row) bottom_layout = QHBoxLayout() bottom_layout.addWidget(self.label_status_action) bottom_layout.addWidget(SpacerHorizontal()) bottom_layout.addWidget(self.label_status) bottom_layout.addStretch() bottom_layout.addWidget(self.progress_bar) bottom_layout.addWidget(SpacerHorizontal()) bottom_layout.addWidget(self.button_cancel) bottom_layout.addWidget(SpacerHorizontal()) bottom_layout.addWidget(self.button_apply) bottom_layout.addWidget(SpacerHorizontal()) bottom_layout.addWidget(self.button_clear) self.frame_bottom.setLayout(bottom_layout) layout = QVBoxLayout(self) layout.addWidget(self.frame_top) layout.addLayout(middle_layout) layout.addWidget(self.frame_bottom) self.setLayout(layout) # Signals and slots self.button_cancel.clicked.connect( lambda: self.sig_cancel_requested.emit(C.TAB_ENVIRONMENT)) self.combobox_filter.currentTextChanged.connect(self.filter_package) self.button_apply.clicked.connect(self.apply_multiple_actions) self.button_clear.clicked.connect(self.clear_actions) self.button_channels.clicked.connect(self.show_channels) self.button_update.clicked.connect(self.update_package_index) self.textbox_search.textChanged.connect(self.search_package) self.table.sig_actions_updated.connect(self.update_actions) self.table.sig_status_updated.connect(self.update_status) self.table.sig_next_focus.connect(self.table_last_row.handle_tab) self.table.sig_previous_focus.connect( lambda: self.table_first_row.widget_before.setFocus()) self.table_first_row.sig_enter_first.connect(self._handle_tab_focus) self.table_last_row.sig_enter_last.connect(self._handle_backtab_focus) self.button_cancel.setVisible(False) # --- Helpers # ------------------------------------------------------------------------- def _handle_tab_focus(self): self.table.setFocus() if self.table.proxy_model: index = self.table.proxy_model.index(0, 0) self.table.setCurrentIndex(index) def _handle_backtab_focus(self): self.table.setFocus() if self.table.proxy_model: row = self.table.proxy_model.rowCount() - 1 index = self.table.proxy_model.index(row, 0) self.table.setCurrentIndex(index) # --- Setup # ------------------------------------------------------------------------- def setup(self, packages=None, model_data=None, prefix=None): """ Setup packages. Populate the table with `packages` information. Parameters ---------- packages: dict Grouped package information by package name. blacklist: list of str List of conda package names to be excluded from the actual package manager view. """ self.table.setup_model(packages, model_data) combobox_text = self.combobox_filter.currentText() self.combobox_filter.setCurrentText(combobox_text) self.filter_package(combobox_text) self.table.setFocus() self.sig_ready.emit() # --- Other methods # ------------------------------------------------------------------------- def apply_multiple_actions(self): """Apply multiple actions on packages.""" logger.debug('') actions = self.table.get_actions() pip_actions = actions[C.PIP_PACKAGE] conda_actions = actions[C.CONDA_PACKAGE] self.sig_packages_action_requested.emit(conda_actions, pip_actions) def clear_actions(self): """Clear the table actions.""" self.table.clear_actions() def filter_package(self, value): """Filter packages by type.""" self.table.filter_status_changed(value) def search_package(self, text): """Search and filter packages by text.""" self.table.search_string_changed(text) def show_channels(self): """Show channel dialog.""" self.sig_channels_requested.emit( self.button_channels, C.TAB_ENVIRONMENT, ) def update_actions(self, number_of_actions): """Update visibility of buttons based on actions.""" self.button_apply.setVisible(bool(number_of_actions)) self.button_clear.setVisible(bool(number_of_actions)) def update_package_index(self): """Update pacakge index.""" self.sig_update_index_requested.emit(C.ENVIRONMENT_PACKAGE_MANAGER) # --- Common methods # ------------------------------------------------------------------------- def ordered_widgets(self): pass def set_widgets_enabled(self, value): """Set the enabled status of widgets and subwidgets.""" self.table.setEnabled(value) self.button_clear.setEnabled(value) self.button_apply.setEnabled(value) self.button_cancel.setEnabled(not value) self.button_cancel.setVisible(not value) def update_status(self, action='', message='', value=None, max_value=None): """ Update status of package widget. - progress == None and max_value == None -> Not Visible - progress == 0 and max_value == 0 -> Busy - progress == n and max_value == m -> Progress values """ self.label_status_action.setText(action) self.label_status.setText(message) if max_value is None and value is None: self.progress_bar.setVisible(False) else: self.progress_bar.setVisible(True) self.progress_bar.setMaximum(max_value) self.progress_bar.setValue(value) def update_style_sheet(self, style_sheet=None): """Update custom CSS style sheet.""" if style_sheet is None: self.style_sheet = load_style_sheet() else: self.style_sheet = style_sheet self.setStyleSheet(self.style_sheet)
class BasePlotCurveEditorDialog(QDialog): """QDialog that is used in Qt Designer to edit the properties of the curves in a waveform plot. This dialog is shown when you double-click the plot, or when you right click it and choose 'edit curves'. This thing is mostly just a wrapper for a table view, with a couple buttons to add and remove curves, and a button to save the changes.""" TABLE_MODEL_CLASS = BasePlotCurvesModel def __init__(self, plot, parent=None): super(BasePlotCurveEditorDialog, self).__init__(parent) self.plot = plot self.setup_ui() self.table_model = self.TABLE_MODEL_CLASS(self.plot) self.table_view.setModel(self.table_model) self.table_model.plot = plot # self.table_view.resizeColumnsToContents() self.add_button.clicked.connect(self.addCurve) self.remove_button.clicked.connect(self.removeSelectedCurve) self.remove_button.setEnabled(False) self.table_view.selectionModel().selectionChanged.connect( self.handleSelectionChange) self.table_view.doubleClicked.connect(self.handleDoubleClick) self.resize(800, 300) def setup_ui(self): self.vertical_layout = QVBoxLayout(self) self.table_view = QTableView(self) self.table_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.table_view.setProperty("showDropIndicator", False) self.table_view.setDragDropOverwriteMode(False) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSortingEnabled(False) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.verticalHeader().setVisible(False) self.table_view.setColumnWidth(0, 160) self.table_view.setColumnWidth(1, 160) self.table_view.setColumnWidth(2, 160) self.vertical_layout.addWidget(self.table_view) self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("Add Curve", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove Curve", self) self.add_remove_layout.addWidget(self.remove_button) self.vertical_layout.addLayout(self.add_remove_layout) self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Done", QDialogButtonBox.AcceptRole) self.vertical_layout.addWidget(self.button_box) self.button_box.accepted.connect(self.saveChanges) self.button_box.rejected.connect(self.reject) self.setWindowTitle("Waveform Curve Editor") def setup_delegate_columns(self, index=2): symbol_delegate = SymbolColumnDelegate(self) self.table_view.setItemDelegateForColumn(index+3, symbol_delegate) line_delegate = LineColumnDelegate(self) self.table_view.setItemDelegateForColumn(index+1, line_delegate) color_delegate = ColorColumnDelegate(self) self.table_view.setItemDelegateForColumn(index, color_delegate) @Slot() def addCurve(self): self.table_model.append() @Slot() def removeSelectedCurve(self): self.table_model.removeAtIndex(self.table_view.currentIndex()) @Slot(QItemSelection, QItemSelection) def handleSelectionChange(self, selected, deselected): self.remove_button.setEnabled( self.table_view.selectionModel().hasSelection()) @Slot(QModelIndex) def handleDoubleClick(self, index): if self.table_model.needsColorDialog(index): # The table model returns a QBrush for BackgroundRole, not a QColor init_color = self.table_model.data(index, Qt.BackgroundRole).color() color = QColorDialog.getColor(init_color, self) if color.isValid(): self.table_model.setData(index, color, role=Qt.EditRole) @Slot() def saveChanges(self): formWindow = QDesignerFormWindowInterface.findFormWindow(self.plot) if formWindow: formWindow.cursor().setProperty("curves", self.plot.curves) self.accept()