def create_tabwidget(self): """Create a new QTabWidget with a button to add new tabs""" tabs = QTabWidget(self) tabs.setMovable(True) tabs.setTabsClosable(True) # create a button to add new tabs plus_btn = QToolButton(tabs) plus_btn.setText('+') plus_btn.clicked.connect(self.plus_button_clicked) tabs.setCornerWidget(plus_btn, Qt.TopLeftCorner) tabs.tabCloseRequested.connect(self.close_tab) return tabs
def __init__(self): QToolButton.__init__(self) self.setIcon(get_std_icon('MessageBoxInformation')) style = """ QToolButton { border: 1px solid grey; padding:0px; border-radius: 2px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f6f7fa, stop: 1 #dadbde); } """ self.setStyleSheet(style)
def __init__(self, parent=None, icon=True): super(SearchLineEdit, self).__init__(parent) self.setTextMargins(1, 0, 20, 0) if icon: self.setTextMargins(18, 0, 20, 0) self._label = QLabel(self) self._pixmap_icon = QPixmap(get_image_path('conda_search.png')) self._label.setPixmap(self._pixmap_icon) self._label.setStyleSheet('''border: 0px; padding-bottom: 0px; padding-left: 2px;''') self._pixmap = QPixmap(get_image_path('conda_del.png')) self.button_clear = QToolButton(self) self.button_clear.setIcon(QIcon(self._pixmap)) self.button_clear.setIconSize(QSize(18, 18)) self.button_clear.setCursor(Qt.ArrowCursor) self.button_clear.setStyleSheet("""QToolButton {background: transparent; padding-right: 2px; border: none; margin:0px; }""") self.button_clear.setVisible(False) # Layout self._layout = QHBoxLayout(self) self._layout.addWidget(self.button_clear, 0, Qt.AlignRight) self._layout.setSpacing(0) self._layout.setContentsMargins(0, 2, 2, 0) # Signals and slots self.button_clear.clicked.connect(self.clear_text) self.textChanged.connect(self._toggle_visibility) self.textEdited.connect(self._toggle_visibility)
def action2button(action, autoraise=True, text_beside_icon=False, parent=None): """Create a QToolButton directly from a QAction object""" if parent is None: parent = action.parent() button = QToolButton(parent) button.setDefaultAction(action) button.setAutoRaise(autoraise) if text_beside_icon: button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) return button
class CustomDateEdit(QWidget): def __init__(self): QWidget.__init__(self) self._line_edit = ClearableLineEdit() self._calendar_button = QToolButton() self._calendar_button.setPopupMode(QToolButton.InstantPopup) self._calendar_button.setFixedSize(26, 26) self._calendar_button.setAutoRaise(True) self._calendar_button.setIcon(resourceIcon("calendar.png")) self._calendar_button.setStyleSheet( "QToolButton::menu-indicator { image: none; }") tool_menu = QMenu(self._calendar_button) self._calendar_widget = QCalendarWidget(tool_menu) action = QWidgetAction(tool_menu) action.setDefaultWidget(self._calendar_widget) tool_menu.addAction(action) self._calendar_button.setMenu(tool_menu) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._line_edit) layout.addWidget(self._calendar_button) self.setLayout(layout) self._calendar_widget.activated.connect(self.setDate) def setDate(self, date): if isinstance(date, datetime.date): date = QDate(date.year, date.month, date.day) if date is not None and date.isValid(): self._line_edit.setText(str(date.toString("yyyy-MM-dd"))) else: self._line_edit.setText("") def date(self): date_string = self._line_edit.text() if len(str(date_string).strip()) > 0: date = QDate.fromString(date_string, "yyyy-MM-dd") if date.isValid(): return datetime.date(date.year(), date.month(), date.day()) return None
def __init__(self, *args, **kwargs): super(MOSViewerToolbar, self).__init__(*args, **kwargs) # self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) # Define icon path icon_path = os.path.join(os.path.dirname(__file__), 'ui', 'icons') # Define the toolbar actions self.cycle_previous_action = QAction( QIcon(os.path.join(icon_path, "Previous-96.png")), "Previous", self) self.cycle_next_action = QAction( QIcon(os.path.join(icon_path, "Next-96.png")), "Next", self) # Include the dropdown widget self.source_select = QComboBox() # Add the items to the toolbar self.addAction(self.cycle_previous_action) self.addAction(self.cycle_next_action) self.addWidget(self.source_select) # Include a button to open spectrum in specviz self.open_specviz = QAction( QIcon(os.path.join(icon_path, "External-96.png")), "Open in SpecViz", self) # Create a tool button to hold the lock axes menu object tool_button = QToolButton(self) tool_button.setText("Axes Settings") tool_button.setIcon(QIcon(os.path.join(icon_path, "Settings-96.png"))) tool_button.setPopupMode(QToolButton.MenuButtonPopup) # Create a menu for the axes settings drop down self.settings_menu = QMenu(self) # Add lock x axis action self.lock_x_action = QAction("Lock X Axis", self.settings_menu) self.lock_x_action.setCheckable(True) # Add lock y axis action self.lock_y_action = QAction("Lock Y Axis", self.settings_menu) self.lock_y_action.setCheckable(True) # Add the actions to the menu self.settings_menu.addAction(self.lock_x_action) self.settings_menu.addAction(self.lock_y_action) # Set the menu object on the tool button tool_button.setMenu(self.settings_menu) # Create a widget action object to hold the tool button, this way the # toolbar behaves the way it's expected to tool_button_action = QWidgetAction(self) tool_button_action.setDefaultWidget(tool_button) self.addAction(tool_button_action) self.addSeparator() self.addAction(self.open_specviz)
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)
class ExplorerWidget(QWidget): """Explorer widget.""" sig_option_changed = Signal(str, object) sig_open_file = Signal(str) sig_add_to_project = Signal(str) open_dir = Signal(str) def __init__( self, parent=None, name_filters=['*.py', '*.pyw'], show_all=True, show_cd_only=None, show_icontext=False, home=None, ): """Explorer widget.""" QWidget.__init__(self, parent) self.home = home # Widgets self.treewidget = ExplorerTreeWidget(self, show_cd_only=show_cd_only) self.button_home = QToolButton(self) self.button_home.setFocusPolicy(Qt.StrongFocus) self.label_breadcrumb = QLabel() home_action = create_action( self, text="Home", icon=QIcon(), # ima.icon('ArrowBack'), triggered=self.go_home) # Setup widgets self.treewidget.setup(name_filters=name_filters, show_all=show_all) self.treewidget.chdir(getcwd()) self.button_home.setDefaultAction(home_action) # Layouts blayout = QHBoxLayout() blayout.addWidget(self.button_home) blayout.addWidget(self.label_breadcrumb) blayout.addStretch() layout = QVBoxLayout() layout.addLayout(blayout) layout.addWidget(self.treewidget) self.setLayout(layout) # Signals and slots self.treewidget.sig_add_to_project.connect(self.sig_add_to_project) self.treewidget.sig_changed_dir.connect(self.update_breadcrumb) self.label_breadcrumb.linkActivated.connect(self.go_to) self.update_breadcrumb() def go_to(self, path): """Got to breadcrumb path.""" self.treewidget.chdir(path) self.update_breadcrumb() def update_breadcrumb(self, path=None): """Update the breadcrumb naviagtion.""" path = path or getcwd() parts = path.split(os.path.sep) text = '<style>a{color:#43b02a;}</style>' cum_path = '' for p in parts: cum_path += p + os.path.sep text += '<a href="{link}">{p}</a>'.format(p=p, link=cum_path) text += ' ' + os.path.sep + ' ' self.label_breadcrumb.setText(text) def set_current_folder(self, path): """Set the current folder.""" self.treewidget.set_current_folder(path) self.treewidget.chdir(path) self.treewidget.project_path = path self.update_breadcrumb() def set_home(self, home): """Set the editor home.""" self.home = home def go_home(self): """Set treewidget to project folder (home).""" if self.home and os.path.isdir(self.home): self.treewidget.set_current_folder(self.home) self.update_breadcrumb(self.home) def ordered_widgets(self): """Return a list of the ordered widgets.""" ordered_widgets = [self.button_home, self.treewidget] return ordered_widgets @Slot(bool) def toggle_icontext(self, state): """Toggle icon text.""" self.sig_option_changed.emit('show_icontext', state) for widget in self.action_widgets: if widget is not self.button_menu: if state: widget.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) else: widget.setToolButtonStyle(Qt.ToolButtonIconOnly)
def __init__(self, parent, opacity, duration, easing_curve, tour=None): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.tour = tour self.frames = None self.color_top = QColor.fromRgb(230, 230, 230) self.color_back = QColor.fromRgb(255, 255, 255) self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets def toolbutton(icon): bt = QToolButton() bt.setAutoRaise(True) bt.setIcon(icon) return bt self.button_close = toolbutton(ima.icon("tour.close")) self.button_home = toolbutton(ima.icon("tour.home")) self.button_previous = toolbutton(ima.icon("tour.previous")) self.button_end = toolbutton(ima.icon("tour.end")) self.button_next = toolbutton(ima.icon("tour.next")) self.button_run = QPushButton(_('Run code')) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current] arrow = get_image_path('hide.png') self.stylesheet = '''QComboBox { padding-left: 5px; background-color: rgbs(230,230,230,100%); border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; } QComboBox::down-arrow { image: url(''' + arrow + '''); } ''' # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace('\\', '/') self.setFocusPolicy(Qt.StrongFocus) for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets, self.setFocus]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu)
class SimulationPanel(QWidget): def __init__(self, config_file): QWidget.__init__(self) self._config_file = config_file self._ee_config = None if FeatureToggling.is_enabled("prefect") or FeatureToggling.is_enabled( "ensemble-evaluator"): self._ee_config = EvaluatorServerConfig() self.setObjectName("Simulation_panel") layout = QVBoxLayout() self._simulation_mode_combo = QComboBox() self._simulation_mode_combo.setObjectName("Simulation_mode") addHelpToWidget(self._simulation_mode_combo, "run/simulation_mode") self._simulation_mode_combo.currentIndexChanged.connect( self.toggleSimulationMode) simulation_mode_layout = QHBoxLayout() simulation_mode_layout.addSpacing(10) simulation_mode_layout.addWidget(QLabel("Simulation mode:"), 0, Qt.AlignVCenter) simulation_mode_layout.addWidget(self._simulation_mode_combo, 0, Qt.AlignVCenter) simulation_mode_layout.addSpacing(20) self.run_button = QToolButton() self.run_button.setObjectName('start_simulation') self.run_button.setIconSize(QSize(32, 32)) self.run_button.setText("Start Simulation") self.run_button.setIcon(resourceIcon("ide/gear_in_play")) self.run_button.clicked.connect(self.runSimulation) self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) addHelpToWidget(self.run_button, "run/start_simulation") simulation_mode_layout.addWidget(self.run_button) simulation_mode_layout.addStretch(1) layout.addSpacing(5) layout.addLayout(simulation_mode_layout) layout.addSpacing(10) self._simulation_stack = QStackedWidget() self._simulation_stack.setLineWidth(1) self._simulation_stack.setFrameStyle(QFrame.StyledPanel) layout.addWidget(self._simulation_stack) self._simulation_widgets = OrderedDict() """ :type: OrderedDict[BaseRunModel,SimulationConfigPanel]""" if not FeatureToggling.is_enabled("prefect"): self.addSimulationConfigPanel(SingleTestRunPanel()) self.addSimulationConfigPanel(EnsembleExperimentPanel()) if FeatureToggling.is_enabled("prefect"): prefect_config = os.path.basename(config_file) self.addSimulationConfigPanel( PrefectEnsembleExperimentPanel(prefect_config)) if ERT.ert.have_observations( ) and not FeatureToggling.is_enabled("prefect"): self.addSimulationConfigPanel(EnsembleSmootherPanel()) self.addSimulationConfigPanel(MultipleDataAssimilationPanel()) self.addSimulationConfigPanel(IteratedEnsembleSmootherPanel()) self.setLayout(layout) def addSimulationConfigPanel(self, panel): assert isinstance(panel, SimulationConfigPanel) self._simulation_stack.addWidget(panel) simulation_model = panel.getSimulationModel() self._simulation_widgets[simulation_model] = panel self._simulation_mode_combo.addItem(simulation_model.name(), simulation_model) panel.simulationConfigurationChanged.connect( self.validationStatusChanged) def getActions(self): return [] def getCurrentSimulationModel(self): return self._simulation_mode_combo.itemData( self._simulation_mode_combo.currentIndex(), Qt.UserRole) def getSimulationArguments(self): """ @rtype: dict[str,object]""" simulation_widget = self._simulation_widgets[ self.getCurrentSimulationModel()] args = simulation_widget.getSimulationArguments() if self._ee_config is not None: args.update({"ee_config": self._ee_config}) return args def runSimulation(self): case_name = getCurrentCaseName() message = "Are you sure you want to use case '%s' for initialization of the initial ensemble when running the simulations?" % case_name start_simulations = QMessageBox.question( self, "Start simulations?", message, QMessageBox.Yes | QMessageBox.No) if start_simulations == QMessageBox.Yes: run_model = self.getCurrentSimulationModel() arguments = self.getSimulationArguments() dialog = RunDialog(self._config_file, run_model(), arguments) dialog.startSimulation() dialog.exec_() ERT.emitErtChange() # simulations may have added new cases. def toggleSimulationMode(self): current_model = self.getCurrentSimulationModel() if current_model is not None: widget = self._simulation_widgets[self.getCurrentSimulationModel()] self._simulation_stack.setCurrentWidget(widget) self.validationStatusChanged() def validationStatusChanged(self): widget = self._simulation_widgets[self.getCurrentSimulationModel()] self.run_button.setEnabled(widget.isConfigurationValid())
class SearchLineEdit(QLineEdit): """Line edit search widget with icon and remove all button""" def __init__(self, parent=None, icon=True): super(SearchLineEdit, self).__init__(parent) self.setTextMargins(1, 0, 20, 0) if icon: self.setTextMargins(18, 0, 20, 0) self._label = QLabel(self) self._pixmap_icon = QPixmap(get_image_path('conda_search.png')) self._label.setPixmap(self._pixmap_icon) self._label.setStyleSheet('''border: 0px; padding-bottom: 0px; padding-left: 2px;''') self._pixmap = QPixmap(get_image_path('conda_del.png')) self.button_clear = QToolButton(self) self.button_clear.setIcon(QIcon(self._pixmap)) self.button_clear.setIconSize(QSize(18, 18)) self.button_clear.setCursor(Qt.ArrowCursor) self.button_clear.setStyleSheet("""QToolButton {background: transparent; padding-right: 2px; border: none; margin:0px; }""") self.button_clear.setVisible(False) # Layout self._layout = QHBoxLayout(self) self._layout.addWidget(self.button_clear, 0, Qt.AlignRight) self._layout.setSpacing(0) self._layout.setContentsMargins(0, 2, 2, 0) # Signals and slots self.button_clear.clicked.connect(self.clear_text) self.textChanged.connect(self._toggle_visibility) self.textEdited.connect(self._toggle_visibility) def _toggle_visibility(self): """ """ if len(self.text()) == 0: self.button_clear.setVisible(False) else: self.button_clear.setVisible(True) def sizeHint(self): return QSize(200, self._pixmap_icon.height()) # Public api # ---------- def clear_text(self): """ """ self.setText('') self.setFocus()
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[:] try: import spyderlib.utils.icon_manager as ima icon_options = ima.icon('tooloptions') except Exception: import qtawesome as qta icon_options = qta.icon('fa.cog') # Widgets self.cancel_dialog = ClosePackageManagerDialog self.bbox = QDialogButtonBox(Qt.Horizontal) self.button_cancel = QPushButton('Cancel') self.button_channels = QPushButton(_('Channels')) self.button_ok = QPushButton(_('Ok')) self.button_update = QPushButton(_('Update index...')) self.button_apply = QPushButton(_('Apply')) self.button_clear = QPushButton(_('Clear')) self.button_options = QToolButton() 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.button_options] 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]) # Widget setup self.button_options.setPopupMode(QToolButton.InstantPopup) self.button_options.setIcon(icon_options) self.button_options.setAutoRaise(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.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.addWidget(self.button_options) 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) layout = QVBoxLayout(self) layout.addLayout(top_layout) layout.addLayout(middle_layout) layout.addLayout(bottom_layout) layout.addSpacing(6) 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) # New api def show_login_dialog(self): pass def show_options_menu(self): pass
def add_tool_button(self, name: str, text: str, icon_path: str = ''): from pmgwidgets import create_icon b = QToolButton() b.setText(text) icon = create_icon(icon_path) b.setIcon(icon) b.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) b.setIconSize(QSize(40, 40)) b.setMaximumWidth(80) b.setMinimumWidth(80) b.setMinimumHeight(60) b.setMaximumHeight(60) self.add_widget(b) return b
class WorkflowWidget(QWidget): sigAddFunction = Signal(object) sigRunWorkflow = Signal() # TODO -- emit Workflow from sigRunWorkflow def __init__(self, workflowview: QAbstractItemView, operation_filter: Callable[[OperationPlugin], bool] = None): super(WorkflowWidget, self).__init__() self.operation_filter = operation_filter self.view = workflowview self.autorun_checkbox = QCheckBox("Run Automatically") self.autorun_checkbox.setCheckState(Qt.Unchecked) self.autorun_checkbox.stateChanged.connect(self._autorun_state_changed) self.run_button = QPushButton("Run Workflow") self.run_button.clicked.connect(self.sigRunWorkflow.emit) self.view.model().workflow.attach(self._autorun) # TODO -- actually hook up the auto run OR dependent class needs to connect (see SAXSGUIPlugin) self.toolbar = QToolBar() self.addfunctionmenu = QToolButton() self.addfunctionmenu.setIcon(QIcon(path("icons/addfunction.png"))) self.addfunctionmenu.setText("Add Function") # Defer menu population to once the plugins have been loaded; otherwise, the menu may not contain anything # if this widget is init'd before all plugins have been loaded. self.functionmenu = QMenu() self.functionmenu.aboutToShow.connect(self.populateFunctionMenu) self.addfunctionmenu.setMenu(self.functionmenu) self.addfunctionmenu.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(self.addfunctionmenu) # self.toolbar.addAction(QIcon(path('icons/up.png')), 'Move Up') # self.toolbar.addAction(QIcon(path('icons/down.png')), 'Move Down') self.toolbar.addAction(QIcon(path("icons/folder.png")), "Load Workflow") self.toolbar.addAction(QIcon(path("icons/trash.png")), "Delete Operation", self.deleteOperation) v = QVBoxLayout() v.addWidget(self.view) h = QHBoxLayout() h.addWidget(self.autorun_checkbox) h.addWidget(self.run_button) v.addLayout(h) v.addWidget(self.toolbar) v.setContentsMargins(0, 0, 0, 0) self.setLayout(v) def _autorun_state_changed(self, state): if state == Qt.Checked: self.run_button.setDisabled(True) else: self.run_button.setDisabled(False) def _autorun(self): if self.autorun_checkbox.isChecked(): self.sigRunWorkflow.emit() def populateFunctionMenu(self): self.functionmenu.clear() sortingDict = MenuDict() operations = pluginmanager.get_plugins_of_type("OperationPlugin") if self.operation_filter is not None: operations = filter(self.operation_filter, operations) for operation in operations: categories = operation.categories if not categories: categories = [("Uncategorized", ) ] # put found operations into a default category for categories_tuple in categories: if isinstance(categories_tuple, str): categories_tuple = (categories_tuple, ) submenu = sortingDict categories_list = list(categories_tuple) while categories_list: category = categories_list.pop(0) submenu = submenu[category] submenu['___'].append(operation) self._mkMenu(sortingDict) def _mkMenu(self, sorting_dict, menu=None): if menu is None: menu = self.functionmenu menu.clear() for key in sorting_dict: if key == '___': menu.addSeparator() for operation in sorting_dict['___']: menu.addAction( operation.name, partial(self.addOperation, operation, autoconnectall=True)) else: submenu = QMenu(title=key, parent=menu) menu.addMenu(submenu) self._mkMenu(sorting_dict[key], submenu) def addOperation(self, operation: OperationPlugin, autoconnectall=True): self.view.model().workflow.add_operation(operation()) if autoconnectall: self.view.model().workflow.auto_connect_all() print("selected new row:", self.view.model().rowCount() - 1) self.view.setCurrentIndex(self.view.model().index( self.view.model().rowCount() - 1, 0)) def deleteOperation(self): index = self.view.currentIndex() operation = self.view.model().workflow.operations[index.row()] self.view.model().workflow.remove_operation(operation) self.view.setCurrentIndex(QModelIndex())
def toolbutton(icon): bt = QToolButton() bt.setAutoRaise(True) bt.setIcon(icon) return bt
def __init__(self, parent, opacity, duration, easing_curve, tour=None, color_top=None, color_back=None, combobox_background=None): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.tour = tour self.frames = None self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets def toolbutton(icon): bt = QToolButton() bt.setAutoRaise(True) bt.setIcon(icon) return bt self.button_close = toolbutton(ima.icon("tour.close")) self.button_home = toolbutton(ima.icon("tour.home")) self.button_previous = toolbutton(ima.icon("tour.previous")) self.button_end = toolbutton(ima.icon("tour.end")) self.button_next = toolbutton(ima.icon("tour.next")) self.button_run = QPushButton(_('Run code')) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current] arrow = get_image_path('hide.png') self.color_top = color_top self.color_back = color_back self.combobox_background = combobox_background self.stylesheet = '''QComboBox {{ padding-left: 5px; background-color: {} border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; }} QComboBox::drop-down {{ subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; }} QComboBox::down-arrow {{ image: url({}); }} '''.format(self.combobox_background.name(), arrow) # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace('\\', '/') self.setFocusPolicy(Qt.StrongFocus) for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets, self.setFocus]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu)
class PlotToolBar(QToolBar): """ This is a separate tool bar due to the fact that you cannot add custom widget objects (e.g. menus) to the native tool bar within Qt Creator. """ def __init__(self, *args): super(PlotToolBar, self).__init__(*args) # Disable moving self.setMovable(False) # Window menu self.window_menu = QMenu() self.atn_change_top_axis = self.window_menu.addAction( "Change Top Axis") self.atn_change_units = self.window_menu.addAction("Change Units") self.atn_line_ids = self.window_menu.addAction("Plot Line IDs") icon = QIcon(QPixmap(":/img/Settings-50.png")) self.window_menu_btn = QToolButton(self) self.window_menu_btn.setIcon(icon) self.window_menu_btn.setMenu(self.window_menu) self.window_menu_btn.setPopupMode(QToolButton.InstantPopup) self.addWidget(self.window_menu_btn) # Layer menu self.layer_menu = QMenu() # self.layer_menu.addAction("Color") icon = QIcon(QPixmap(":/img/Settings 3-50.png")) self.layer_menu_btn = QToolButton(self) self.layer_menu_btn.setIcon(icon) self.layer_menu_btn.setMenu(self.layer_menu) self.layer_menu_btn.setPopupMode(QToolButton.InstantPopup) self.addWidget(self.layer_menu_btn)
def __init__(self, presenter: IDataViewSubscriber, dims_info, can_normalise, parent=None, conf=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots_active = False self.can_normalise = can_normalise self.nonortho_transform = None self.conf = conf self._line_plots = None self._image_info_tracker = None self._region_selection_on = False self._orig_lims = None self.colorbar_layout = QVBoxLayout() self.colorbar_layout.setContentsMargins(0, 0, 0, 0) self.colorbar_layout.setSpacing(0) self.image_info_widget = ImageInfoWidget(self) self.image_info_widget.setToolTip("Information about the selected pixel") self.track_cursor = QCheckBox("Track Cursor", self) self.track_cursor.setToolTip( "Update the image readout table when the cursor is over the plot. " "If unticked the table will update only when the plot is clicked") self.track_cursor.setChecked(True) self.track_cursor.stateChanged.connect(self.on_track_cursor_state_change) # Dimension widget self.dimensions_layout = QGridLayout() self.dimensions_layout.setHorizontalSpacing(10) if (dims_info): self.create_dimensions(dims_info) else: self.dimensions = None # normalization options if can_normalise: self.norm_label = QLabel("Normalization") self.colorbar_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.colorbar_layout.addWidget(self.norm_opts) # MPL figure + colorbar self.fig = Figure() self.ax = None self.image = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = SliceViewerCanvas(self.fig) self.canvas.mpl_connect('button_release_event', self.mouse_release) self.canvas.mpl_connect('button_press_event', self.presenter.canvas_clicked) self.canvas.mpl_connect('key_press_event', self.presenter.key_pressed) self.canvas.mpl_connect('motion_notify_event', self.presenter.mouse_moved) self.colorbar_label = QLabel("Colormap") self.colorbar_layout.addWidget(self.colorbar_label) norm_scale = self.get_default_scale_norm() self.colorbar = ColorbarWidget(self, norm_scale) self.colorbar.cmap.setToolTip("Colormap options") self.colorbar.crev.setToolTip("Reverse colormap") self.colorbar.norm.setToolTip("Colormap normalisation options") self.colorbar.powerscale.setToolTip("Power colormap scale") self.colorbar.cmax.setToolTip("Colormap maximum limit") self.colorbar.cmin.setToolTip("Colormap minimum limit") self.colorbar.autoscale.setToolTip("Automatically changes colormap limits when zooming on the plot") self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.scaleNormChanged.connect(self.scale_norm_changed) # make width larger to fit image readout table self.colorbar.setMaximumWidth(200) # MPL toolbar self.toolbar_layout = QHBoxLayout() self.mpl_toolbar = SliceViewerNavigationToolbar(self.canvas, self, False) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.on_line_plots_toggle) self.mpl_toolbar.regionSelectionClicked.connect(self.on_region_selection_toggle) self.mpl_toolbar.homeClicked.connect(self.on_home_clicked) self.mpl_toolbar.nonOrthogonalClicked.connect(self.on_non_orthogonal_axes_toggle) self.mpl_toolbar.zoomPanClicked.connect(self.presenter.zoom_pan_clicked) self.mpl_toolbar.zoomPanFinished.connect(self.on_data_limits_changed) self.toolbar_layout.addWidget(self.mpl_toolbar) # Status bar self.status_bar = QStatusBar(parent=self) self.status_bar.setStyleSheet('QStatusBar::item {border: None;}') # Hide spacers between button and label self.status_bar_label = QLabel() self.help_button = QToolButton() self.help_button.setText("?") self.status_bar.addWidget(self.help_button) self.status_bar.addWidget(self.status_bar_label) # layout layout = QGridLayout(self) layout.setSpacing(1) layout.addLayout(self.dimensions_layout, 0, 0, 1, 2) layout.addLayout(self.toolbar_layout, 1, 0, 1, 1) layout.addLayout(self.colorbar_layout, 1, 1, 3, 1) layout.addWidget(self.canvas, 2, 0, 1, 1) layout.addWidget(self.status_bar, 3, 0, 1, 1) layout.setRowStretch(2, 1)
def create_toolbutton(parent, text=None, shortcut=None, icon=None, tip=None, toggled=None, triggered=None, autoraise=True, text_beside_icon=False): """Create a QToolButton""" button = QToolButton(parent) if text is not None: button.setText(text) if icon is not None: if is_text_string(icon): icon = get_icon(icon) button.setIcon(icon) if text is not None or tip is not None: button.setToolTip(text if tip is None else tip) if text_beside_icon: button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setAutoRaise(autoraise) if triggered is not None: button.clicked.connect(triggered) if toggled is not None: button.toggled.connect(toggled) button.setCheckable(True) if shortcut is not None: button.setShortcut(shortcut) return button
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[:] try: import spyderlib.utils.icon_manager as ima icon_options = ima.icon('tooloptions') except Exception: import qtawesome as qta icon_options = qta.icon('fa.cog') # Widgets self.cancel_dialog = ClosePackageManagerDialog self.bbox = QDialogButtonBox(Qt.Horizontal) self.button_cancel = QPushButton('Cancel') self.button_channels = QPushButton(_('Channels')) self.button_ok = QPushButton(_('Ok')) self.button_update = QPushButton(_('Update index...')) self.button_apply = QPushButton(_('Apply')) self.button_clear = QPushButton(_('Clear')) self.button_options = QToolButton() 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.button_options] 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]) # Widget setup self.button_options.setPopupMode(QToolButton.InstantPopup) self.button_options.setIcon(icon_options) self.button_options.setAutoRaise(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.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.addWidget(self.button_options) 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) layout = QVBoxLayout(self) layout.addLayout(top_layout) layout.addLayout(middle_layout) layout.addLayout(bottom_layout) layout.addSpacing(6) 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()
class ChannelPreview(QWidget): """ class for preview single colormap. Witch checkbox for change selection. :param colormap: colormap to show :param accepted: if checkbox should be checked :param name: name which will be emitted in all signals as firs argument """ selection_changed = Signal(str, bool) """checkbox selection changed (name)""" edit_request = Signal([str], [ColorMap]) """send after pressing edit signal (name) (ColorMap object)""" remove_request = Signal(str) """Signal with name of colormap (name)""" def __init__(self, colormap: BaseColormap, accepted: bool, name: str, removable: bool = False, used: bool = False): super().__init__() self.image = convert_colormap_to_image(colormap) self.name = name self.removable = removable self.checked = QCheckBox() self.checked.setChecked(accepted) self.checked.setDisabled(used) self.setMinimumWidth(80) metrics = QFontMetrics(QFont()) layout = QHBoxLayout() layout.addWidget(self.checked) layout.addStretch(1) self.remove_btn = QToolButton() self.remove_btn.setIcon(_icon_selector.close_icon) if removable: self.remove_btn.setToolTip("Remove colormap") else: self.remove_btn.setToolTip("This colormap is protected") self.remove_btn.setEnabled(not accepted and self.removable) self.edit_btn = QToolButton() self.edit_btn.setIcon(_icon_selector.edit_icon) layout.addWidget(self.remove_btn) layout.addWidget(self.edit_btn) self.setLayout(layout) self.checked.stateChanged.connect(self._selection_changed) self.edit_btn.clicked.connect(partial(self.edit_request.emit, name)) if isinstance(colormap, ColorMap): self.edit_btn.clicked.connect( partial(self.edit_request[ColorMap].emit, colormap)) self.edit_btn.setToolTip("Create colormap base on this") else: self.edit_btn.setDisabled(True) self.edit_btn.setToolTip("This colormap is not editable") self.remove_btn.clicked.connect(partial(self.remove_request.emit, name)) self.setMinimumHeight( max(metrics.height(), self.edit_btn.minimumHeight(), self.checked.minimumHeight()) + 20) def _selection_changed(self, _=None): chk = self.checked.isChecked() self.selection_changed.emit(self.name, chk) self.remove_btn.setEnabled(not chk and self.removable) def set_blocked(self, block): """Set if block possibility of remove or uncheck """ self.checked.setDisabled(block) if self.removable and not block: self.remove_btn.setToolTip("Remove colormap") else: self.remove_btn.setToolTip("This colormap is protected") self.remove_btn.setDisabled(True) @property def state_changed(self): """Inner checkbox stateChanged signal""" return self.checked.stateChanged @property def is_checked(self): """If colormap is selected""" return self.checked.isChecked() def set_chosen(self, state: bool): """Set selection of check box.""" self.checked.setChecked(state) def paintEvent(self, event: QPaintEvent): painter = QPainter(self) start = 2 * self.checked.x() + self.checked.width() end = self.remove_btn.x() - self.checked.x() rect = self.rect() rect.setX(start) rect.setWidth(end - start) painter.drawImage(rect, self.image) super().paintEvent(event)
def __init__(self, *args, **kwargs): super(MOSViewerToolbar, self).__init__(*args, **kwargs) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) # Define the toolbar actions self.cycle_previous_action = QAction( QIcon(os.path.join(ICON_DIR, "Previous-96.png")), "Previous", self) self.cycle_next_action = QAction( QIcon(os.path.join(ICON_DIR, "Next-96.png")), "Next", self) # Include the dropdown widget self.source_select = QComboBox() # Add the items to the toolbar self.addAction(self.cycle_previous_action) self.addAction(self.cycle_next_action) self.addWidget(self.source_select) # Include a button to open spectrum in specviz self.open_specviz = QAction( QIcon(os.path.join(ICON_DIR, "External-96.png")), "Open in SpecViz", self) # Create a tool button to hold the lock axes menu object tool_button = QToolButton(self) tool_button.setText("Axes Settings") tool_button.setIcon(QIcon(os.path.join(ICON_DIR, "Settings-96.png"))) tool_button.setPopupMode(QToolButton.MenuButtonPopup) tool_button.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) # Create a menu for the axes settings drop down self.settings_menu = QMenu(self) # Add lock x axis action self.lock_x_action = QAction("Lock spectral axis", self.settings_menu) self.lock_x_action.setCheckable(True) # Add lock y axis action self.lock_y_action = QAction("Lock vertical displacement axis", self.settings_menu) self.lock_y_action.setCheckable(True) # Add the actions to the menu self.settings_menu.addAction(self.lock_x_action) self.settings_menu.addAction(self.lock_y_action) # Set the menu object on the tool button tool_button.setMenu(self.settings_menu) # Create a widget action object to hold the tool button, this way the # toolbar behaves the way it's expected to tool_button_action = QWidgetAction(self) tool_button_action.setDefaultWidget(tool_button) self.addAction(tool_button_action) self.addSeparator() self.addAction(self.open_specviz)
def __init__(self, parent=None, inline=True, offset=0, force_float=False): QDialog.__init__(self, parent=parent) self._parent = parent self._text = None self._valid = None self._offset = offset # TODO: add this as an option in the General Preferences? self._force_float = force_float self._help_inline = _(""" <b>Numpy Array/Matrix Helper</b><br> Type an array in Matlab : <code>[1 2;3 4]</code><br> or Spyder simplified syntax : <code>1 2;3 4</code> <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two spaces or two tabs to generate a ';'. """) self._help_table = _(""" <b>Numpy Array/Matrix Helper</b><br> Enter an array in the table. <br> Use Tab to move between cells. <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two tabs at the end of a row to move to the next row. """) # Widgets self._button_warning = QToolButton() self._button_help = HelperToolButton() self._button_help.setIcon(ima.icon('MessageBoxInformation')) style = """ QToolButton { border: 1px solid grey; padding:0px; border-radius: 2px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f6f7fa, stop: 1 #dadbde); } """ self._button_help.setStyleSheet(style) if inline: self._button_help.setToolTip(self._help_inline) self._text = NumpyArrayInline(self) self._widget = self._text else: self._button_help.setToolTip(self._help_table) self._table = NumpyArrayTable(self) self._widget = self._table style = """ QDialog { margin:0px; border: 1px solid grey; padding:0px; border-radius: 2px; }""" self.setStyleSheet(style) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self._button_warning.setStyleSheet(style) # widget setup self.setWindowFlags(Qt.Window | Qt.Dialog | Qt.FramelessWindowHint) self.setModal(True) self.setWindowOpacity(0.90) self._widget.setMinimumWidth(200) # layout self._layout = QHBoxLayout() self._layout.addWidget(self._widget) self._layout.addWidget(self._button_warning, 1, Qt.AlignTop) self._layout.addWidget(self._button_help, 1, Qt.AlignTop) self.setLayout(self._layout) self._widget.setFocus()
class FadingTipBox(FadingDialog): """ """ def __init__(self, parent, opacity, duration, easing_curve, tour=None): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.tour = tour self.frames = None self.color_top = QColor.fromRgb(230, 230, 230) self.color_back = QColor.fromRgb(255, 255, 255) self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets self.button_home = QPushButton("<<") self.button_close = QPushButton("X") self.button_previous = QPushButton(" < ") self.button_end = QPushButton(">>") self.button_next = QPushButton(" > ") self.button_run = QPushButton(_('Run code')) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [ self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current ] arrow = get_image_path('hide.png') self.stylesheet = '''QPushButton { background-color: rgbs(200,200,200,100%); color: rgbs(0,0,0,100%); border-style: outset; border-width: 1px; border-radius: 3px; border-color: rgbs(100,100,100,100%); padding: 2px; } QPushButton:hover { background-color: rgbs(150, 150, 150, 100%); } QPushButton:disabled { background-color: rgbs(230,230,230,100%); color: rgbs(200,200,200,100%); border-color: rgbs(200,200,200,100%); } QComboBox { padding-left: 5px; background-color: rgbs(230,230,230,100%); border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; } QComboBox::down-arrow { image: url(''' + arrow + '''); } ''' # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace('\\', '/') self.setFocusPolicy(Qt.StrongFocus) for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets, self.setFocus]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu) # signals and slots # These are defined every time by the AnimatedTour Class def _disable_widgets(self): """ """ for widget in self.widgets: widget.setDisabled(True) def _enable_widgets(self): """ """ self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) for widget in self.widgets: widget.setDisabled(False) if self.button_disable == 'previous': self.button_previous.setDisabled(True) self.button_home.setDisabled(True) elif self.button_disable == 'next': self.button_next.setDisabled(True) self.button_end.setDisabled(True) def set_data(self, title, content, current, image, run, frames=None, step=None): """ """ self.label_title.setText(title) self.combo_title.clear() self.combo_title.addItems(frames) self.combo_title.setCurrentIndex(step) # min_content_len = max([len(f) for f in frames]) # self.combo_title.setMinimumContentsLength(min_content_len) # Fix and try to see how it looks with a combo box self.label_current.setText(current) self.button_current.setText(current) self.label_content.setText(content) self.image = image if image is None: self.label_image.setFixedHeight(1) self.label_image.setFixedWidth(1) else: extension = image.split('.')[-1] self.image = QPixmap(get_image_path(image), extension) self.label_image.setPixmap(self.image) self.label_image.setFixedSize(self.image.size()) if run is None: self.button_run.setVisible(False) else: self.button_run.setDisabled(False) self.button_run.setVisible(True) # Refresh layout self.layout().activate() def set_pos(self, x, y): """ """ self.x = x self.y = y self.move(QPoint(x, y)) def build_paths(self): """ """ geo = self.geometry() radius = 30 shadow = self.offset_shadow x0, y0 = geo.x(), geo.y() width, height = geo.width() - shadow, geo.height() - shadow left, top = 0, 0 right, bottom = width, height self.round_rect_path = QPainterPath() self.round_rect_path.moveTo(right, top + radius) self.round_rect_path.arcTo(right - radius, top, radius, radius, 0.0, 90.0) self.round_rect_path.lineTo(left + radius, top) self.round_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.round_rect_path.lineTo(left, bottom - radius) self.round_rect_path.arcTo(left, bottom - radius, radius, radius, 180.0, 90.0) self.round_rect_path.lineTo(right - radius, bottom) self.round_rect_path.arcTo(right - radius, bottom - radius, radius, radius, 270.0, 90.0) self.round_rect_path.closeSubpath() # Top path header = 36 offset = 2 left, top = offset, offset right = width - (offset) self.top_rect_path = QPainterPath() self.top_rect_path.lineTo(right, top + radius) self.top_rect_path.moveTo(right, top + radius) self.top_rect_path.arcTo(right - radius, top, radius, radius, 0.0, 90.0) self.top_rect_path.lineTo(left + radius, top) self.top_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.top_rect_path.lineTo(left, top + header) self.top_rect_path.lineTo(right, top + header) def paintEvent(self, event): """ """ self.build_paths() painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillPath(self.round_rect_path, self.color_back) painter.fillPath(self.top_rect_path, self.color_top) painter.strokePath(self.round_rect_path, QPen(Qt.gray, 1)) # TODO: Build the pointing arrow? def keyReleaseEvent(self, event): """ """ key = event.key() self.key_pressed = key keys = [ Qt.Key_Right, Qt.Key_Left, Qt.Key_Down, Qt.Key_Up, Qt.Key_Escape, Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Home, Qt.Key_End, Qt.Key_Menu ] if key in keys: if not self.is_fade_running(): self.sig_key_pressed.emit() def mousePressEvent(self, event): """override Qt method""" # Raise the main application window on click self.parent.raise_() self.raise_() if event.button() == Qt.RightButton: pass # clicked_widget = self.childAt(event.x(), event.y()) # if clicked_widget == self.label_current: # self.context_menu_requested(event) def focusOutEvent(self, event): """Override Qt method.""" # To be used so tips do not appear outside spyder self.tour.lost_focus() def context_menu_requested(self, event): """ """ pos = QPoint(event.x(), event.y()) menu = QMenu(self) actions = [] action_title = create_action(self, _('Go to step: '), icon=QIcon()) action_title.setDisabled(True) actions.append(action_title) # actions.append(create_action(self, _(': '), icon=QIcon())) add_actions(menu, actions) menu.popup(self.mapToGlobal(pos)) def reject(self): """Qt method to handle escape key event""" if not self.is_fade_running(): key = Qt.Key_Escape self.key_pressed = key self.sig_key_pressed.emit()
class SearchLineEdit(QLineEdit): """Line edit search widget with icon and remove all button""" def __init__(self, parent=None, icon=True): super(SearchLineEdit, self).__init__(parent) self.setTextMargins(1, 0, 20, 0) if icon: self.setTextMargins(18, 0, 20, 0) self._label = QLabel(self) self._pixmap_icon = QPixmap(get_image_path('conda_search.png')) self._label.setPixmap(self._pixmap_icon) self._label.setStyleSheet('''border: 0px; padding-bottom: 0px; padding-left: 2px;''') self._pixmap = QPixmap(get_image_path('conda_del.png')) self.button_clear = QToolButton(self) self.button_clear.setIcon(QIcon(self._pixmap)) self.button_clear.setIconSize(QSize(18, 18)) self.button_clear.setCursor(Qt.ArrowCursor) self.button_clear.setStyleSheet("""QToolButton {background: transparent; padding-right: 2px; border: none; margin:0px; }""") self.button_clear.setVisible(False) # Layout self._layout = QHBoxLayout(self) self._layout.addWidget(self.button_clear, 0, Qt.AlignRight) self._layout.setSpacing(0) self._layout.setContentsMargins(0, 2, 2, 0) # Signals and slots self.button_clear.clicked.connect(self.clear_text) self.textChanged.connect(self._toggle_visibility) self.textEdited.connect(self._toggle_visibility) def _toggle_visibility(self): """ """ if len(self.text()) == 0: self.button_clear.setVisible(False) else: self.button_clear.setVisible(True) def sizeHint(self): return QSize(200, self._pixmap_icon.height()) # Public api # ---------- def clear_text(self): """ """ self.setText('') self.setFocus()
def __init__(self, parent, opacity, duration, easing_curve, tour=None): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.tour = tour self.frames = None self.color_top = QColor.fromRgb(230, 230, 230) self.color_back = QColor.fromRgb(255, 255, 255) self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets self.button_home = QPushButton("<<") self.button_close = QPushButton("X") self.button_previous = QPushButton(" < ") self.button_end = QPushButton(">>") self.button_next = QPushButton(" > ") self.button_run = QPushButton(_('Run code')) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [ self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current ] arrow = get_image_path('hide.png') self.stylesheet = '''QPushButton { background-color: rgbs(200,200,200,100%); color: rgbs(0,0,0,100%); border-style: outset; border-width: 1px; border-radius: 3px; border-color: rgbs(100,100,100,100%); padding: 2px; } QPushButton:hover { background-color: rgbs(150, 150, 150, 100%); } QPushButton:disabled { background-color: rgbs(230,230,230,100%); color: rgbs(200,200,200,100%); border-color: rgbs(200,200,200,100%); } QComboBox { padding-left: 5px; background-color: rgbs(230,230,230,100%); border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; } QComboBox::down-arrow { image: url(''' + arrow + '''); } ''' # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace('\\', '/') self.setFocusPolicy(Qt.StrongFocus) for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets, self.setFocus]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu)
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 AddRemoveWidget(QWidget): """ A simple class that provides to vertically positioned buttons for adding and removing something. The addFunction and removeFunction functions must be provided. """ def __init__(self, addFunction=None, removeFunction=None, horizontal=False): QWidget.__init__(self) self.addButton = QToolButton(self) self.addButton.setIcon(resourceIcon("add")) self.addButton.setIconSize(QSize(16, 16)) self.addButton.clicked.connect(addFunction) self.removeButton = QToolButton(self) self.removeButton.setIcon(resourceIcon("remove")) self.removeButton.setIconSize(QSize(16, 16)) self.removeButton.clicked.connect(removeFunction) if horizontal: self.buttonLayout = QHBoxLayout() else: self.buttonLayout = QVBoxLayout() self.buttonLayout.setContentsMargins(0, 0, 0, 0) if horizontal: self.buttonLayout.addStretch(1) self.buttonLayout.addWidget(self.addButton) self.buttonLayout.addWidget(self.removeButton) if not horizontal: self.buttonLayout.addStretch(1) else: self.buttonLayout.addSpacing(2) self.setLayout(self.buttonLayout) def enableAddButton(self, state): """Enable or disable the add button""" self.addButton.setEnabled(state) def enableRemoveButton(self, state): """Enable or disable the remove button""" self.removeButton.setEnabled(state)
def __init__(self, parent=None, name_filters=['*.py', '*.pyw'], show_all=False, show_cd_only=None, show_icontext=True): QWidget.__init__(self, parent) # Widgets self.treewidget = ExplorerTreeWidget(self, show_cd_only=show_cd_only) button_previous = QToolButton(self) button_next = QToolButton(self) button_parent = QToolButton(self) self.button_menu = QToolButton(self) menu = QMenu(self) self.action_widgets = [ button_previous, button_next, button_parent, self.button_menu ] # Actions icontext_action = create_action(self, _("Show icons and text"), toggled=self.toggle_icontext) previous_action = create_action( self, text=_("Previous"), icon=ima.icon('ArrowBack'), triggered=self.treewidget.go_to_previous_directory) next_action = create_action( self, text=_("Next"), icon=ima.icon('ArrowForward'), triggered=self.treewidget.go_to_next_directory) parent_action = create_action( self, text=_("Parent"), icon=ima.icon('ArrowUp'), triggered=self.treewidget.go_to_parent_directory) options_action = create_action(self, text='', tip=_('Options')) # Setup widgets self.treewidget.setup(name_filters=name_filters, show_all=show_all) self.treewidget.chdir(getcwd()) self.treewidget.common_actions += [None, icontext_action] button_previous.setDefaultAction(previous_action) previous_action.setEnabled(False) button_next.setDefaultAction(next_action) next_action.setEnabled(False) button_parent.setDefaultAction(parent_action) self.button_menu.setIcon(ima.icon('tooloptions')) self.button_menu.setPopupMode(QToolButton.InstantPopup) self.button_menu.setMenu(menu) add_actions(menu, self.treewidget.common_actions) options_action.setMenu(menu) self.toggle_icontext(show_icontext) icontext_action.setChecked(show_icontext) for widget in self.action_widgets: widget.setAutoRaise(True) widget.setIconSize(QSize(16, 16)) # Layouts blayout = QHBoxLayout() blayout.addWidget(button_previous) blayout.addWidget(button_next) blayout.addWidget(button_parent) blayout.addStretch() blayout.addWidget(self.button_menu) layout = QVBoxLayout() layout.addLayout(blayout) layout.addWidget(self.treewidget) self.setLayout(layout) # Signals and slots self.treewidget.set_previous_enabled.connect( previous_action.setEnabled) self.treewidget.set_next_enabled.connect(next_action.setEnabled)
class RunWorkflowWidget(QWidget): workflowSucceeded = Signal(list) workflowFailed = Signal() workflowKilled = Signal() def __init__(self): QWidget.__init__(self) layout = QHBoxLayout() layout.addSpacing(10) self._workflow_combo = QComboBox() addHelpToWidget(self._workflow_combo, "run/workflow") self._workflow_combo.addItems(getWorkflowNames()) layout.addWidget(QLabel("Select Workflow:"), 0, Qt.AlignVCenter) layout.addWidget(self._workflow_combo, 0, Qt.AlignVCenter) # simulation_mode_layout.addStretch() layout.addSpacing(20) self.run_button = QToolButton() self.run_button.setIconSize(QSize(32, 32)) self.run_button.setText("Start Workflow") self.run_button.setIcon(resourceIcon("ide/gear_in_play")) self.run_button.clicked.connect(self.startWorkflow) self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) layout.addWidget(self.run_button) layout.addStretch(1) self.setLayout(layout) self._running_workflow_dialog = None self.workflowSucceeded.connect(self.workflowFinished) self.workflowFailed.connect(self.workflowFinishedWithFail) self.workflowKilled.connect(self.workflowStoppedByUser) self._workflow_runner = None """:type: WorkflowRunner""" def createSpinWidget(self): widget = QWidget() layout = QHBoxLayout() size = 64 spin_movie = resourceMovie("ide/loading.gif") spin_movie.setSpeed(60) spin_movie.setScaledSize(QSize(size, size)) spin_movie.start() processing_animation = QLabel() processing_animation.setMaximumSize(QSize(size, size)) processing_animation.setMinimumSize(QSize(size, size)) processing_animation.setMovie(spin_movie) layout.addWidget(processing_animation) processing_label = QLabel("Processing workflow '%s'" % self.getCurrentWorkflowName()) layout.addWidget(processing_label, Qt.AlignBottom) widget.setLayout(layout) return widget def cancelWorkflow(self): if self._workflow_runner.isRunning(): cancel = QMessageBox.question( self, "Confirm Cancel", "Are you sure you want to cancel the running workflow?", QMessageBox.Yes | QMessageBox.No, ) if cancel == QMessageBox.Yes: self._workflow_runner.cancel() self._running_workflow_dialog.disableCloseButton() def getCurrentWorkflowName(self): index = self._workflow_combo.currentIndex() return getWorkflowNames()[index] def startWorkflow(self): self._running_workflow_dialog = WorkflowDialog("Running Workflow", self.createSpinWidget(), self) self._running_workflow_dialog.closeButtonPressed.connect( self.cancelWorkflow) workflow_thread = Thread(name="ert_gui_workflow_thread") workflow_thread.setDaemon(True) workflow_thread.run = self.runWorkflow self._workflow_runner = createWorkflowRunner( self.getCurrentWorkflowName()) self._workflow_runner.run() workflow_thread.start() self._running_workflow_dialog.show() def runWorkflow(self): while self._workflow_runner.isRunning(): time.sleep(2) cancelled = self._workflow_runner.isCancelled() if cancelled: self.workflowKilled.emit() else: success = self._workflow_runner.workflowResult() if success: report = self._workflow_runner.workflowReport() failed_jobs = [ k for k, v in report.items() if not v["completed"] ] self.workflowSucceeded.emit(failed_jobs) else: self.workflowFailed.emit() def workflowFinished(self, failed_jobs): workflow_name = self.getCurrentWorkflowName() jobs_msg = "successfully!" if failed_jobs: jobs_msg = "\nThe following jobs failed: " + ", ".join( [j for j in failed_jobs]) QMessageBox.information( self, "Workflow completed!", "The workflow '{}' completed {}".format(workflow_name, jobs_msg), ) self._running_workflow_dialog.accept() self._running_workflow_dialog = None def workflowFinishedWithFail(self): workflow_name = self.getCurrentWorkflowName() error = self._workflow_runner.workflowError() QMessageBox.critical( self, "Workflow failed!", "The workflow '%s' failed!\n\n%s" % (workflow_name, error), ) self._running_workflow_dialog.reject() self._running_workflow_dialog = None def workflowStoppedByUser(self): workflow_name = self.getCurrentWorkflowName() QMessageBox.information( self, "Workflow killed!", "The workflow '%s' was killed successfully!" % workflow_name, ) self._running_workflow_dialog.reject() self._running_workflow_dialog = None
def setup_ui(self): """ Initialize the widgets and layouts. """ self.setLayout(self.main_layout) self.pv_layout.addWidget(self.pv_protocol_cmb) self.pv_layout.addWidget(self.pv_name_line_edt) self.pv_layout.addWidget(self.pv_connect_push_btn) self.pv_add_panel.setLayout(self.pv_layout) QTimer.singleShot(0, self.pv_name_line_edt.setFocus) self.curve_settings_tab.setLayout(self.curves_tab_layout) self.chart_settings_tab.setLayout(self.chart_settings_layout) self.setup_chart_settings_layout() self.data_settings_tab.setLayout(self.data_tab_layout) self.setup_data_tab_layout() self.tab_panel.addTab(self.curve_settings_tab, "Curves") self.tab_panel.addTab(self.data_settings_tab, "Data") self.tab_panel.addTab(self.chart_settings_tab, "Graph") self.crosshair_settings_layout.addWidget(self.enable_crosshair_chk) self.crosshair_settings_layout.addWidget(self.crosshair_coord_lbl) self.zoom_x_layout.addWidget(self.zoom_in_x_btn) self.zoom_x_layout.addWidget(self.zoom_out_x_btn) self.zoom_y_layout.addWidget(self.zoom_in_y_btn) self.zoom_y_layout.addWidget(self.zoom_out_y_btn) self.view_all_reset_chart_layout.addWidget(self.reset_chart_btn) self.view_all_reset_chart_layout.addWidget(self.view_all_btn) self.pause_chart_layout.addWidget(self.pause_chart_btn) self.import_export_data_layout.addWidget(self.import_data_btn) self.import_export_data_layout.addWidget(self.export_data_btn) self.chart_control_layout.addLayout(self.zoom_x_layout) self.chart_control_layout.addLayout(self.zoom_y_layout) self.chart_control_layout.addLayout(self.view_all_reset_chart_layout) self.chart_control_layout.addLayout(self.pause_chart_layout) self.chart_control_layout.addLayout(self.crosshair_settings_layout) self.chart_control_layout.addLayout(self.import_export_data_layout) self.chart_control_layout.insertSpacing(5, 30) self.chart_layout.addWidget(self.chart) self.chart_layout.addLayout(self.chart_control_layout) self.chart_panel.setLayout(self.chart_layout) self.splitter.addWidget(self.chart_panel) self.splitter.addWidget(self.tab_panel) self.splitter.setSizes([1, 0]) self.splitter.setHandleWidth(10) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.charting_layout.addWidget(self.splitter) self.body_layout.addWidget(self.pv_add_panel) self.body_layout.addLayout(self.charting_layout) self.body_layout.setSpacing(0) self.body_layout.setContentsMargins(0, 0, 0, 0) self.main_layout.addLayout(self.body_layout) self.enable_chart_control_buttons(False) handle = self.splitter.handle(1) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) button = QToolButton(handle) button.setArrowType(Qt.LeftArrow) button.clicked.connect(lambda: self.handle_splitter_button(True)) layout.addWidget(button) button = QToolButton(handle) button.setArrowType(Qt.RightArrow) button.clicked.connect(lambda: self.handle_splitter_button(False)) layout.addWidget(button) handle.setLayout(layout)
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 SliceViewerDataView(QWidget): """The view for the data portion of the sliceviewer""" def __init__(self, presenter: IDataViewSubscriber, dims_info, can_normalise, parent=None, conf=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots_active = False self.can_normalise = can_normalise self.nonortho_transform = None self.conf = conf self._line_plots = None self._image_info_tracker = None self._region_selection_on = False self._orig_lims = None self.colorbar_layout = QVBoxLayout() self.colorbar_layout.setContentsMargins(0, 0, 0, 0) self.colorbar_layout.setSpacing(0) self.image_info_widget = ImageInfoWidget(self) self.image_info_widget.setToolTip("Information about the selected pixel") self.track_cursor = QCheckBox("Track Cursor", self) self.track_cursor.setToolTip( "Update the image readout table when the cursor is over the plot. " "If unticked the table will update only when the plot is clicked") self.track_cursor.setChecked(True) self.track_cursor.stateChanged.connect(self.on_track_cursor_state_change) # Dimension widget self.dimensions_layout = QGridLayout() self.dimensions_layout.setHorizontalSpacing(10) if (dims_info): self.create_dimensions(dims_info) else: self.dimensions = None # normalization options if can_normalise: self.norm_label = QLabel("Normalization") self.colorbar_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.colorbar_layout.addWidget(self.norm_opts) # MPL figure + colorbar self.fig = Figure() self.ax = None self.image = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = SliceViewerCanvas(self.fig) self.canvas.mpl_connect('button_release_event', self.mouse_release) self.canvas.mpl_connect('button_press_event', self.presenter.canvas_clicked) self.canvas.mpl_connect('key_press_event', self.presenter.key_pressed) self.canvas.mpl_connect('motion_notify_event', self.presenter.mouse_moved) self.colorbar_label = QLabel("Colormap") self.colorbar_layout.addWidget(self.colorbar_label) norm_scale = self.get_default_scale_norm() self.colorbar = ColorbarWidget(self, norm_scale) self.colorbar.cmap.setToolTip("Colormap options") self.colorbar.crev.setToolTip("Reverse colormap") self.colorbar.norm.setToolTip("Colormap normalisation options") self.colorbar.powerscale.setToolTip("Power colormap scale") self.colorbar.cmax.setToolTip("Colormap maximum limit") self.colorbar.cmin.setToolTip("Colormap minimum limit") self.colorbar.autoscale.setToolTip("Automatically changes colormap limits when zooming on the plot") self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.scaleNormChanged.connect(self.scale_norm_changed) # make width larger to fit image readout table self.colorbar.setMaximumWidth(200) # MPL toolbar self.toolbar_layout = QHBoxLayout() self.mpl_toolbar = SliceViewerNavigationToolbar(self.canvas, self, False) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.on_line_plots_toggle) self.mpl_toolbar.regionSelectionClicked.connect(self.on_region_selection_toggle) self.mpl_toolbar.homeClicked.connect(self.on_home_clicked) self.mpl_toolbar.nonOrthogonalClicked.connect(self.on_non_orthogonal_axes_toggle) self.mpl_toolbar.zoomPanClicked.connect(self.presenter.zoom_pan_clicked) self.mpl_toolbar.zoomPanFinished.connect(self.on_data_limits_changed) self.toolbar_layout.addWidget(self.mpl_toolbar) # Status bar self.status_bar = QStatusBar(parent=self) self.status_bar.setStyleSheet('QStatusBar::item {border: None;}') # Hide spacers between button and label self.status_bar_label = QLabel() self.help_button = QToolButton() self.help_button.setText("?") self.status_bar.addWidget(self.help_button) self.status_bar.addWidget(self.status_bar_label) # layout layout = QGridLayout(self) layout.setSpacing(1) layout.addLayout(self.dimensions_layout, 0, 0, 1, 2) layout.addLayout(self.toolbar_layout, 1, 0, 1, 1) layout.addLayout(self.colorbar_layout, 1, 1, 3, 1) layout.addWidget(self.canvas, 2, 0, 1, 1) layout.addWidget(self.status_bar, 3, 0, 1, 1) layout.setRowStretch(2, 1) def create_dimensions(self, dims_info): self.dimensions = DimensionWidget(dims_info, parent=self) self.dimensions.dimensionsChanged.connect(self.presenter.dimensions_changed) self.dimensions.valueChanged.connect(self.presenter.slicepoint_changed) self.dimensions_layout.addWidget(self.dimensions, 1, 0, 1, 1) self.dimensions_layout.addWidget(self.track_cursor, 0, 1, Qt.AlignRight) self.dimensions_layout.addWidget(self.image_info_widget, 1, 1) @property def grid_on(self): return self._grid_on @property def line_plotter(self): return self._line_plots @property def nonorthogonal_mode(self): return self.nonortho_transform is not None def create_axes_orthogonal(self, redraw_on_zoom=False): """Create a standard set of orthogonal axes :param redraw_on_zoom: If True then when scroll zooming the canvas is redrawn immediately """ self.clear_figure() self.nonortho_transform = None self.ax = self.fig.add_subplot(111, projection='mantid') self.enable_zoom_on_mouse_scroll(redraw_on_zoom) if self.grid_on: self.ax.grid(self.grid_on) if self.line_plots_active: self.add_line_plots() self.plot_MDH = self.plot_MDH_orthogonal self.canvas.draw_idle() def create_axes_nonorthogonal(self, transform): self.clear_figure() self.set_nonorthogonal_transform(transform) self.ax = CurveLinearSubPlot(self.fig, 1, 1, 1, grid_helper=GridHelperCurveLinear( (transform.tr, transform.inv_tr))) # don't redraw on zoom as the data is rebinned and has to be redrawn again anyway self.enable_zoom_on_mouse_scroll(redraw=False) self.set_grid_on() self.fig.add_subplot(self.ax) self.plot_MDH = self.plot_MDH_nonorthogonal self.canvas.draw_idle() def enable_zoom_on_mouse_scroll(self, redraw): """Enable zoom on scroll the mouse wheel for the created axes :param redraw: Pass through to redraw option in enable_zoom_on_scroll """ self.canvas.enable_zoom_on_scroll(self.ax, redraw=redraw, toolbar=self.mpl_toolbar, callback=self.on_data_limits_changed) def add_line_plots(self, toolcls, exporter): """Assuming line plots are currently disabled, enable them on the current figure The image axes must have been created first. :param toolcls: Use this class to handle creating the plots :param exporter: Object defining methods to export cuts/roi """ if self.line_plots_active: return self.line_plots_active = True self._line_plots = toolcls(LinePlots(self.ax, self.colorbar), exporter) self.status_bar_label.setText(self._line_plots.status_message()) self.canvas.setFocus() self.mpl_toolbar.set_action_checked(ToolItemText.LINEPLOTS, True, trigger=False) def switch_line_plots_tool(self, toolcls, exporter): """Assuming line plots are currently enabled then switch the tool used to generate the plot curves. :param toolcls: Use this class to handle creating the plots """ if not self.line_plots_active: return # Keep the same set of line plots axes but swap the selection tool plotter = self._line_plots.plotter plotter.delete_line_plot_lines() self._line_plots.disconnect() self._line_plots = toolcls(plotter, exporter) self.status_bar_label.setText(self._line_plots.status_message()) self.canvas.setFocus() self.canvas.draw_idle() def remove_line_plots(self): """Assuming line plots are currently enabled, remove them from the current figure """ if not self.line_plots_active: return self._line_plots.plotter.close() self.status_bar_label.clear() self._line_plots = None self.line_plots_active = False def plot_MDH_orthogonal(self, ws, **kwargs): """ clears the plot and creates a new one using a MDHistoWorkspace """ self.clear_image() self.image = self.ax.imshow(ws, origin='lower', aspect='auto', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) # ensure the axes data limits are updated to match the # image. For example if the axes were zoomed and the # swap dimensions was clicked we need to restore the # appropriate extents to see the image in the correct place extent = self.image.get_extent() self.ax.set_xlim(extent[0], extent[1]) self.ax.set_ylim(extent[2], extent[3]) # Set the original data limits which get passed to the ImageInfoWidget so that # the mouse projection to data space is correct for MDH workspaces when zoomed/changing slices self._orig_lims = self.get_axes_limits() self.on_track_cursor_state_change(self.track_cursor_checked()) self.draw_plot() def plot_MDH_nonorthogonal(self, ws, **kwargs): self.clear_image() self.image = pcolormesh_nonorthogonal(self.ax, ws, self.nonortho_transform.tr, transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) self.on_track_cursor_state_change(self.track_cursor_checked()) # swapping dimensions in nonorthogonal mode currently resets back to the # full data limits as the whole axes has been recreated so we don't have # access to the original limits # pcolormesh clears any grid that was previously visible if self.grid_on: self.ax.grid(self.grid_on) self.draw_plot() def plot_matrix(self, ws, **kwargs): """ clears the plot and creates a new one using a MatrixWorkspace keeping the axes limits that have already been set """ # ensure view is correct if zoomed in while swapping dimensions # compute required extent and just have resampling imshow deal with it old_extent = None if self.image is not None: old_extent = self.image.get_extent() if self.image.transpose != self.dimensions.transpose: e1, e2, e3, e4 = old_extent old_extent = e3, e4, e1, e2 self.clear_image() self.image = self.ax.imshow(ws, origin='lower', aspect='auto', interpolation='none', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), extent=old_extent, **kwargs) self.on_track_cursor_state_change(self.track_cursor_checked()) self.draw_plot() def clear_image(self): """Removes any image from the axes""" if self.image is not None: if self.line_plots_active: self._line_plots.plotter.delete_line_plot_lines() self.image_info_widget.cursorAt(DBLMAX, DBLMAX, DBLMAX) if hasattr(self.ax, "remove_artists_if"): self.ax.remove_artists_if(lambda art: art == self.image) else: self.image.remove() self.image = None def clear_figure(self): """Removes everything from the figure""" if self.line_plots_active: self._line_plots.plotter.close() self.line_plots_active = False self.image = None self.canvas.disable_zoom_on_scroll() self.fig.clf() self.ax = None def draw_plot(self): self.ax.set_title('') self.canvas.draw() if self.image: self.colorbar.set_mappable(self.image) self.colorbar.update_clim() self.mpl_toolbar.update() # clear nav stack if self.line_plots_active: self._line_plots.plotter.delete_line_plot_lines() self._line_plots.plotter.update_line_plot_labels() def export_region(self, limits, cut): """ React to a region selection that should be exported :param limits: 2-tuple of ((left, right), (bottom, top)) :param cut: A str denoting which cuts to export. """ self.presenter.export_region(limits, cut) def update_plot_data(self, data): """ This just updates the plot data without creating a new plot. The extents can change if the data has been rebinned. """ if self.nonortho_transform: self.image.set_array(data.T.ravel()) else: self.image.set_data(data.T) self.colorbar.update_clim() def track_cursor_checked(self): return self.track_cursor.isChecked() if self.track_cursor else False def on_track_cursor_state_change(self, state): """ Called to notify the current state of the track cursor box """ if self._image_info_tracker is not None: self._image_info_tracker.disconnect() if self._line_plots is not None and not self._region_selection_on: self._line_plots.disconnect() self._image_info_tracker = ImageInfoTracker(image=self.image, transform=self.nonortho_transform, do_transform=self.nonorthogonal_mode, widget=self.image_info_widget, cursor_transform=self._orig_lims) if state: self._image_info_tracker.connect() if self._line_plots and not self._region_selection_on: self._line_plots.connect() else: self._image_info_tracker.disconnect() if self._line_plots and not self._region_selection_on: self._line_plots.disconnect() def on_home_clicked(self): """Reset the view to encompass all of the data""" self.presenter.show_all_data_clicked() def on_line_plots_toggle(self, state): """Switch state of the line plots""" self.presenter.line_plots(state) def on_region_selection_toggle(self, state): """Switch state of the region selection""" self.presenter.region_selection(state) self._region_selection_on = state # If state is off and track cursor is on, make sure line plots are re-connected to move cursor if not state and self.track_cursor_checked(): if self._line_plots: self._line_plots.connect() def on_non_orthogonal_axes_toggle(self, state): """ Switch state of the non-orthognal axes on/off """ self.presenter.nonorthogonal_axes(state) def on_data_limits_changed(self): """ React to when the data limits have changed """ self.presenter.data_limits_changed() def deactivate_and_disable_tool(self, tool_text): """Deactivate a tool as if the control had been pressed and disable the functionality""" self.deactivate_tool(tool_text) self.disable_tool_button(tool_text) def activate_tool(self, tool_text): """Activate a given tool as if the control had been pressed""" self.mpl_toolbar.set_action_checked(tool_text, True) def deactivate_tool(self, tool_text): """Deactivate a given tool as if the tool button had been pressed""" self.mpl_toolbar.set_action_checked(tool_text, False) def enable_tool_button(self, tool_text): """Set a given tool button enabled so it can be interacted with""" self.mpl_toolbar.set_action_enabled(tool_text, True) def disable_tool_button(self, tool_text): """Set a given tool button disabled so it cannot be interacted with""" self.mpl_toolbar.set_action_enabled(tool_text, False) def get_axes_limits(self): """ Return the limits on the image axes transformed into the nonorthogonal frame if appropriate """ if self.image is None: return None else: xlim, ylim = self.ax.get_xlim(), self.ax.get_ylim() if self.nonorthogonal_mode: inv_tr = self.nonortho_transform.inv_tr # viewing axis y not aligned with plot axis xmin_p, ymax_p = inv_tr(xlim[0], ylim[1]) xmax_p, ymin_p = inv_tr(xlim[1], ylim[0]) xlim, ylim = (xmin_p, xmax_p), (ymin_p, ymax_p) return xlim, ylim def get_full_extent(self): """ Return the full extent of image - only applicable for plots of matrix workspaces """ if self.image and isinstance(self.image, samplingimage.SamplingImage): return self.image.get_full_extent() else: return None def set_axes_limits(self, xlim, ylim): """ Set the view limits on the image axes to the given extents. Assume the limits are in the orthogonal frame. :param xlim: 2-tuple of (xmin, xmax) :param ylim: 2-tuple of (ymin, ymax) """ self.ax.set_xlim(xlim) self.ax.set_ylim(ylim) def set_grid_on(self): """ If not visible sets the grid visibility """ if not self._grid_on: self._grid_on = True self.mpl_toolbar.set_action_checked(ToolItemText.GRID, state=self._grid_on) def set_nonorthogonal_transform(self, transform): """ Set the transform for nonorthogonal axes mode :param transform: An object with a tr method to transform from nonorthognal coordinates to display coordinates """ self.nonortho_transform = transform def show_temporary_status_message(self, msg, timeout_ms): """ Show a message in the status bar that disappears after a set period :param msg: A str message to display :param timeout_ms: Timeout in milliseconds to display the message for """ self.status_bar.showMessage(msg, timeout_ms) def toggle_grid(self, state): """ Toggle the visibility of the grid on the axes """ self._grid_on = state self.ax.grid(self._grid_on) self.canvas.draw_idle() def mouse_release(self, event): if event.inaxes != self.ax: return self.canvas.setFocus() if event.button == 1: self._image_info_tracker.on_cursor_at(event.xdata, event.ydata) if self.line_plots_active and not self._region_selection_on: self._line_plots.on_cursor_at(event.xdata, event.ydata) if event.button == 3: self.on_home_clicked() def deactivate_zoom_pan(self): self.deactivate_tool(ToolItemText.PAN) self.deactivate_tool(ToolItemText.ZOOM) def update_data_clim(self): self.image.set_clim(self.colorbar.colorbar.mappable.get_clim()) if self.line_plots_active: self._line_plots.plotter.update_line_plot_limits() self.canvas.draw_idle() def set_normalization(self, ws, **kwargs): normalize_by_bin_width, _ = get_normalize_by_bin_width(ws, self.ax, **kwargs) is_normalized = normalize_by_bin_width or ws.isDistribution() self.presenter.normalization = is_normalized if is_normalized: self.norm_opts.setCurrentIndex(1) else: self.norm_opts.setCurrentIndex(0) def get_default_scale_norm(self): scale = 'Linear' if self.conf is None: return scale if self.conf.has(SCALENORM): scale = self.conf.get(SCALENORM, type=str) if scale == 'Power' and self.conf.has(POWERSCALE): exponent = self.conf.get(POWERSCALE, type=str) scale = (scale, exponent) scale = "SymmetricLog10" if scale == 'Log' else scale return scale def scale_norm_changed(self): if self.conf is None: return scale = self.colorbar.norm.currentText() self.conf.set(SCALENORM, scale) if scale == 'Power': exponent = self.colorbar.powerscale_value self.conf.set(POWERSCALE, exponent) def on_resize(self): if not self.line_plots_active: self.ax.figure.tight_layout() # tight_layout doesn't work with LinePlots enabled atm
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 __init__(self, parent, opacity, duration, easing_curve): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.frames = None self.color_top = QColor.fromRgb(230, 230, 230) self.color_back = QColor.fromRgb(255, 255, 255) self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets self.button_home = QPushButton("<<") self.button_close = QPushButton("X") self.button_previous = QPushButton(" < ") self.button_end = QPushButton(">>") self.button_next = QPushButton(" > ") self.button_run = QPushButton(_("Run code")) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [ self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current, ] arrow = get_image_path("hide.png") self.stylesheet = ( """QPushButton { background-color: rgbs(200,200,200,100%); color: rgbs(0,0,0,100%); border-style: outset; border-width: 1px; border-radius: 3px; border-color: rgbs(100,100,100,100%); padding: 2px; } QPushButton:hover { background-color: rgbs(150, 150, 150, 100%); } QPushButton:disabled { background-color: rgbs(230,230,230,100%); color: rgbs(200,200,200,100%); border-color: rgbs(200,200,200,100%); } QComboBox { padding-left: 5px; background-color: rgbs(230,230,230,100%); border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; } QComboBox::down-arrow { image: url(""" + arrow + """); } """ ) # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace("\\", "/") for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu)
class CustomizePlotDialog(QDialog): applySettings = Signal() undoSettings = Signal() redoSettings = Signal() resetSettings = Signal() copySettings = Signal(str) copySettingsToOthers = Signal(list) def __init__(self, title, parent, key_defs, key=""): QDialog.__init__(self, parent) self.setWindowTitle(title) self.current_key = key self._key_defs = key_defs self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) self._tab_map = {} self._tab_order = [] layout = QVBoxLayout() self._tabs = QTabWidget() layout.addWidget(self._tabs) layout.setSizeConstraint(QLayout.SetFixedSize) # not resizable!!! self._button_layout = QHBoxLayout() self._reset_button = QToolButton() self._reset_button.setIcon(resourceIcon("format_color_reset.svg")) self._reset_button.setToolTip("Reset all settings back to default") self._reset_button.clicked.connect(self.resetSettings) self._undo_button = QToolButton() self._undo_button.setIcon(resourceIcon("undo.svg")) self._undo_button.setToolTip("Undo") self._undo_button.clicked.connect(self.undoSettings) self._redo_button = QToolButton() self._redo_button.setIcon(resourceIcon("redo.svg")) self._redo_button.setToolTip("Redo") self._redo_button.clicked.connect(self.redoSettings) self._redo_button.setEnabled(False) self._copy_from_button = QToolButton() self._copy_from_button.setIcon(resourceIcon("download.svg")) self._copy_from_button.setToolTip("Copy settings from another key") self._copy_from_button.setPopupMode(QToolButton.InstantPopup) self._copy_from_button.setEnabled(False) self._copy_to_button = QToolButton() self._copy_to_button.setIcon(resourceIcon("upload.svg")) self._copy_to_button.setToolTip( "Copy current plot settings to other keys") self._copy_to_button.setPopupMode(QToolButton.InstantPopup) self._copy_to_button.clicked.connect(self.initiateCopyStyleToDialog) self._copy_to_button.setEnabled(True) tool_menu = QMenu(self._copy_from_button) self._popup_list = QListWidget(tool_menu) self._popup_list.setSortingEnabled(True) self._popup_list.itemClicked.connect(self.keySelected) action = QWidgetAction(tool_menu) action.setDefaultWidget(self._popup_list) tool_menu.addAction(action) self._copy_from_button.setMenu(tool_menu) self._apply_button = QPushButton("Apply") self._apply_button.setToolTip("Apply the new settings") self._apply_button.clicked.connect(self.applySettings) self._apply_button.setDefault(True) self._close_button = QPushButton("Close") self._close_button.setToolTip("Hide this dialog") self._close_button.clicked.connect(self.hide) self._button_layout.addWidget(self._reset_button) self._button_layout.addStretch() self._button_layout.addWidget(self._undo_button) self._button_layout.addWidget(self._redo_button) self._button_layout.addWidget(self._copy_from_button) self._button_layout.addWidget(self._copy_to_button) self._button_layout.addStretch() self._button_layout.addWidget(self._apply_button) self._button_layout.addWidget(self._close_button) layout.addStretch() layout.addLayout(self._button_layout) self.setLayout(layout) def initiateCopyStyleToDialog(self): dialog = CopyStyleToDialog(self, self.current_key, self._key_defs) if dialog.exec_(): self.copySettingsToOthers.emit(dialog.getSelectedKeys()) def addCopyableKey(self, key): self._popup_list.addItem(key) def keySelected(self, list_widget_item): self.copySettings.emit(str(list_widget_item.text())) def currentPlotKeyChanged(self, new_key): self.current_key = new_key def keyPressEvent(self, q_key_event): if q_key_event.key() == Qt.Key_Escape: self.hide() else: QDialog.keyPressEvent(self, q_key_event) def addTab(self, attribute_name, title, widget): self._tabs.addTab(widget, title) self._tab_map[attribute_name] = widget self._tab_order.append(attribute_name) def __getitem__(self, item): """@rtype: ert_gui.tools.plot.customize.customization_view.CustomizationView""" return self._tab_map[item] def __iter__(self): for attribute_name in self._tab_order: yield self._tab_map[attribute_name] def setUndoRedoCopyState(self, undo, redo, copy=False): self._undo_button.setEnabled(undo) self._redo_button.setEnabled(redo) self._copy_from_button.setEnabled(copy)
def __init__(self, parent=None, name_filters=['*.py', '*.pyw'], show_all=False, show_cd_only=None, show_icontext=True): QWidget.__init__(self, parent) # Widgets self.treewidget = ExplorerTreeWidget(self, show_cd_only=show_cd_only) button_previous = QToolButton(self) button_next = QToolButton(self) button_parent = QToolButton(self) self.button_menu = QToolButton(self) menu = QMenu(self) self.action_widgets = [button_previous, button_next, button_parent, self.button_menu] # Actions icontext_action = create_action(self, _("Show icons and text"), toggled=self.toggle_icontext) previous_action = create_action(self, text=_("Previous"), icon=ima.icon('ArrowBack'), triggered=self.treewidget.go_to_previous_directory) next_action = create_action(self, text=_("Next"), icon=ima.icon('ArrowForward'), triggered=self.treewidget.go_to_next_directory) parent_action = create_action(self, text=_("Parent"), icon=ima.icon('ArrowUp'), triggered=self.treewidget.go_to_parent_directory) options_action = create_action(self, text='', tip=_('Options')) # Setup widgets self.treewidget.setup(name_filters=name_filters, show_all=show_all) self.treewidget.chdir(getcwd()) self.treewidget.common_actions += [None, icontext_action] button_previous.setDefaultAction(previous_action) previous_action.setEnabled(False) button_next.setDefaultAction(next_action) next_action.setEnabled(False) button_parent.setDefaultAction(parent_action) self.button_menu.setIcon(ima.icon('tooloptions')) self.button_menu.setPopupMode(QToolButton.InstantPopup) self.button_menu.setMenu(menu) add_actions(menu, self.treewidget.common_actions) options_action.setMenu(menu) self.toggle_icontext(show_icontext) icontext_action.setChecked(show_icontext) for widget in self.action_widgets: widget.setAutoRaise(True) widget.setIconSize(QSize(16, 16)) # Layouts blayout = QHBoxLayout() blayout.addWidget(button_previous) blayout.addWidget(button_next) blayout.addWidget(button_parent) blayout.addStretch() blayout.addWidget(self.button_menu) layout = QVBoxLayout() layout.addLayout(blayout) layout.addWidget(self.treewidget) self.setLayout(layout) # Signals and slots self.treewidget.set_previous_enabled.connect( previous_action.setEnabled) self.treewidget.set_next_enabled.connect(next_action.setEnabled)
def setup_grain_design(): # grain table view master = QHBoxLayout(self) master.addWidget(QTableView()) # grain design controls controls = QVBoxLayout(self) # add a push button self.btn_new_grain = QPushButton(self.tr("New Grain")) self.btn_new_grain.setMinimumHeight(50) controls.addWidget(self.btn_new_grain) # add a dividing line line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) controls.addSpacing(5) controls.addWidget(line) # rest of the controls buttons self.btn_edit_grain = QPushButton(self.tr("Edit")) self.btn_edit_grain.setMinimumHeight(30) controls.addWidget(self.btn_edit_grain) self.btn_delete_Grain = QPushButton(self.tr("Delete")) self.btn_delete_Grain.setMinimumHeight(30) controls.addWidget(self.btn_delete_Grain) # move grain up and down moveup = QHBoxLayout() self.btn_move_up = QToolButton() self.btn_move_up.setArrowType(Qt.UpArrow) moveup.addWidget(self.btn_move_up) moveup.addWidget(QLabel(self.tr("Move Up"))) controls.addLayout(moveup) movedown = QHBoxLayout() self.btn_move_down = QToolButton() self.btn_move_down.setArrowType(Qt.DownArrow) movedown.addWidget(self.btn_move_down) movedown.addWidget(QLabel(self.tr("Move Down"))) controls.addLayout(movedown) controls.addStretch() # add info for motor design fl_propellant_info = QFormLayout() gb_motor_info = QGroupBox(self.tr("Propellant Info")) gb_motor_info.setLayout(fl_propellant_info) self.lbl_num_grains = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Number of Segments:")), self.lbl_num_grains) self.lbl_motor_dia = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Motor Diameter:")), self.lbl_motor_dia) self.lbl_motor_len = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Propellant Length:")), self.lbl_motor_len) self.lbl_prop_mass = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Propellant Mass:")), self.lbl_prop_mass) self.lbl_volume_loading = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Volume Loading:")), self.lbl_volume_loading) # set group box's layout controls.addWidget(gb_motor_info) # setup master layout master.addLayout(controls) self.gb_design = QGroupBox(self.tr("Grain Design")) self.gb_design.setLayout(master)
def __init__(self, title, parent, key_defs, key=""): QDialog.__init__(self, parent) self.setWindowTitle(title) self.current_key = key self._key_defs = key_defs self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) self._tab_map = {} self._tab_order = [] layout = QVBoxLayout() self._tabs = QTabWidget() layout.addWidget(self._tabs) layout.setSizeConstraint(QLayout.SetFixedSize) # not resizable!!! self._button_layout = QHBoxLayout() self._reset_button = QToolButton() self._reset_button.setIcon(resourceIcon("format_color_reset.svg")) self._reset_button.setToolTip("Reset all settings back to default") self._reset_button.clicked.connect(self.resetSettings) self._undo_button = QToolButton() self._undo_button.setIcon(resourceIcon("undo.svg")) self._undo_button.setToolTip("Undo") self._undo_button.clicked.connect(self.undoSettings) self._redo_button = QToolButton() self._redo_button.setIcon(resourceIcon("redo.svg")) self._redo_button.setToolTip("Redo") self._redo_button.clicked.connect(self.redoSettings) self._redo_button.setEnabled(False) self._copy_from_button = QToolButton() self._copy_from_button.setIcon(resourceIcon("download.svg")) self._copy_from_button.setToolTip("Copy settings from another key") self._copy_from_button.setPopupMode(QToolButton.InstantPopup) self._copy_from_button.setEnabled(False) self._copy_to_button = QToolButton() self._copy_to_button.setIcon(resourceIcon("upload.svg")) self._copy_to_button.setToolTip( "Copy current plot settings to other keys") self._copy_to_button.setPopupMode(QToolButton.InstantPopup) self._copy_to_button.clicked.connect(self.initiateCopyStyleToDialog) self._copy_to_button.setEnabled(True) tool_menu = QMenu(self._copy_from_button) self._popup_list = QListWidget(tool_menu) self._popup_list.setSortingEnabled(True) self._popup_list.itemClicked.connect(self.keySelected) action = QWidgetAction(tool_menu) action.setDefaultWidget(self._popup_list) tool_menu.addAction(action) self._copy_from_button.setMenu(tool_menu) self._apply_button = QPushButton("Apply") self._apply_button.setToolTip("Apply the new settings") self._apply_button.clicked.connect(self.applySettings) self._apply_button.setDefault(True) self._close_button = QPushButton("Close") self._close_button.setToolTip("Hide this dialog") self._close_button.clicked.connect(self.hide) self._button_layout.addWidget(self._reset_button) self._button_layout.addStretch() self._button_layout.addWidget(self._undo_button) self._button_layout.addWidget(self._redo_button) self._button_layout.addWidget(self._copy_from_button) self._button_layout.addWidget(self._copy_to_button) self._button_layout.addStretch() self._button_layout.addWidget(self._apply_button) self._button_layout.addWidget(self._close_button) layout.addStretch() layout.addLayout(self._button_layout) self.setLayout(layout)
def __init__(self, config_file): QWidget.__init__(self) self._config_file = config_file self._ee_config = None if FeatureToggling.is_enabled("prefect") or FeatureToggling.is_enabled( "ensemble-evaluator"): self._ee_config = EvaluatorServerConfig() self.setObjectName("Simulation_panel") layout = QVBoxLayout() self._simulation_mode_combo = QComboBox() self._simulation_mode_combo.setObjectName("Simulation_mode") addHelpToWidget(self._simulation_mode_combo, "run/simulation_mode") self._simulation_mode_combo.currentIndexChanged.connect( self.toggleSimulationMode) simulation_mode_layout = QHBoxLayout() simulation_mode_layout.addSpacing(10) simulation_mode_layout.addWidget(QLabel("Simulation mode:"), 0, Qt.AlignVCenter) simulation_mode_layout.addWidget(self._simulation_mode_combo, 0, Qt.AlignVCenter) simulation_mode_layout.addSpacing(20) self.run_button = QToolButton() self.run_button.setObjectName('start_simulation') self.run_button.setIconSize(QSize(32, 32)) self.run_button.setText("Start Simulation") self.run_button.setIcon(resourceIcon("ide/gear_in_play")) self.run_button.clicked.connect(self.runSimulation) self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) addHelpToWidget(self.run_button, "run/start_simulation") simulation_mode_layout.addWidget(self.run_button) simulation_mode_layout.addStretch(1) layout.addSpacing(5) layout.addLayout(simulation_mode_layout) layout.addSpacing(10) self._simulation_stack = QStackedWidget() self._simulation_stack.setLineWidth(1) self._simulation_stack.setFrameStyle(QFrame.StyledPanel) layout.addWidget(self._simulation_stack) self._simulation_widgets = OrderedDict() """ :type: OrderedDict[BaseRunModel,SimulationConfigPanel]""" if not FeatureToggling.is_enabled("prefect"): self.addSimulationConfigPanel(SingleTestRunPanel()) self.addSimulationConfigPanel(EnsembleExperimentPanel()) if FeatureToggling.is_enabled("prefect"): prefect_config = os.path.basename(config_file) self.addSimulationConfigPanel( PrefectEnsembleExperimentPanel(prefect_config)) if ERT.ert.have_observations( ) and not FeatureToggling.is_enabled("prefect"): self.addSimulationConfigPanel(EnsembleSmootherPanel()) self.addSimulationConfigPanel(MultipleDataAssimilationPanel()) self.addSimulationConfigPanel(IteratedEnsembleSmootherPanel()) self.setLayout(layout)
class DesignTab(QWidget): def __init__(self): super(DesignTab, self).__init__() self.setup_ui() self.grain_dialog = None self.btn_new_grain.clicked.connect(self.create_grain_dialog) def create_grain_dialog(self): self.grain_dialog = NewGrainDialog() self.grain_dialog.show() self.grain_dialog.activateWindow() self.grain_dialog.raise_() def setup_ui(self): sp = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setSizePolicy(sp) def setup_grain_design(): # grain table view master = QHBoxLayout(self) master.addWidget(QTableView()) # grain design controls controls = QVBoxLayout(self) # add a push button self.btn_new_grain = QPushButton(self.tr("New Grain")) self.btn_new_grain.setMinimumHeight(50) controls.addWidget(self.btn_new_grain) # add a dividing line line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) controls.addSpacing(5) controls.addWidget(line) # rest of the controls buttons self.btn_edit_grain = QPushButton(self.tr("Edit")) self.btn_edit_grain.setMinimumHeight(30) controls.addWidget(self.btn_edit_grain) self.btn_delete_Grain = QPushButton(self.tr("Delete")) self.btn_delete_Grain.setMinimumHeight(30) controls.addWidget(self.btn_delete_Grain) # move grain up and down moveup = QHBoxLayout() self.btn_move_up = QToolButton() self.btn_move_up.setArrowType(Qt.UpArrow) moveup.addWidget(self.btn_move_up) moveup.addWidget(QLabel(self.tr("Move Up"))) controls.addLayout(moveup) movedown = QHBoxLayout() self.btn_move_down = QToolButton() self.btn_move_down.setArrowType(Qt.DownArrow) movedown.addWidget(self.btn_move_down) movedown.addWidget(QLabel(self.tr("Move Down"))) controls.addLayout(movedown) controls.addStretch() # add info for motor design fl_propellant_info = QFormLayout() gb_motor_info = QGroupBox(self.tr("Propellant Info")) gb_motor_info.setLayout(fl_propellant_info) self.lbl_num_grains = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Number of Segments:")), self.lbl_num_grains) self.lbl_motor_dia = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Motor Diameter:")), self.lbl_motor_dia) self.lbl_motor_len = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Propellant Length:")), self.lbl_motor_len) self.lbl_prop_mass = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Propellant Mass:")), self.lbl_prop_mass) self.lbl_volume_loading = QLabel() fl_propellant_info.addRow(QLabel(self.tr("Volume Loading:")), self.lbl_volume_loading) # set group box's layout controls.addWidget(gb_motor_info) # setup master layout master.addLayout(controls) self.gb_design = QGroupBox(self.tr("Grain Design")) self.gb_design.setLayout(master) def setup_chamber_design(): # master layout master = QVBoxLayout() # master group box self.gb_chamber = QGroupBox(self.tr("Chamber Design")) # nozzle settings button self.btn_nozzle_settings = QPushButton(self.tr("Edit Nozzle")) self.btn_nozzle_settings.setMinimumHeight(50) master.addWidget(self.btn_nozzle_settings) # nozzle info pane fl_nozzle_info = QFormLayout() gb_nozzle = QGroupBox(self.tr("Nozzle Info")) gb_nozzle.setLayout(fl_nozzle_info) self.lbl_nozzle_throat = QLabel() fl_nozzle_info.addRow(QLabel(self.tr("Throat Diameter:")), self.lbl_nozzle_throat) self.lbl_nozzle_exit = QLabel() fl_nozzle_info.addRow(QLabel(self.tr("Exit Diameter:")), self.lbl_nozzle_exit) self.lbl_nozzle_expansion_ratio = QLabel() fl_nozzle_info.addRow(QLabel(self.tr("Expansion Ratio:")), self.lbl_nozzle_expansion_ratio) # add group box to master master.addWidget(gb_nozzle) master.addStretch() # overall motor info pane fl_motor_info = QFormLayout() gb_motor = QGroupBox(self.tr("Motor Info")) gb_motor.setLayout(fl_motor_info) self.lbl_kn = QLabel() fl_motor_info.addRow(QLabel(self.tr("Kn:")), self.lbl_kn) self.lbl_port_throat = QLabel() fl_motor_info.addRow(QLabel(self.tr("Port/Throat Ratio:")), self.lbl_port_throat) master.addWidget(gb_motor) self.gb_chamber.setLayout(master) def setup_gfx_ui(): # design overview self.motor_display_view = QGraphicsView() self.motor_display_scene = QGraphicsScene() self.motor_display_view.setScene(self.motor_display_scene) self.motor_display_view.show() # sliced cross section self.grain_slice_view = QGraphicsView() self.grain_slice_scene = QGraphicsScene() self.grain_slice_view.setScene(self.grain_slice_scene) self.grain_slice_view.show() # splitter self.splt_gfx = QSplitter(Qt.Horizontal) self.splt_gfx.addWidget(self.motor_display_view) self.splt_gfx.addWidget(self.grain_slice_view) self.splt_gfx.setStretchFactor(0, 10) self.splt_gfx.setStretchFactor(1, 3) self.splt_gfx.setMinimumHeight(50) setup_grain_design() setup_chamber_design() setup_gfx_ui() self.splt_grain_design = QSplitter(Qt.Horizontal) self.splt_grain_design.addWidget(self.gb_design) self.splt_grain_design.addWidget(self.gb_chamber) self.splt_grain_design.setStretchFactor(0, 10) self.splt_grain_design.setStretchFactor(1, 3) self.splt_main = QSplitter(Qt.Vertical) self.splt_main.addWidget(self.splt_grain_design) self.splt_main.addWidget(self.splt_gfx) self.splt_main.setSizes([300, 150]) layout = QVBoxLayout() layout.addWidget(self.splt_main) self.setLayout(layout)
class NumpyArrayDialog(QDialog): def __init__(self, parent=None, inline=True, offset=0, force_float=False): QDialog.__init__(self, parent=parent) self._parent = parent self._text = None self._valid = None self._offset = offset # TODO: add this as an option in the General Preferences? self._force_float = force_float self._help_inline = _(""" <b>Numpy Array/Matrix Helper</b><br> Type an array in Matlab : <code>[1 2;3 4]</code><br> or Spyder simplified syntax : <code>1 2;3 4</code> <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two spaces or two tabs to generate a ';'. """) self._help_table = _(""" <b>Numpy Array/Matrix Helper</b><br> Enter an array in the table. <br> Use Tab to move between cells. <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two tabs at the end of a row to move to the next row. """) # Widgets self._button_warning = QToolButton() self._button_help = HelperToolButton() self._button_help.setIcon(ima.icon('MessageBoxInformation')) style = """ QToolButton { border: 1px solid grey; padding:0px; border-radius: 2px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f6f7fa, stop: 1 #dadbde); } """ self._button_help.setStyleSheet(style) if inline: self._button_help.setToolTip(self._help_inline) self._text = NumpyArrayInline(self) self._widget = self._text else: self._button_help.setToolTip(self._help_table) self._table = NumpyArrayTable(self) self._widget = self._table style = """ QDialog { margin:0px; border: 1px solid grey; padding:0px; border-radius: 2px; }""" self.setStyleSheet(style) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self._button_warning.setStyleSheet(style) # widget setup self.setWindowFlags(Qt.Window | Qt.Dialog | Qt.FramelessWindowHint) self.setModal(True) self.setWindowOpacity(0.90) self._widget.setMinimumWidth(200) # layout self._layout = QHBoxLayout() self._layout.addWidget(self._widget) self._layout.addWidget(self._button_warning, 1, Qt.AlignTop) self._layout.addWidget(self._button_help, 1, Qt.AlignTop) self.setLayout(self._layout) self._widget.setFocus() def keyPressEvent(self, event): """ Qt override. """ QToolTip.hideText() ctrl = event.modifiers() & Qt.ControlModifier if event.key() in [Qt.Key_Enter, Qt.Key_Return]: if ctrl: self.process_text(array=False) else: self.process_text(array=True) self.accept() else: QDialog.keyPressEvent(self, event) def event(self, event): """ Qt Override. Usefull when in line edit mode. """ if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab: return False return QWidget.event(self, event) def process_text(self, array=True): """ Construct the text based on the entered content in the widget. """ if array: prefix = 'np.array([[' else: prefix = 'np.matrix([[' suffix = ']])' values = self._widget.text().strip() if values != '': # cleans repeated spaces exp = r'(\s*)' + ROW_SEPARATOR + r'(\s*)' values = re.sub(exp, ROW_SEPARATOR, values) values = re.sub(r"\s+", " ", values) values = re.sub(r"]$", "", values) values = re.sub(r"^\[", "", values) values = re.sub(ROW_SEPARATOR + r'*$', '', values) # replaces spaces by commas values = values.replace(' ', ELEMENT_SEPARATOR) # iterate to find number of rows and columns new_values = [] rows = values.split(ROW_SEPARATOR) nrows = len(rows) ncols = [] for row in rows: new_row = [] elements = row.split(ELEMENT_SEPARATOR) ncols.append(len(elements)) for e in elements: num = e # replaces not defined values if num in NAN_VALUES: num = 'np.nan' # Convert numbers to floating point if self._force_float: try: num = str(float(e)) except: pass new_row.append(num) new_values.append(ELEMENT_SEPARATOR.join(new_row)) new_values = ROW_SEPARATOR.join(new_values) values = new_values # Check validity if len(set(ncols)) == 1: self._valid = True else: self._valid = False # Single rows are parsed as 1D arrays/matrices if nrows == 1: prefix = prefix[:-1] suffix = suffix.replace("]])", "])") # Fix offset offset = self._offset braces = BRACES.replace(' ', '\n' + ' ' * (offset + len(prefix) - 1)) values = values.replace(ROW_SEPARATOR, braces) text = "{0}{1}{2}".format(prefix, values, suffix) self._text = text else: self._text = '' self.update_warning() def update_warning(self): """ Updates the icon and tip based on the validity of the array content. """ widget = self._button_warning if not self.is_valid(): tip = _('Array dimensions not valid') widget.setIcon(ima.icon('MessageBoxWarning')) widget.setToolTip(tip) QToolTip.showText(self._widget.mapToGlobal(QPoint(0, 5)), tip) else: self._button_warning.setToolTip('') def is_valid(self): """ Return if the current array state is valid. """ return self._valid def text(self): """ Return the parsed array/matrix text. """ return self._text @property def array_widget(self): """ Return the array builder widget. """ return self._widget
def __init__(self, parent=None, inline=True, offset=0, force_float=False): QDialog.__init__(self, parent=parent) self._parent = parent self._text = None self._valid = None self._offset = offset # TODO: add this as an option in the General Preferences? self._force_float = force_float self._help_inline = _(""" <b>Numpy Array/Matrix Helper</b><br> Type an array in Matlab : <code>[1 2;3 4]</code><br> or Spyder simplified syntax : <code>1 2;3 4</code> <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two spaces or two tabs to generate a ';'. """) self._help_table = _(""" <b>Numpy Array/Matrix Helper</b><br> Enter an array in the table. <br> Use Tab to move between cells. <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two tabs at the end of a row to move to the next row. """) # Widgets self._button_warning = QToolButton() self._button_help = HelperToolButton() self._button_help.setIcon(ima.icon('MessageBoxInformation')) style = """ QToolButton { border: 1px solid grey; padding:0px; border-radius: 2px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f6f7fa, stop: 1 #dadbde); } """ self._button_help.setStyleSheet(style) if inline: self._button_help.setToolTip(self._help_inline) self._text = NumpyArrayInline(self) self._widget = self._text else: self._button_help.setToolTip(self._help_table) self._table = NumpyArrayTable(self) self._widget = self._table style = """ QDialog { margin:0px; border: 1px solid grey; padding:0px; border-radius: 2px; }""" self.setStyleSheet(style) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self._button_warning.setStyleSheet(style) # widget setup self.setWindowFlags(Qt.Window | Qt.Dialog | Qt.FramelessWindowHint) self.setModal(True) self.setWindowOpacity(0.90) self._widget.setMinimumWidth(200) # layout self._layout = QHBoxLayout() self._layout.addWidget(self._widget) self._layout.addWidget(self._button_warning, 1, Qt.AlignTop) self._layout.addWidget(self._button_help, 1, Qt.AlignTop) self.setLayout(self._layout) self._widget.setFocus()
def create_toolbutton(parent, text=None, shortcut=None, icon=None, tip=None, toggled=None, triggered=None, autoraise=True, text_beside_icon=False): """Create a QToolButton""" button = QToolButton(parent) if text is not None: button.setText(text) if icon is not None: if is_text_string(icon): icon = get_icon(icon) button.setIcon(icon) if text is not None or tip is not None: button.setToolTip(text if tip is None else tip) if text_beside_icon: button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setAutoRaise(autoraise) if triggered is not None: button.clicked.connect(triggered) if toggled is not None: button.toggled.connect(toggled) button.setCheckable(True) if shortcut is not None: button.setShortcut(shortcut) return button
class NumpyArrayDialog(QDialog): def __init__(self, parent=None, inline=True, offset=0, force_float=False): QDialog.__init__(self, parent=parent) self._parent = parent self._text = None self._valid = None self._offset = offset # TODO: add this as an option in the General Preferences? self._force_float = force_float self._help_inline = _(""" <b>Numpy Array/Matrix Helper</b><br> Type an array in Matlab : <code>[1 2;3 4]</code><br> or Spyder simplified syntax : <code>1 2;3 4</code> <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two spaces or two tabs to generate a ';'. """) self._help_table = _(""" <b>Numpy Array/Matrix Helper</b><br> Enter an array in the table. <br> Use Tab to move between cells. <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two tabs at the end of a row to move to the next row. """) # Widgets self._button_warning = QToolButton() self._button_help = HelperToolButton() self._button_help.setIcon(ima.icon('MessageBoxInformation')) style = """ QToolButton { border: 1px solid grey; padding:0px; border-radius: 2px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f6f7fa, stop: 1 #dadbde); } """ self._button_help.setStyleSheet(style) if inline: self._button_help.setToolTip(self._help_inline) self._text = NumpyArrayInline(self) self._widget = self._text else: self._button_help.setToolTip(self._help_table) self._table = NumpyArrayTable(self) self._widget = self._table style = """ QDialog { margin:0px; border: 1px solid grey; padding:0px; border-radius: 2px; }""" self.setStyleSheet(style) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self._button_warning.setStyleSheet(style) # widget setup self.setWindowFlags(Qt.Window | Qt.Dialog | Qt.FramelessWindowHint) self.setModal(True) self.setWindowOpacity(0.90) self._widget.setMinimumWidth(200) # layout self._layout = QHBoxLayout() self._layout.addWidget(self._widget) self._layout.addWidget(self._button_warning, 1, Qt.AlignTop) self._layout.addWidget(self._button_help, 1, Qt.AlignTop) self.setLayout(self._layout) self._widget.setFocus() def keyPressEvent(self, event): """ Qt override. """ QToolTip.hideText() ctrl = event.modifiers() & Qt.ControlModifier if event.key() in [Qt.Key_Enter, Qt.Key_Return]: if ctrl: self.process_text(array=False) else: self.process_text(array=True) self.accept() else: QDialog.keyPressEvent(self, event) def event(self, event): """ Qt Override. Usefull when in line edit mode. """ if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab: return False return QWidget.event(self, event) def process_text(self, array=True): """ Construct the text based on the entered content in the widget. """ if array: prefix = 'np.array([[' else: prefix = 'np.matrix([[' suffix = ']])' values = self._widget.text().strip() if values != '': # cleans repeated spaces exp = r'(\s*)' + ROW_SEPARATOR + r'(\s*)' values = re.sub(exp, ROW_SEPARATOR, values) values = re.sub("\s+", " ", values) values = re.sub("]$", "", values) values = re.sub("^\[", "", values) values = re.sub(ROW_SEPARATOR + r'*$', '', values) # replaces spaces by commas values = values.replace(' ', ELEMENT_SEPARATOR) # iterate to find number of rows and columns new_values = [] rows = values.split(ROW_SEPARATOR) nrows = len(rows) ncols = [] for row in rows: new_row = [] elements = row.split(ELEMENT_SEPARATOR) ncols.append(len(elements)) for e in elements: num = e # replaces not defined values if num in NAN_VALUES: num = 'np.nan' # Convert numbers to floating point if self._force_float: try: num = str(float(e)) except: pass new_row.append(num) new_values.append(ELEMENT_SEPARATOR.join(new_row)) new_values = ROW_SEPARATOR.join(new_values) values = new_values # Check validity if len(set(ncols)) == 1: self._valid = True else: self._valid = False # Single rows are parsed as 1D arrays/matrices if nrows == 1: prefix = prefix[:-1] suffix = suffix.replace("]])", "])") # Fix offset offset = self._offset braces = BRACES.replace(' ', '\n' + ' '*(offset + len(prefix) - 1)) values = values.replace(ROW_SEPARATOR, braces) text = "{0}{1}{2}".format(prefix, values, suffix) self._text = text else: self._text = '' self.update_warning() def update_warning(self): """ Updates the icon and tip based on the validity of the array content. """ widget = self._button_warning if not self.is_valid(): tip = _('Array dimensions not valid') widget.setIcon(ima.icon('MessageBoxWarning')) widget.setToolTip(tip) QToolTip.showText(self._widget.mapToGlobal(QPoint(0, 5)), tip) else: self._button_warning.setToolTip('') def is_valid(self): """ Return if the current array state is valid. """ return self._valid def text(self): """ Return the parsed array/matrix text. """ return self._text @property def array_widget(self): """ Return the array builder widget. """ return self._widget
class ModernWindow(QWidget): """ Modern window. Args: w (QWidget): Main widget. parent (QWidget, optional): Parent widget. """ def __init__(self, w, parent=None): QWidget.__init__(self, parent) self._w = w self.setupUi() contentLayout = QHBoxLayout() contentLayout.setContentsMargins(0, 0, 0, 0) contentLayout.addWidget(w) self.windowContent.setLayout(contentLayout) self.setWindowTitle(w.windowTitle()) self.setGeometry(w.geometry()) self.installEventFilter(self) def setupUi(self): # create title bar, content self.vboxWindow = QVBoxLayout(self) self.vboxWindow.setContentsMargins(0, 0, 0, 0) self.windowFrame = QWidget(self) self.windowFrame.setObjectName('windowFrame') self.vboxFrame = QVBoxLayout(self.windowFrame) self.vboxFrame.setContentsMargins(0, 0, 0, 0) self.titleBar = WindowDragger(self, self.windowFrame) self.titleBar.setObjectName('titleBar') self.titleBar.setSizePolicy( QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)) self.hboxTitle = QHBoxLayout(self.titleBar) self.hboxTitle.setContentsMargins(0, 0, 0, 0) self.hboxTitle.setSpacing(0) self.lblTitle = QLabel('Title') self.lblTitle.setObjectName('lblTitle') self.lblTitle.setAlignment(Qt.AlignCenter) self.hboxTitle.addWidget(self.lblTitle) spButtons = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.btnMinimize = QToolButton(self.titleBar) self.btnMinimize.setObjectName('btnMinimize') self.btnMinimize.setSizePolicy(spButtons) self.hboxTitle.addWidget(self.btnMinimize) self.btnRestore = QToolButton(self.titleBar) self.btnRestore.setObjectName('btnRestore') self.btnRestore.setSizePolicy(spButtons) self.btnRestore.setVisible(False) self.hboxTitle.addWidget(self.btnRestore) self.btnMaximize = QToolButton(self.titleBar) self.btnMaximize.setObjectName('btnMaximize') self.btnMaximize.setSizePolicy(spButtons) self.hboxTitle.addWidget(self.btnMaximize) self.btnClose = QToolButton(self.titleBar) self.btnClose.setObjectName('btnClose') self.btnClose.setSizePolicy(spButtons) self.hboxTitle.addWidget(self.btnClose) self.vboxFrame.addWidget(self.titleBar) self.windowContent = QWidget(self.windowFrame) self.vboxFrame.addWidget(self.windowContent) self.vboxWindow.addWidget(self.windowFrame) # set window flags self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint) if QT_VERSION >= (5, ): self.setAttribute(Qt.WA_TranslucentBackground) # set stylesheet with open(_FL_STYLESHEET) as stylesheet: self.setStyleSheet(stylesheet.read()) # automatically connect slots QMetaObject.connectSlotsByName(self) def eventFilter(self, source, event): if event.type() == QEvent.Close: return self._w.close() return QWidget.eventFilter(self, source, event) def setWindowTitle(self, title): """ Set window title. Args: title (str): Title. """ super(ModernWindow, self).setWindowTitle(title) self.lblTitle.setText(title) @Slot() def on_btnMinimize_clicked(self): self.setWindowState(Qt.WindowMinimized) @Slot() def on_btnRestore_clicked(self): self.btnRestore.setVisible(False) self.btnMaximize.setVisible(True) self.setWindowState(Qt.WindowNoState) @Slot() def on_btnMaximize_clicked(self): self.btnRestore.setVisible(True) self.btnMaximize.setVisible(False) self.setWindowState(Qt.WindowMaximized) @Slot() def on_btnClose_clicked(self): self.close() @Slot() def on_titleBar_doubleClicked(self): if self.btnMaximize.isVisible(): self.on_btnMaximize_clicked() else: self.on_btnRestore_clicked()
class FadingTipBox(FadingDialog): """ """ def __init__(self, parent, opacity, duration, easing_curve, tour=None): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.tour = tour self.frames = None self.color_top = QColor.fromRgb(230, 230, 230) self.color_back = QColor.fromRgb(255, 255, 255) self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets def toolbutton(icon): bt = QToolButton() bt.setAutoRaise(True) bt.setIcon(icon) return bt self.button_close = toolbutton(ima.icon("tour.close")) self.button_home = toolbutton(ima.icon("tour.home")) self.button_previous = toolbutton(ima.icon("tour.previous")) self.button_end = toolbutton(ima.icon("tour.end")) self.button_next = toolbutton(ima.icon("tour.next")) self.button_run = QPushButton(_('Run code')) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current] arrow = get_image_path('hide.png') self.stylesheet = '''QComboBox { padding-left: 5px; background-color: rgbs(230,230,230,100%); border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; } QComboBox::down-arrow { image: url(''' + arrow + '''); } ''' # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace('\\', '/') self.setFocusPolicy(Qt.StrongFocus) for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets, self.setFocus]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu) # signals and slots # These are defined every time by the AnimatedTour Class def _disable_widgets(self): """ """ for widget in self.widgets: widget.setDisabled(True) def _enable_widgets(self): """ """ self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) for widget in self.widgets: widget.setDisabled(False) if self.button_disable == 'previous': self.button_previous.setDisabled(True) self.button_home.setDisabled(True) elif self.button_disable == 'next': self.button_next.setDisabled(True) self.button_end.setDisabled(True) def set_data(self, title, content, current, image, run, frames=None, step=None): """ """ self.label_title.setText(title) self.combo_title.clear() self.combo_title.addItems(frames) self.combo_title.setCurrentIndex(step) # min_content_len = max([len(f) for f in frames]) # self.combo_title.setMinimumContentsLength(min_content_len) # Fix and try to see how it looks with a combo box self.label_current.setText(current) self.button_current.setText(current) self.label_content.setText(content) self.image = image if image is None: self.label_image.setFixedHeight(1) self.label_image.setFixedWidth(1) else: extension = image.split('.')[-1] self.image = QPixmap(get_image_path(image), extension) self.label_image.setPixmap(self.image) self.label_image.setFixedSize(self.image.size()) if run is None: self.button_run.setVisible(False) else: self.button_run.setDisabled(False) self.button_run.setVisible(True) # Refresh layout self.layout().activate() def set_pos(self, x, y): """ """ self.x = x self.y = y self.move(QPoint(x, y)) def build_paths(self): """ """ geo = self.geometry() radius = 0 shadow = self.offset_shadow x0, y0 = geo.x(), geo.y() width, height = geo.width() - shadow, geo.height() - shadow left, top = 0, 0 right, bottom = width, height self.round_rect_path = QPainterPath() self.round_rect_path.moveTo(right, top + radius) self.round_rect_path.arcTo(right-radius, top, radius, radius, 0.0, 90.0) self.round_rect_path.lineTo(left+radius, top) self.round_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.round_rect_path.lineTo(left, bottom-radius) self.round_rect_path.arcTo(left, bottom-radius, radius, radius, 180.0, 90.0) self.round_rect_path.lineTo(right-radius, bottom) self.round_rect_path.arcTo(right-radius, bottom-radius, radius, radius, 270.0, 90.0) self.round_rect_path.closeSubpath() # Top path header = 36 offset = 2 left, top = offset, offset right = width - (offset) self.top_rect_path = QPainterPath() self.top_rect_path.lineTo(right, top + radius) self.top_rect_path.moveTo(right, top + radius) self.top_rect_path.arcTo(right-radius, top, radius, radius, 0.0, 90.0) self.top_rect_path.lineTo(left+radius, top) self.top_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.top_rect_path.lineTo(left, top + header) self.top_rect_path.lineTo(right, top + header) def paintEvent(self, event): """ """ self.build_paths() painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillPath(self.round_rect_path, self.color_back) painter.fillPath(self.top_rect_path, self.color_top) painter.strokePath(self.round_rect_path, QPen(Qt.gray, 1)) # TODO: Build the pointing arrow? def keyReleaseEvent(self, event): """ """ key = event.key() self.key_pressed = key keys = [Qt.Key_Right, Qt.Key_Left, Qt.Key_Down, Qt.Key_Up, Qt.Key_Escape, Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Home, Qt.Key_End, Qt.Key_Menu] if key in keys: if not self.is_fade_running(): self.sig_key_pressed.emit() def mousePressEvent(self, event): """override Qt method""" # Raise the main application window on click self.parent.raise_() self.raise_() if event.button() == Qt.RightButton: pass # clicked_widget = self.childAt(event.x(), event.y()) # if clicked_widget == self.label_current: # self.context_menu_requested(event) def focusOutEvent(self, event): """Override Qt method.""" # To be used so tips do not appear outside spyder self.tour.lost_focus() def context_menu_requested(self, event): """ """ pos = QPoint(event.x(), event.y()) menu = QMenu(self) actions = [] action_title = create_action(self, _('Go to step: '), icon=QIcon()) action_title.setDisabled(True) actions.append(action_title) # actions.append(create_action(self, _(': '), icon=QIcon())) add_actions(menu, actions) menu.popup(self.mapToGlobal(pos)) def reject(self): """Qt method to handle escape key event""" if not self.is_fade_running(): key = Qt.Key_Escape self.key_pressed = key self.sig_key_pressed.emit()
def setupUi(self): # create title bar, content self.vboxWindow = QVBoxLayout(self) self.vboxWindow.setContentsMargins(0, 0, 0, 0) self.windowFrame = QWidget(self) self.windowFrame.setObjectName('windowFrame') self.vboxFrame = QVBoxLayout(self.windowFrame) self.vboxFrame.setContentsMargins(0, 0, 0, 0) self.titleBar = WindowDragger(self, self.windowFrame) self.titleBar.setObjectName('titleBar') self.titleBar.setSizePolicy( QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)) self.hboxTitle = QHBoxLayout(self.titleBar) self.hboxTitle.setContentsMargins(0, 0, 0, 0) self.hboxTitle.setSpacing(0) self.lblTitle = QLabel('Title') self.lblTitle.setObjectName('lblTitle') self.lblTitle.setAlignment(Qt.AlignCenter) self.hboxTitle.addWidget(self.lblTitle) spButtons = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.btnMinimize = QToolButton(self.titleBar) self.btnMinimize.setObjectName('btnMinimize') self.btnMinimize.setSizePolicy(spButtons) self.hboxTitle.addWidget(self.btnMinimize) self.btnRestore = QToolButton(self.titleBar) self.btnRestore.setObjectName('btnRestore') self.btnRestore.setSizePolicy(spButtons) self.btnRestore.setVisible(False) self.hboxTitle.addWidget(self.btnRestore) self.btnMaximize = QToolButton(self.titleBar) self.btnMaximize.setObjectName('btnMaximize') self.btnMaximize.setSizePolicy(spButtons) self.hboxTitle.addWidget(self.btnMaximize) self.btnClose = QToolButton(self.titleBar) self.btnClose.setObjectName('btnClose') self.btnClose.setSizePolicy(spButtons) self.hboxTitle.addWidget(self.btnClose) self.vboxFrame.addWidget(self.titleBar) self.windowContent = QWidget(self.windowFrame) self.vboxFrame.addWidget(self.windowContent) self.vboxWindow.addWidget(self.windowFrame) # set window flags self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint) if QT_VERSION >= (5, ): self.setAttribute(Qt.WA_TranslucentBackground) # set stylesheet with open(_FL_STYLESHEET) as stylesheet: self.setStyleSheet(stylesheet.read()) # automatically connect slots QMetaObject.connectSlotsByName(self)
def toolbutton(icon): bt = QToolButton() bt.setAutoRaise(True) bt.setIcon(icon) return bt
def __init__(self, parent=None): QToolButton.__init__(self, parent) self.setFixedSize(20, 20) self.setIconSize(QSize(12, 12)) self.clicked.connect(self.choose_color) self._color = QColor()