def filename(self, new_file): """ The filename of the image to be displayed. This file can be either relative to the ``.ui`` file or absolute. If the path does not exist, a shape of ``.null_color`` will be displayed instead. Parameters ------- new_file : str The filename to be used """ # Expand user (~ or ~user) and environment variables. pixmap = None self._file = new_file abs_path = os.path.expanduser(os.path.expandvars(self._file)) # Find the absolute path relative to UI if not os.path.isabs(abs_path): try: # Based on the QApplication if is_pydm_app(): abs_path = QApplication.instance().get_path(abs_path) # Based on the QtDesigner elif is_qt_designer(): # pragma: no cover p = self.get_designer_window() if p is not None: ui_dir = p.absoluteDir().absolutePath() abs_path = os.path.join(ui_dir, abs_path) except Exception: logger.exception("Unable to find full filepath for %s", self._file) # Check that the path exists if os.path.isfile(abs_path): if self._movie is not None: self._movie.stop() self._movie.deleteLater() self._movie = None if not abs_path.endswith(".gif"): pixmap = QPixmap(abs_path) else: self._movie = QMovie(abs_path, parent=self) self._movie.setCacheMode(QMovie.CacheAll) self._movie.frameChanged.connect(self.movie_frame_changed) if self._movie.frameCount() > 1: self._movie.finished.connect(self.movie_finished) self._movie.start() # Return a blank image if we don't have a valid path else: # Warn the user loudly if their file does not exist, but avoid # doing this in Designer as this spams the user as they are typing if not is_qt_designer(): # pragma: no cover logger.error("Image file %r does not exist", abs_path) pixmap = QPixmap(self.sizeHint()) pixmap.fill(self.null_color) # Update the display if pixmap is not None: self._pixmap = pixmap self.update()
def __init__(self, parent=None) -> None: super().__init__(parent=parent) self.setLayout(QHBoxLayout()) self._activityBtn = QToolButton() self._activityBtn.setObjectName("QtActivityButton") self._activityBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._activityBtn.setArrowType(Qt.UpArrow) self._activityBtn.setIconSize(QSize(11, 11)) self._activityBtn.setText(trans._('activity')) self._activityBtn.setCheckable(True) self._inProgressIndicator = QLabel(trans._("in progress..."), self) sp = self._inProgressIndicator.sizePolicy() sp.setRetainSizeWhenHidden(True) self._inProgressIndicator.setSizePolicy(sp) load_gif = str(Path(napari.resources.__file__).parent / "loading.gif") mov = QMovie(load_gif) mov.setScaledSize(QSize(18, 18)) self._inProgressIndicator.setMovie(mov) self._inProgressIndicator.hide() self.layout().addWidget(self._inProgressIndicator) self.layout().addWidget(self._activityBtn) self.layout().setContentsMargins(0, 0, 0, 0)
def _setupUi(self): cw = QWidget() self.setCentralWidget(cw) label = QLabel('<h3>ID Control Window</h3>', self, alignment=Qt.AlignCenter) label.setStyleSheet('QLabel{min-height: 3em; max-height: 3em;}') self.label_mov1 = QLabel(self) self.label_mov1.setVisible(False) self.label_mov1.setStyleSheet( 'QLabel{min-height: 3em; max-height: 3em;}') self.label_mov2 = QLabel(self) self.label_mov2.setVisible(False) self.label_mov2.setStyleSheet( 'QLabel{min-height: 3em; max-height: 3em;}') self.movie_mov = QMovie(_os.path.join( _os.path.abspath(_os.path.dirname(__file__)), 'hula.gif')) self.movie_mov.setScaledSize(QSize(50, 50)) self.label_mov1.setMovie(self.movie_mov) self.label_mov2.setMovie(self.movie_mov) self._gbox_apu = QGroupBox('APU', self) self._gbox_apu.setLayout(self._setupAPULayout()) lay = QGridLayout(cw) lay.addWidget(self.label_mov1, 0, 0) lay.addWidget(label, 0, 1) lay.addWidget(self.label_mov2, 0, 2) lay.addWidget(self._gbox_apu, 1, 0, 1, 3) lay.setColumnStretch(0, 1) lay.setColumnStretch(1, 15) lay.setColumnStretch(2, 1)
def __init__(self, name=None, prefix=None): super(ListItemEnvironment, self).__init__() self._selected = False self._name = name self._prefix = prefix # Widgets self.button_options = ButtonEnvironmentOptions() self.button_name = ButtonEnvironmentName(name) self.label_icon = LabelEnvironmentIcon() self.movie = QMovie(SPINNER_GREEN_16_PATH) self.widget = WidgetEnvironment() self.widget.setFocusPolicy(Qt.NoFocus) self.widget.button_name = self.button_name self.widget.button_options = self.button_options # Widget setup self.label_icon.setMovie(self.movie) self.button_name.setDefault(True) # Layouts layout = QHBoxLayout() layout.addWidget(self.label_icon) layout.addWidget(self.button_name) layout.addWidget(self.button_options) layout.addStretch() self.widget.setLayout(layout) self.widget.setStyleSheet(load_style_sheet()) self.setSizeHint(self.widget.sizeHint())
def __init__(self): super().__init__() self.setWindowFlags(QtCore.Qt.FramelessWindowHint) layout = QHBoxLayout(self) self.label = QLabel() layout.addWidget(self.label) self.setLayout(layout) self.movie = QMovie(osp.join(pjpath, "resource", "loading.gif")) self.label.setMovie(self.movie) self.movie.start()
def __init__(self, log_path: str, initial_length: int, f: int = Qt.WindowStaysOnTopHint | Qt.SplashScreen): """ A QSplashScreen customized to display an animated gif. The splash triggers launch when clicked. After minsplashtime, this splash waits until the animation finishes before triggering the launch. Parameters ---------- log_path : str Path to the Xi-CAM log file to reflect initial_length: int Length in bytes to seek forward before reading f : int Extra flags (see base class) """ # Get logo movie from relative path self.movie = QMovie(str(static.path("images/animated_logo.gif"))) # Setup drawing self.movie.frameChanged.connect(self.paintFrame) self.movie.jumpToFrame(1) self.pixmap = QPixmap(self.movie.frameRect().size()) super(XicamSplashScreen, self).__init__(self.pixmap, f) self.setMask(self.pixmap.mask()) self.movie.finished.connect(self.restartmovie) self.showMessage('Starting Xi-CAM...') self._launching = False self._launchready = False self.timer = QTimer(self) self.log_file = open(log_path, 'r') self.log_file.seek(initial_length) # Start splashing self.setAttribute(Qt.WA_DeleteOnClose) self.show() self.raise_() self.activateWindow() QApplication.instance().setActiveWindow(self) # Setup timed triggers for launching the QMainWindow self.timer.singleShot(self.minsplashtime, self.launchwindow)
def set_loading(self, value): # self.button_options.setDisabled(value) if value: self.label_icon.setMovie(self.movie) self.movie.start() else: self.label_icon.setMovie(QMovie()) self.button_options.setFocus()
def __init__(self, parent): super(KiteWelcome, self).__init__(parent) self.setFixedHeight(350) # Left side install_info = QLabel( _("<big><b>Level up your completions with " "Kite</b></big><br><br>" "Kite is a native app that runs locally " "on your computer <br>and uses machine learning " "to provide advanced <br>completions.<br><br>" "✓ Specialized support for Python " "data analysis packages<br><br>" "✓ 1.5x more completions " "than the builtin engine<br><br>" "✓ Completions ranked by code context <br><br>" "✓ Full line code completions<br><br>" "✓ 100% local - no internet " "connection required<br><br>" "✓ 100% free to use<br><br>" "<a href=\"https://kite.com/spyder-integration\">" "Go to Kite website</a>")) install_info.setOpenExternalLinks(True) # Right side action_layout = QVBoxLayout() install_gif_source = get_image_path('kite.gif') install_gif = QMovie(install_gif_source) install_gif_label = QLabel() install_gif.start() install_image = install_gif.currentPixmap() image_height = install_image.height() * 0.8 image_width = install_image.width() * 0.8 install_gif.setScaledSize(QSize(image_width, image_height)) install_gif_label.setMovie(install_gif) button_layout = QHBoxLayout() install_button = QPushButton(_('Install Kite')) dismiss_button = QPushButton(_('Dismiss')) button_layout.addStretch() button_layout.addWidget(install_button) button_layout.addWidget(dismiss_button) button_layout.addStretch() action_layout.addWidget(install_gif_label) action_layout.addStretch() action_layout.addLayout(button_layout) # Layout general_layout = QHBoxLayout() general_layout.addWidget(install_info) general_layout.addLayout(action_layout) self.setLayout(general_layout) # Signals install_button.clicked.connect(self.sig_install_button_clicked) dismiss_button.clicked.connect(self.sig_dismiss_button_clicked)
def __init__(self, mainwindow: Callable[[], QMainWindow] = None, f: int = Qt.WindowStaysOnTopHint | Qt.SplashScreen): """ A QSplashScreen customized to display an animated gif. The splash triggers launch when clicked. After minsplashtime, this splash waits until the animation finishes before triggering the launch. Parameters ---------- mainwindow : class Subclass of QMainWindow to display after splashing f : int Extra flags (see base class) """ # Get logo movie from relative path self.movie = QMovie(str(static.path("images/animated_logo.gif"))) # Setup drawing self.movie.frameChanged.connect(self.paintFrame) self.movie.jumpToFrame(1) self.pixmap = QPixmap(self.movie.frameRect().size()) super(XicamSplashScreen, self).__init__(self.pixmap, f) self.setMask(self.pixmap.mask()) self.movie.finished.connect(self.restartmovie) self._launching = False self._launchready = False self.timer = QTimer(self) self.mainwindow = mainwindow # Start splashing self.setAttribute(Qt.WA_DeleteOnClose) self.show() self.raise_() self.activateWindow() QApplication.instance().setActiveWindow(self) if "--nosplash" in sys.argv: self.execlaunch() else: # Setup timed triggers for launching the QMainWindow self.timer.singleShot(self.minsplashtime, self.launchwindow)
def set_loading(self, value): """Set loading status of widget.""" # self.button_options.setDisabled(value) # if value: # self.label_icon.setMovie(self.movie) # self.movie.start() # else: # self.label_icon.setMovie(QMovie()) # self.button_options.setFocus() self.label_icon.setMovie(QMovie()) self.button_options.setFocus()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._icon_size = QSize(32, 32) if TyphosLoading.loading_gif is None: loading_path = os.path.join(ui_dir, 'loading.gif') TyphosLoading.loading_gif = QMovie(loading_path) self._animation = TyphosLoading.loading_gif self._animation.setScaledSize(self._icon_size) self.setMovie(self._animation) self._animation.start() if self.LOADING_TIMEOUT_MS > 0: QtCore.QTimer.singleShot(self.LOADING_TIMEOUT_MS, self._handle_timeout)
def __init__(self, timeout_message, *, parent=None, **kwargs): self.timeout_message = timeout_message super().__init__(parent=parent, **kwargs) self._icon_size = QSize(32, 32) if TyphosLoading.loading_gif is None: loading_path = os.path.join(ui_dir, 'loading.gif') TyphosLoading.loading_gif = QMovie(loading_path) self._animation = TyphosLoading.loading_gif self._animation.setScaledSize(self._icon_size) self.setMovie(self._animation) self._animation.start() if self.LOADING_TIMEOUT_MS > 0: QtCore.QTimer.singleShot(self.LOADING_TIMEOUT_MS, self._handle_timeout) self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
def __init__(self, name=None, description=None, command=None, pixmap=None, version=None, versions=None, path=None, dev_tool=True, prefix=None, is_conda_app=False, packages_widget=None): super(ListItemApplication, self).__init__() self.api = AnacondaAPI() self.command = command self.dev_tool = dev_tool self.installed = False self.is_conda_app = is_conda_app self.name = name self.path = path self.pixmap = pixmap if pixmap else QPixmap(ANACONDA_ICON_64_PATH) self.prefix = prefix self.timeout = 10000 # In miliseconds self.version = version self.versions = versions self.packages_widget = packages_widget # Widgets self.button_install = ButtonApplicationInstall("Install") self.button_launch = ButtonApplicationLaunch("Launch") self.button_options = ButtonApplicationOptions() self.label_icon = LabelApplicationIcon() self.label_name = LabelApplicationName(name) self.label_description = LabelApplicationDescription(description) # self.label_update = LabelApplicationUpdate() self.button_version = ButtonApplicationVersion(to_text_string(version)) self.label_spinner = LabelApplicationSpinner() # self.label_version = LabelApplicationVersion(to_text_string(version)) self.menu_options = QMenu('Application options') self.menu_versions = QMenu('Install specific version') self.movie_spinner = QMovie(SPINNER_WHITE_16_PATH) self.timer = QTimer() self.widget = WidgetApplication() # Widget setup self.button_version.setFocusPolicy(Qt.NoFocus) self.label_name.setToolTip(description) self.label_description.setAlignment(Qt.AlignCenter) self.movie_spinner.start() self.timer.setInterval(self.timeout) self.timer.setSingleShot(True) self.label_icon.setToolTip(description) self.label_icon.setPixmap( self.pixmap.scaled(self.ICON_SIZE, self.ICON_SIZE, Qt.KeepAspectRatio, Qt.SmoothTransformation)) self.label_icon.setAlignment(Qt.AlignCenter) self.label_name.setAlignment(Qt.AlignCenter) self.label_name.setWordWrap(True) self.label_description.setWordWrap(True) self.label_description.setAlignment(Qt.AlignTop | Qt.AlignHCenter) self.label_spinner.setVisible(False) self.label_spinner.setMinimumWidth(16) self.label_spinner.setMinimumHeight(16) # Layouts layout = QVBoxLayout() layout.addWidget(self.button_options, 0, Qt.AlignRight) layout.addWidget(self.label_icon, 0, Qt.AlignCenter) layout.addWidget(self.label_name, 0, Qt.AlignCenter) layout.addWidget(self.label_description, 0, Qt.AlignCenter) # hlayout = QHBoxLayout() # hlayout.addWidget(self.label_update) # hlayout.addWidget(self.label_version) # layout.addLayout(hlayout) # layout.addWidget(self.label_version, 0, Qt.AlignCenter) layout.addWidget(self.button_version, 0, Qt.AlignCenter) layout.addWidget(self.label_spinner, 0, Qt.AlignCenter) layout.addWidget(self.button_launch, 0, Qt.AlignCenter) layout.addWidget(self.button_install, 0, Qt.AlignCenter) self.widget.setLayout(layout) self.widget.setStyleSheet(load_style_sheet()) self.setSizeHint(self.widget.sizeHint()) # Signals self.button_install.clicked.connect(self.install_application) self.button_launch.clicked.connect(self.launch_application) self.button_options.clicked.connect(self.actions_menu_requested) self.timer.timeout.connect(self._application_launched) # Setup self.update_status()
def setup_ui(self): self.resize(1080, 640) vlay_1 = QVBoxLayout(self) self.h_splitter = QSplitter(self) vlay_1.addWidget(self.h_splitter) self.h_splitter.setOrientation(Qt.Horizontal) self.v_splitter = QSplitter(self.h_splitter) self.v_splitter.setOrientation(Qt.Vertical) self.v_splitter.setMinimumWidth(500) installed = QWidget(self.v_splitter) lay = QVBoxLayout(installed) lay.setContentsMargins(0, 2, 0, 2) self.installed_label = QLabel(trans._("Installed Plugins")) self.packages_filter = QLineEdit() self.packages_filter.setPlaceholderText(trans._("filter...")) self.packages_filter.setMaximumWidth(350) self.packages_filter.setClearButtonEnabled(True) mid_layout = QVBoxLayout() mid_layout.addWidget(self.packages_filter) mid_layout.addWidget(self.installed_label) lay.addLayout(mid_layout) self.installed_list = QPluginList(installed, self.installer) self.packages_filter.textChanged.connect(self.installed_list.filter) lay.addWidget(self.installed_list) uninstalled = QWidget(self.v_splitter) lay = QVBoxLayout(uninstalled) lay.setContentsMargins(0, 2, 0, 2) self.avail_label = QLabel(trans._("Available Plugins")) mid_layout = QHBoxLayout() mid_layout.addWidget(self.avail_label) mid_layout.addStretch() lay.addLayout(mid_layout) self.available_list = QPluginList(uninstalled, self.installer) self.packages_filter.textChanged.connect(self.available_list.filter) lay.addWidget(self.available_list) self.stdout_text = QTextEdit(self.v_splitter) self.stdout_text.setReadOnly(True) self.stdout_text.setObjectName("pip_install_status") self.stdout_text.hide() buttonBox = QHBoxLayout() self.working_indicator = QLabel(trans._("loading ..."), self) sp = self.working_indicator.sizePolicy() sp.setRetainSizeWhenHidden(True) self.working_indicator.setSizePolicy(sp) self.process_error_indicator = QLabel(self) self.process_error_indicator.setObjectName("error_label") self.process_error_indicator.hide() load_gif = str(Path(napari.resources.__file__).parent / "loading.gif") mov = QMovie(load_gif) mov.setScaledSize(QSize(18, 18)) self.working_indicator.setMovie(mov) mov.start() visibility_direct_entry = not running_as_constructor_app() self.direct_entry_edit = QLineEdit(self) self.direct_entry_edit.installEventFilter(self) self.direct_entry_edit.setPlaceholderText( trans._('install by name/url, or drop file...')) self.direct_entry_edit.setVisible(visibility_direct_entry) self.direct_entry_btn = QPushButton(trans._("Install"), self) self.direct_entry_btn.setVisible(visibility_direct_entry) self.direct_entry_btn.clicked.connect(self._install_packages) self.show_status_btn = QPushButton(trans._("Show Status"), self) self.show_status_btn.setFixedWidth(100) self.cancel_all_btn = QPushButton(trans._("cancel all actions"), self) self.cancel_all_btn.setObjectName("remove_button") self.cancel_all_btn.setVisible(False) self.cancel_all_btn.clicked.connect(lambda: self.installer.cancel()) self.close_btn = QPushButton(trans._("Close"), self) self.close_btn.clicked.connect(self.accept) self.close_btn.setObjectName("close_button") buttonBox.addWidget(self.show_status_btn) buttonBox.addWidget(self.working_indicator) buttonBox.addWidget(self.direct_entry_edit) buttonBox.addWidget(self.direct_entry_btn) if not visibility_direct_entry: buttonBox.addStretch() buttonBox.addWidget(self.process_error_indicator) buttonBox.addSpacing(20) buttonBox.addWidget(self.cancel_all_btn) buttonBox.addSpacing(20) buttonBox.addWidget(self.close_btn) buttonBox.setContentsMargins(0, 0, 4, 0) vlay_1.addLayout(buttonBox) self.show_status_btn.setCheckable(True) self.show_status_btn.setChecked(False) self.show_status_btn.toggled.connect(self._toggle_status) self.v_splitter.setStretchFactor(1, 2) self.h_splitter.setStretchFactor(0, 2) self.packages_filter.setFocus()
class ListItemApplication(ListWidgetItemBase): """Item with custom widget for the applications list.""" ICON_SIZE = 64 def __init__( self, name=None, description=None, command=None, versions=None, image_path=None, prefix=None, needs_license=False, ): """Item with custom widget for the applications list.""" super(ListItemApplication, self).__init__() self.api = AnacondaAPI() self.prefix = prefix self.name = name self.url = '' self.expired = False self.needs_license = needs_license self.description = description self.command = command self.versions = versions self.image_path = image_path if image_path else ANACONDA_ICON_256_PATH self.style_sheet = None self.timeout = 2000 # Widgets self.button_install = ButtonApplicationInstall("Install") # or Try! self.button_launch = ButtonApplicationLaunch("Launch") self.button_options = ButtonApplicationOptions() self.label_license = LabelApplicationLicense('') self.button_license = ButtonApplicationLicense('') self.label_icon = LabelApplicationIcon() self.label_name = LabelApplicationName(self.name) self.label_description = LabelApplicationDescription(self.description) self.button_version = ButtonApplicationVersion( to_text_string(self.version)) self.label_spinner = LabelApplicationSpinner() self.menu_options = QMenu('Application options') self.menu_versions = QMenu('Install specific version') self.movie_spinner = QMovie(SPINNER_WHITE_16_PATH) self.pixmap = QPixmap(self.image_path) self.timer = QTimer() self.widget = WidgetApplication() # Widget setup self.button_version.setFocusPolicy(Qt.NoFocus) self.button_version.setEnabled(True) self.label_description.setAlignment(Qt.AlignCenter) self.movie_spinner.start() self.timer.setInterval(self.timeout) self.timer.setSingleShot(True) self.label_icon.setPixmap(self.pixmap) self.label_icon.setScaledContents(True) # important on High DPI! self.label_icon.setMaximumWidth(self.ICON_SIZE) self.label_icon.setMaximumHeight(self.ICON_SIZE) self.label_icon.setAlignment(Qt.AlignCenter) self.label_name.setAlignment(Qt.AlignCenter) self.label_name.setWordWrap(True) self.label_description.setWordWrap(True) self.label_description.setAlignment(Qt.AlignTop | Qt.AlignHCenter) self.label_spinner.setVisible(False) # Layouts layout_spinner = QHBoxLayout() layout_spinner.addWidget(self.button_version, 0, Qt.AlignCenter) layout_spinner.addWidget(self.label_spinner, 0, Qt.AlignCenter) layout_license = QHBoxLayout() layout_license.addStretch() layout_license.addWidget(self.label_license, 0, Qt.AlignCenter) layout_license.addWidget(self.button_license, 0, Qt.AlignCenter) layout_license.addStretch() layout_main = QVBoxLayout() layout_main.addWidget(self.button_options, 0, Qt.AlignRight) layout_main.addWidget(self.label_icon, 0, Qt.AlignCenter) layout_main.addWidget(self.label_name, 0, Qt.AlignCenter) layout_main.addLayout(layout_spinner) layout_main.addLayout(layout_license) layout_main.addWidget(self.label_description, 0, Qt.AlignCenter) layout_main.addWidget(self.button_launch, 0, Qt.AlignCenter) layout_main.addWidget(self.button_install, 0, Qt.AlignCenter) self.widget.setLayout(layout_main) self.widget.setStyleSheet(load_style_sheet()) self.setSizeHint(self.widget_size()) # This might help with visual quirks on the home screen self.widget.setMinimumSize(self.widget_size()) # Signals self.button_install.clicked.connect(self.install_application) self.button_launch.clicked.connect(self.launch_application) self.button_options.clicked.connect(self.actions_menu_requested) self.button_license.clicked.connect(self.launch_url) self.timer.timeout.connect(self._application_launched) # Setup self.update_status() # --- Callbacks # ------------------------------------------------------------------------- def _application_launched(self): self.button_launch.setDisabled(False) update_pointer() # --- Helpers # ------------------------------------------------------------------------- def update_style_sheet(self, style_sheet=None): """Update custom CSS stylesheet.""" if style_sheet: self.style_sheet = style_sheet else: self.style_sheet = load_style_sheet() self.menu_options.setStyleSheet(self.style_sheet) self.menu_versions.setStyleSheet(self.style_sheet) def ordered_widgets(self): """Return a list of the ordered widgets.""" return [ self.button_license, self.button_install, self.button_launch, self.button_options ] @staticmethod def widget_size(): """Return the size defined in the SASS file.""" return QSize(SASS_VARIABLES.WIDGET_APPLICATION_TOTAL_WIDTH, SASS_VARIABLES.WIDGET_APPLICATION_TOTAL_HEIGHT) def launch_url(self): """Launch signal for url click.""" self.widget.sig_url_clicked.emit(self.url) def actions_menu_requested(self): """Create and display menu for the currently selected application.""" self.menu_options.clear() self.menu_versions.clear() # Add versions menu versions = self.versions if self.versions else [] version_actions = [] for version in reversed(versions): action = create_action(self.widget, version, triggered=lambda value, version=version: self.install_application(version=version)) action.setCheckable(True) if self.version == version and self.installed: action.setChecked(True) action.setDisabled(True) version_actions.append(action) install_action = create_action( self.widget, 'Install application', triggered=lambda: self.install_application()) install_action.setEnabled(not self.installed) update_action = create_action( self.widget, 'Update application', triggered=lambda: self.update_application()) if versions and versions[-1] == self.version: update_action.setDisabled(True) else: update_action.setDisabled(False) remove_action = create_action( self.widget, 'Remove application', triggered=lambda: self.remove_application()) remove_action.setEnabled(self.installed) actions = [ install_action, update_action, remove_action, None, self.menu_versions ] add_actions(self.menu_options, actions) add_actions(self.menu_versions, version_actions) offset = QPoint(self.button_options.width(), 0) position = self.button_options.mapToGlobal(QPoint(0, 0)) self.menu_versions.setEnabled(len(versions) > 1) self.menu_options.move(position + offset) self.menu_options.exec_() def update_status(self): """Update status.""" # License check license_label_text = '' license_url_text = '' self.url = '' self.expired = False button_label = 'Install' if self.needs_license: # TODO: Fix this method to use the api license_info = self.api.get_package_license(self.name) license_days = self.api.get_days_left(license_info) end_date = license_info.get('end_date', '') self.expired = license_days == 0 plural = 's' if license_days != 1 else '' is_trial = license_info.get('type', '').lower() == 'trial' if self.installed and license_info: if is_trial and not self.expired: license_label_text = ('Trial, {days} day{plural} ' 'remaining'.format(days=license_days, plural=plural)) self.url = '' elif is_trial and self.expired: license_label_text = 'Trial expired, ' license_url_text = 'contact us' self.url = 'mailto:[email protected]' elif not is_trial and not self.expired: license_label_text = 'License expires {}'.format(end_date) self.url = '' elif not is_trial and self.expired: license_url_text = 'Renew license' self.url = 'mailto:[email protected]' elif self.installed and not bool(license_info): # Installed but no license found! license_url_text = 'No license found' self.url = 'mailto:[email protected]' else: if not self.expired: button_label = 'Install' else: button_label = 'Try' self.button_license.setText(license_url_text) self.button_license.setVisible(bool(self.url)) self.label_license.setText(license_label_text) self.label_license.setVisible(bool(license_label_text)) # Version and version updates if (self.versions and self.version != self.versions[-1] and self.installed): # The property is used with CSS to display updatable packages. self.button_version.setProperty('pressed', True) self.button_version.setToolTip('Version {0} available'.format( self.versions[-1])) else: self.button_version.setProperty('pressed', False) if not self.needs_license: self.button_install.setText(button_label) self.button_install.setVisible(not self.installed) self.button_launch.setVisible(self.installed) else: self.button_install.setText('Try' if self.expired else 'Install') self.button_launch.setVisible(not self.expired) self.button_install.setVisible(self.expired) self.button_launch.setEnabled(True) def update_versions(self, version=None, versions=None): """Update button visibility depending on update availability.""" logger.debug(str((self.name, self.dev_tool, self.installed))) if self.installed and version: self.button_options.setVisible(True) self.button_version.setText(version) self.button_version.setVisible(True) elif not self.installed and versions: self.button_install.setEnabled(True) self.button_version.setText(versions[-1]) self.button_version.setVisible(True) self.versions = versions self.version = version self.update_status() def set_loading(self, value): """Set loading status.""" self.button_install.setDisabled(value) self.button_options.setDisabled(value) self.button_launch.setDisabled(value) self.button_license.setDisabled(value) if value: self.label_spinner.setMovie(self.movie_spinner) else: self.label_spinner.setMovie(None) if self.version is None and self.versions is not None: version = self.versions[-1] else: version = self.version self.button_version.setText(version) self.button_launch.setDisabled(self.expired) self.label_spinner.setVisible(value) self.button_version.setVisible(not value) # --- Helpers using api # ------------------------------------------------------------------------- @property def installed(self): """Return the installed status of the package.""" version = None if self.prefix: version = self.api.conda_package_version(prefix=self.prefix, pkg=self.name, build=False) return bool(version) @property def version(self): """Return the current installed version or the highest version.""" version = None if self.prefix: version = self.api.conda_package_version(prefix=self.prefix, pkg=self.name, build=False) if not version: version = self.versions[-1] return version # --- Application actions # ------------------------------------------------------------------------ def install_application(self, value=None, version=None, install=True): """ Update the application on the defined prefix environment. This is used for both normal install and specific version install. """ if not version: version = self.versions[-1] action = C.APPLICATION_INSTALL if install else C.APPLICATION_UPDATE self.widget.sig_conda_action_requested.emit( action, self.name, version, C.TAB_HOME, ) self.set_loading(True) def remove_application(self): """Remove the application from the defined prefix environment.""" self.widget.sig_conda_action_requested.emit( C.APPLICATION_REMOVE, self.name, None, C.TAB_HOME, ) self.set_loading(True) def update_application(self): """Update the application on the defined prefix environment.""" self.install_application(version=self.versions[-1], install=False) def launch_application(self): """Launch application installed in prefix environment.""" leave_path_alone = False if self.command is not None: # Applications on Windows often live in 'bin' folders. By using # ${PREFIX} command we indicate that we do not want launch() to # mess with the path and will instead use selectors in recipes. if '${PREFIX}' in self.command: command = self.command.replace("${PREFIX}", self.prefix) leave_path_alone = True elif self.prefix: command = os.sep.join([self.prefix, 'bin', self.command]) else: command = self.command self.button_launch.setDisabled(True) self.timer.setInterval(self.timeout) self.timer.start() update_pointer(Qt.BusyCursor) self.widget.sig_launch_action_requested.emit( self.name, command, leave_path_alone, self.prefix, C.TAB_HOME, )
def setup_ui(self): self.resize(1080, 640) vlay_1 = QVBoxLayout(self) self.h_splitter = QSplitter(self) vlay_1.addWidget(self.h_splitter) self.h_splitter.setOrientation(Qt.Horizontal) self.v_splitter = QSplitter(self.h_splitter) self.v_splitter.setOrientation(Qt.Vertical) self.v_splitter.setMinimumWidth(500) self.plugin_sorter = QtPluginSorter(parent=self.h_splitter) self.plugin_sorter.layout().setContentsMargins(2, 0, 0, 0) self.plugin_sorter.hide() installed = QWidget(self.v_splitter) lay = QVBoxLayout(installed) lay.setContentsMargins(0, 2, 0, 2) lay.addWidget(QLabel(trans._("Installed Plugins"))) self.installed_list = QPluginList(installed, self.installer) lay.addWidget(self.installed_list) uninstalled = QWidget(self.v_splitter) lay = QVBoxLayout(uninstalled) lay.setContentsMargins(0, 2, 0, 2) self.avail_label = QLabel(trans._("Available Plugins")) lay.addWidget(self.avail_label) self.available_list = QPluginList(uninstalled, self.installer) lay.addWidget(self.available_list) self.stdout_text = QTextEdit(self.v_splitter) self.stdout_text.setReadOnly(True) self.stdout_text.setObjectName("pip_install_status") self.stdout_text.hide() buttonBox = QHBoxLayout() self.working_indicator = QLabel(trans._("loading ..."), self) sp = self.working_indicator.sizePolicy() sp.setRetainSizeWhenHidden(True) self.working_indicator.setSizePolicy(sp) self.process_error_indicator = QLabel(self) self.process_error_indicator.setObjectName("error_label") self.process_error_indicator.hide() load_gif = str(Path(napari.resources.__file__).parent / "loading.gif") mov = QMovie(load_gif) mov.setScaledSize(QSize(18, 18)) self.working_indicator.setMovie(mov) mov.start() self.direct_entry_edit = QLineEdit(self) self.direct_entry_edit.installEventFilter(self) self.direct_entry_edit.setPlaceholderText( trans._('install by name/url, or drop file...')) self.direct_entry_btn = QPushButton(trans._("Install"), self) self.direct_entry_btn.clicked.connect(self._install_packages) self.show_status_btn = QPushButton(trans._("Show Status"), self) self.show_status_btn.setFixedWidth(100) self.show_sorter_btn = QPushButton(trans._("<< Show Sorter"), self) self.close_btn = QPushButton(trans._("Close"), self) self.close_btn.clicked.connect(self.reject) buttonBox.addWidget(self.show_status_btn) buttonBox.addWidget(self.working_indicator) buttonBox.addWidget(self.direct_entry_edit) buttonBox.addWidget(self.direct_entry_btn) buttonBox.addWidget(self.process_error_indicator) buttonBox.addSpacing(60) buttonBox.addWidget(self.show_sorter_btn) buttonBox.addWidget(self.close_btn) buttonBox.setContentsMargins(0, 0, 4, 0) vlay_1.addLayout(buttonBox) self.show_status_btn.setCheckable(True) self.show_status_btn.setChecked(False) self.show_status_btn.toggled.connect(self._toggle_status) self.show_sorter_btn.setCheckable(True) self.show_sorter_btn.setChecked(False) self.show_sorter_btn.toggled.connect(self._toggle_sorter) self.v_splitter.setStretchFactor(1, 2) self.h_splitter.setStretchFactor(0, 2)
class ListItemEnvironment(QListWidgetItem): """ Widget to build an item for the environments list. """ def __init__(self, name=None, prefix=None): super(ListItemEnvironment, self).__init__() self._selected = False self._name = name self._prefix = prefix # Widgets self.button_options = ButtonEnvironmentOptions() self.button_name = ButtonEnvironmentName(name) self.label_icon = LabelEnvironmentIcon() self.movie = QMovie(SPINNER_GREEN_16_PATH) self.widget = WidgetEnvironment() self.widget.setFocusPolicy(Qt.NoFocus) self.widget.button_name = self.button_name self.widget.button_options = self.button_options # Widget setup self.label_icon.setMovie(self.movie) self.button_name.setDefault(True) # Layouts layout = QHBoxLayout() layout.addWidget(self.label_icon) layout.addWidget(self.button_name) layout.addWidget(self.button_options) layout.addStretch() self.widget.setLayout(layout) self.widget.setStyleSheet(load_style_sheet()) self.setSizeHint(self.widget.sizeHint()) def text(self): return self._name def prefix(self): return self._prefix def set_hovered(self, value): self.widget.setProperty('_hovered', value) self.button_name.set_hovered(value) def set_loading(self, value): # self.button_options.setDisabled(value) if value: self.label_icon.setMovie(self.movie) self.movie.start() else: self.label_icon.setMovie(QMovie()) self.button_options.setFocus() def set_selected(self, value): self._selected = value try: self.widget.setProperty('_selected', value) self.button_name.set_selected(value) except RuntimeError: pass self.button_name.setDisabled(value) if value: self.label_icon.setMovie(None) self.button_name.setFocusPolicy(Qt.NoFocus) self.button_options.setVisible(True) self.button_options.setFocus() else: self.label_icon.setMovie(None) self.button_name.setFocusPolicy(Qt.TabFocus) self.button_options.setVisible(False)
def __init__( self, name=None, description=None, command=None, versions=None, image_path=None, prefix=None, needs_license=False, ): """Item with custom widget for the applications list.""" super(ListItemApplication, self).__init__() self.api = AnacondaAPI() self.prefix = prefix self.name = name self.url = '' self.expired = False self.needs_license = needs_license self.description = description self.command = command self.versions = versions self.image_path = image_path if image_path else ANACONDA_ICON_256_PATH self.style_sheet = None self.timeout = 2000 # Widgets self.button_install = ButtonApplicationInstall("Install") # or Try! self.button_launch = ButtonApplicationLaunch("Launch") self.button_options = ButtonApplicationOptions() self.label_license = LabelApplicationLicense('') self.button_license = ButtonApplicationLicense('') self.label_icon = LabelApplicationIcon() self.label_name = LabelApplicationName(self.name) self.label_description = LabelApplicationDescription(self.description) self.button_version = ButtonApplicationVersion( to_text_string(self.version)) self.label_spinner = LabelApplicationSpinner() self.menu_options = QMenu('Application options') self.menu_versions = QMenu('Install specific version') self.movie_spinner = QMovie(SPINNER_WHITE_16_PATH) self.pixmap = QPixmap(self.image_path) self.timer = QTimer() self.widget = WidgetApplication() # Widget setup self.button_version.setFocusPolicy(Qt.NoFocus) self.button_version.setEnabled(True) self.label_description.setAlignment(Qt.AlignCenter) self.movie_spinner.start() self.timer.setInterval(self.timeout) self.timer.setSingleShot(True) self.label_icon.setPixmap(self.pixmap) self.label_icon.setScaledContents(True) # important on High DPI! self.label_icon.setMaximumWidth(self.ICON_SIZE) self.label_icon.setMaximumHeight(self.ICON_SIZE) self.label_icon.setAlignment(Qt.AlignCenter) self.label_name.setAlignment(Qt.AlignCenter) self.label_name.setWordWrap(True) self.label_description.setWordWrap(True) self.label_description.setAlignment(Qt.AlignTop | Qt.AlignHCenter) self.label_spinner.setVisible(False) # Layouts layout_spinner = QHBoxLayout() layout_spinner.addWidget(self.button_version, 0, Qt.AlignCenter) layout_spinner.addWidget(self.label_spinner, 0, Qt.AlignCenter) layout_license = QHBoxLayout() layout_license.addStretch() layout_license.addWidget(self.label_license, 0, Qt.AlignCenter) layout_license.addWidget(self.button_license, 0, Qt.AlignCenter) layout_license.addStretch() layout_main = QVBoxLayout() layout_main.addWidget(self.button_options, 0, Qt.AlignRight) layout_main.addWidget(self.label_icon, 0, Qt.AlignCenter) layout_main.addWidget(self.label_name, 0, Qt.AlignCenter) layout_main.addLayout(layout_spinner) layout_main.addLayout(layout_license) layout_main.addWidget(self.label_description, 0, Qt.AlignCenter) layout_main.addWidget(self.button_launch, 0, Qt.AlignCenter) layout_main.addWidget(self.button_install, 0, Qt.AlignCenter) self.widget.setLayout(layout_main) self.widget.setStyleSheet(load_style_sheet()) self.setSizeHint(self.widget_size()) # This might help with visual quirks on the home screen self.widget.setMinimumSize(self.widget_size()) # Signals self.button_install.clicked.connect(self.install_application) self.button_launch.clicked.connect(self.launch_application) self.button_options.clicked.connect(self.actions_menu_requested) self.button_license.clicked.connect(self.launch_url) self.timer.timeout.connect(self._application_launched) # Setup self.update_status()
class PyDMDrawingImage(PyDMDrawing): """ Renders an image given by the ``filename`` property. This class inherits from PyDMDrawing. Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. Attributes ---------- null_color : Qt.Color QColor to fill the image if the filename is not found. """ null_color = Qt.gray def __init__(self, parent=None, init_channel=None, filename=""): super(PyDMDrawingImage, self).__init__(parent, init_channel) hint = super(PyDMDrawingImage, self).sizeHint() self._pixmap = QPixmap(hint) self._pixmap.fill(self.null_color) self._aspect_ratio_mode = Qt.KeepAspectRatio self._movie = None # Make sure we don't set a non-existant file if filename: self.filename = filename # But we always have an internal value to reference else: self._file = filename if is_qt_designer(): # pragma: no cover designer_window = self.get_designer_window() if designer_window is not None: designer_window.fileNameChanged.connect( self.designer_form_saved) def get_designer_window(self): # pragma: no cover # Internal function to find the designer window that owns this widget. p = self.parent() while p is not None: if isinstance(p, QDesignerFormWindowInterface): return p p = p.parent() return None @Slot(str) def designer_form_saved(self, filename): # pragma: no cover self.filename = self._file @Property(str) def filename(self): """ The filename of the image to be displayed. This can be an absolute or relative path to the display file. Returns ------- str The filename configured. """ return self._file @filename.setter def filename(self, new_file): """ The filename of the image to be displayed. This file can be either relative to the ``.ui`` file or absolute. If the path does not exist, a shape of ``.null_color`` will be displayed instead. Parameters ------- new_file : str The filename to be used """ # Expand user (~ or ~user) and environment variables. pixmap = None self._file = new_file abs_path = os.path.expanduser(os.path.expandvars(self._file)) # Find the absolute path relative to UI if not os.path.isabs(abs_path): try: # Based on the QApplication if is_pydm_app(): abs_path = QApplication.instance().get_path(abs_path) # Based on the QtDesigner elif is_qt_designer(): # pragma: no cover p = self.get_designer_window() if p is not None: ui_dir = p.absoluteDir().absolutePath() abs_path = os.path.join(ui_dir, abs_path) except Exception: logger.exception("Unable to find full filepath for %s", self._file) # Check that the path exists if os.path.isfile(abs_path): if self._movie is not None: self._movie.stop() self._movie.deleteLater() self._movie = None if not abs_path.endswith(".gif"): pixmap = QPixmap(abs_path) else: self._movie = QMovie(abs_path, parent=self) self._movie.setCacheMode(QMovie.CacheAll) self._movie.frameChanged.connect(self.movie_frame_changed) if self._movie.frameCount() > 1: self._movie.finished.connect(self.movie_finished) self._movie.start() # Return a blank image if we don't have a valid path else: # Warn the user loudly if their file does not exist, but avoid # doing this in Designer as this spams the user as they are typing if not is_qt_designer(): # pragma: no cover logger.error("Image file %r does not exist", abs_path) pixmap = QPixmap(self.sizeHint()) pixmap.fill(self.null_color) # Update the display if pixmap is not None: self._pixmap = pixmap self.update() def sizeHint(self): if self._pixmap.size().isEmpty(): return super(PyDMDrawingImage, self).sizeHint() return self._pixmap.size() @Property(Qt.AspectRatioMode) def aspectRatioMode(self): """ PyQT Property for aspect ratio mode to be used when rendering the image Returns ------- int Index at Qt.AspectRatioMode enum """ return self._aspect_ratio_mode @aspectRatioMode.setter def aspectRatioMode(self, new_mode): """ PyQT Property for aspect ratio mode to be used when rendering the image Parameters ---------- new_mode : int Index at Qt.AspectRatioMode enum """ if new_mode != self._aspect_ratio_mode: self._aspect_ratio_mode = new_mode self.update() def draw_item(self, painter): """ Draws the image after setting up the canvas with a call to ```PyDMDrawing.draw_item```. """ super(PyDMDrawingImage, self).draw_item(painter) x, y, w, h = self.get_bounds(maxsize=True, force_no_pen=True) if not isinstance(self._pixmap, QMovie): _scaled = self._pixmap.scaled(w, h, self._aspect_ratio_mode, Qt.SmoothTransformation) # Make sure the image is centered if smaller than the widget itself if w > _scaled.width(): logger.debug("Centering image horizontally ...") x += (w - _scaled.width()) / 2 if h > _scaled.height(): logger.debug("Centering image vertically ...") y += (h - _scaled.height()) / 2 painter.drawPixmap(x, y, _scaled) def movie_frame_changed(self, frame_no): """ Callback executed when a new frame is available at the QMovie. Parameters ---------- frame_no : int The new frame index Returns ------- None """ if self._movie is None: return curr_pixmap = self._movie.currentPixmap() self._pixmap = curr_pixmap self.update() def movie_finished(self): """ Callback executed when the movie is finished. Returns ------- None """ if self._movie is None: return self._movie.start()
class IDControl(SiriusMainWindow): """ID Control Window.""" def __init__(self, parent=None, prefix=_vaca_prefix): super().__init__(parent) self._prefix = prefix self.setObjectName('IDApp') self.setWindowTitle('ID Controls') self.setWindowIcon(get_id_icon()) self._setupUi() self._create_actions() def _setupUi(self): cw = QWidget() self.setCentralWidget(cw) label = QLabel('<h3>ID Control Window</h3>', self, alignment=Qt.AlignCenter) label.setStyleSheet('QLabel{min-height: 3em; max-height: 3em;}') self.label_mov1 = QLabel(self) self.label_mov1.setVisible(False) self.label_mov1.setStyleSheet( 'QLabel{min-height: 3em; max-height: 3em;}') self.label_mov2 = QLabel(self) self.label_mov2.setVisible(False) self.label_mov2.setStyleSheet( 'QLabel{min-height: 3em; max-height: 3em;}') self.movie_mov = QMovie(_os.path.join( _os.path.abspath(_os.path.dirname(__file__)), 'hula.gif')) self.movie_mov.setScaledSize(QSize(50, 50)) self.label_mov1.setMovie(self.movie_mov) self.label_mov2.setMovie(self.movie_mov) self._gbox_apu = QGroupBox('APU', self) self._gbox_apu.setLayout(self._setupAPULayout()) lay = QGridLayout(cw) lay.addWidget(self.label_mov1, 0, 0) lay.addWidget(label, 0, 1) lay.addWidget(self.label_mov2, 0, 2) lay.addWidget(self._gbox_apu, 1, 0, 1, 3) lay.setColumnStretch(0, 1) lay.setColumnStretch(1, 15) lay.setColumnStretch(2, 1) def _setupAPULayout(self): lay = QVBoxLayout() lay.setAlignment(Qt.AlignTop) self._apu_header = APUSummaryHeader(self) lay.addWidget(self._apu_header) self._apu_widgets = list() self._channels_mov = list() idlist = ['SI-06SB:ID-APU22', 'SI-07SP:ID-APU22', 'SI-08SB:ID-APU22', 'SI-09SA:ID-APU22', 'SI-11SP:ID-APU58'] for idname in idlist: apu_wid = APUSummaryWidget(self, self._prefix, idname) lay.addWidget(apu_wid) self._apu_widgets.append(apu_wid) ch_mov = SiriusConnectionSignal(_PVName(idname).substitute( prefix=self._prefix, propty='Moving-Mon')) ch_mov.new_value_signal[float].connect(self._handle_moving_vis) self._channels_mov.append(ch_mov) return lay def _create_actions(self): self.blctrl_enbl_act = QAction("Enable Beamline Control", self) self.blctrl_enbl_act.triggered.connect( lambda: self._set_beamline_control(True)) self.blctrl_dsbl_act = QAction("Disable Beamline Control", self) self.blctrl_dsbl_act.triggered.connect( lambda: self._set_beamline_control(False)) @Slot(bool) def _set_beamline_control(self, state): """Execute enable/disable beamline control actions.""" for widget in self._apu_widgets: try: if state: widget.enable_beamline_control() else: widget.disable_beamline_control() except TypeError: pass def contextMenuEvent(self, event): """Show a custom context menu.""" point = event.pos() menu = QMenu("Actions", self) menu.addAction(self.blctrl_enbl_act) menu.addAction(self.blctrl_dsbl_act) menu.addSeparator() action = menu.addAction('Show Connections...') action.triggered.connect(self.show_connections) menu.popup(self.mapToGlobal(point)) def show_connections(self, checked): """Show connections.""" _ = checked c = ConnectionInspector(self) c.show() def _handle_moving_vis(self, value): """Handle visualization of moving state label.""" show = any([ ch.connected and ch.value != 0 for ch in self._channels_mov]) self.label_mov1.setVisible(show) self.label_mov2.setVisible(show) if show: self.movie_mov.start() else: self.movie_mov.stop()
def resourceMovie(name): """@rtype: QMovie""" movie = QMovie(resource_filename("ert_gui", "resources/gui/img/" + name)) movie.start() return movie
class ListItemEnv(ListWidgetItemBase): """Widget to build an item for the environments list.""" def __init__(self, name=None, prefix=None): """Widget to build an item for the environments list.""" super(ListItemEnv, self).__init__() self._selected = False self._name = name self._prefix = prefix # Widgets self.button_options = ButtonEnvironmentOptions() self.button_name = ButtonEnvironmentName(name) self.label_icon = LabelEnvironmentIcon() self.movie = QMovie(SPINNER_GREEN_16_PATH) self.widget = WidgetEnvironment() self.widget.button_name = self.button_name self.widget.button_options = self.button_options # Widget setup self.button_name.setDefault(True) self.label_icon.setMovie(self.movie) self.widget.setFocusPolicy(Qt.NoFocus) self.button_name.setFocusPolicy(Qt.StrongFocus) self.button_options.setFocusPolicy(Qt.StrongFocus) self.button_name.setToolTip(prefix if prefix else '') # Layouts layout = QHBoxLayout() layout.addWidget(self.label_icon) layout.addWidget(self.button_name) layout.addWidget(self.button_options) layout.addStretch() self.widget.setLayout(layout) self.widget.setStyleSheet(load_style_sheet()) self.setSizeHint(self.widget.sizeHint()) def ordered_widgets(self): """Return a list of the ordered widgets.""" return [self.button_name, self.button_options] @property def name(self): """Resturn the environment name.""" return self._name @property def prefix(self): """Return the environment prefix.""" return self._prefix def set_hovered(self, value): """Set widget as hovered.""" self.widget.setProperty('hovered', value) self.button_name.set_hovered(value) def set_loading(self, value): """Set loading status of widget.""" # self.button_options.setDisabled(value) if value: self.label_icon.setMovie(self.movie) self.movie.start() else: self.label_icon.setMovie(QMovie()) self.button_options.setFocus() def set_selected(self, value): """Set widget as selected.""" self._selected = value try: self.widget.setProperty('pressed', value) self.button_name.set_selected(value) except RuntimeError: pass self.button_name.setDisabled(value) if value: self.label_icon.setMovie(None) self.button_options.setVisible(True) else: self.label_icon.setMovie(None) self.button_options.setVisible(False) @staticmethod def widget_size(): """Return the size defined in the SASS file.""" return QSize(SASS_VARIABLES.WIDGET_ENVIRONMENT_TOTAL_WIDTH, SASS_VARIABLES.WIDGET_ENVIRONMENT_TOTAL_HEIGHT)
class XicamSplashScreen(QSplashScreen): minsplashtime = 3000 def __init__(self, mainwindow: Callable[[], QMainWindow] = None, f: int = Qt.WindowStaysOnTopHint | Qt.SplashScreen): """ A QSplashScreen customized to display an animated gif. The splash triggers launch when clicked. After minsplashtime, this splash waits until the animation finishes before triggering the launch. Parameters ---------- mainwindow : class Subclass of QMainWindow to display after splashing f : int Extra flags (see base class) """ # Get logo movie from relative path self.movie = QMovie(str(static.path("images/animated_logo.gif"))) # Setup drawing self.movie.frameChanged.connect(self.paintFrame) self.movie.jumpToFrame(1) self.pixmap = QPixmap(self.movie.frameRect().size()) super(XicamSplashScreen, self).__init__(self.pixmap, f) self.setMask(self.pixmap.mask()) self.movie.finished.connect(self.restartmovie) self._launching = False self._launchready = False self.timer = QTimer(self) self.mainwindow = mainwindow if args.nosplash: self.execlaunch() else: # Start splashing self.setAttribute(Qt.WA_DeleteOnClose) self.show() self.raise_() self.activateWindow() QApplication.instance().setActiveWindow(self) # Setup timed triggers for launching the QMainWindow self.timer.singleShot(self.minsplashtime, self.launchwindow) def showMessage(self, message: str, color=Qt.white): # TODO: Make this work. super(XicamSplashScreen, self).showMessage(message, color) def mousePressEvent(self, *args, **kwargs): # TODO: Apparently this doesn't work? self.timer.stop() self.execlaunch() def show(self): """ Start the animation when shown """ super(XicamSplashScreen, self).show() self.movie.start() def paintFrame(self): """ Paint the current frame """ self.pixmap = self.movie.currentPixmap() self.setMask(self.pixmap.mask()) self.setPixmap(self.pixmap) self.movie.setSpeed(self.movie.speed() + 20) def sizeHint(self): return self.movie.scaledSize() def restartmovie(self): """ Once the animation reaches the end, check if its time to launch, otherwise restart animation """ if self._launchready: self.execlaunch() return self.movie.start() def launchwindow(self): """ Save state, defer launch until animation finishes """ self._launchready = True def execlaunch(self): """ Launch the mainwindow """ if not self._launching: self._launching = True app = QApplication.instance() from xicam.gui.windows.mainwindow import XicamMainWindow self.mainwindow = XicamMainWindow() self.timer.stop() # Show the QMainWindow self.mainwindow.show() self.mainwindow.raise_() self.mainwindow.activateWindow() app.setActiveWindow(self.mainwindow) # Stop splashing self.hide() self.movie.stop() self.finish(self.mainwindow)
class XicamSplashScreen(QSplashScreen): minsplashtime = 5000 def __init__(self, log_path: str, initial_length: int, f: int = Qt.WindowStaysOnTopHint | Qt.SplashScreen): """ A QSplashScreen customized to display an animated gif. The splash triggers launch when clicked. After minsplashtime, this splash waits until the animation finishes before triggering the launch. Parameters ---------- log_path : str Path to the Xi-CAM log file to reflect initial_length: int Length in bytes to seek forward before reading f : int Extra flags (see base class) """ # Get logo movie from relative path self.movie = QMovie(str(static.path("images/animated_logo.gif"))) # Setup drawing self.movie.frameChanged.connect(self.paintFrame) self.movie.jumpToFrame(1) self.pixmap = QPixmap(self.movie.frameRect().size()) super(XicamSplashScreen, self).__init__(self.pixmap, f) self.setMask(self.pixmap.mask()) self.movie.finished.connect(self.restartmovie) self.showMessage("Starting Xi-CAM...") self._launching = False self._launchready = False self.timer = QTimer(self) self.log_file = open(log_path, "r") self.log_file.seek(initial_length) # Start splashing self.setAttribute(Qt.WA_DeleteOnClose) self.show() self.raise_() self.activateWindow() QApplication.instance().setActiveWindow(self) # Setup timed triggers for launching the QMainWindow self.timer.singleShot(self.minsplashtime, self.launchwindow) def showMessage(self, message: str, color=Qt.darkGray): # attempt to parse out everyting besides the message try: message=message.split(" - ")[-1] except Exception: pass else: super(XicamSplashScreen, self).showMessage(elide(message), color=color, alignment=Qt.AlignBottom) def mousePressEvent(self, *args, **kwargs): # TODO: Apparently this doesn't work? self.timer.stop() self.execlaunch() def show(self): """ Start the animation when shown """ super(XicamSplashScreen, self).show() self.movie.start() def paintFrame(self): """ Paint the current frame """ self.pixmap = self.movie.currentPixmap() self.setMask(self.pixmap.mask()) self.setPixmap(self.pixmap) self.movie.setSpeed(self.movie.speed() + 20) line = self.log_file.readline().strip() if line: self.showMessage(elide(line.split(">")[-1])) def sizeHint(self): return self.movie.scaledSize() def restartmovie(self): """ Once the animation reaches the end, check if its time to launch, otherwise restart animation """ if self._launchready: self.execlaunch() return self.movie.start() def launchwindow(self): """ Save state, defer launch until animation finishes """ self._launchready = True def execlaunch(self): """ Launch the mainwindow """ if not self._launching: self._launching = True self.timer.stop() # Stop splashing self.hide() self.movie.stop() self.close() QApplication.instance().quit()
class ListItemApplication(QListWidgetItem): """ Item with custom widget for the applications list. """ ICON_SIZE = 48 def __init__(self, name=None, description=None, command=None, pixmap=None, version=None, versions=None, path=None, dev_tool=True, prefix=None, is_conda_app=False, packages_widget=None): super(ListItemApplication, self).__init__() self.api = AnacondaAPI() self.command = command self.dev_tool = dev_tool self.installed = False self.is_conda_app = is_conda_app self.name = name self.path = path self.pixmap = pixmap if pixmap else QPixmap(ANACONDA_ICON_64_PATH) self.prefix = prefix self.timeout = 10000 # In miliseconds self.version = version self.versions = versions self.packages_widget = packages_widget # Widgets self.button_install = ButtonApplicationInstall("Install") self.button_launch = ButtonApplicationLaunch("Launch") self.button_options = ButtonApplicationOptions() self.label_icon = LabelApplicationIcon() self.label_name = LabelApplicationName(name) self.label_description = LabelApplicationDescription(description) # self.label_update = LabelApplicationUpdate() self.button_version = ButtonApplicationVersion(to_text_string(version)) self.label_spinner = LabelApplicationSpinner() # self.label_version = LabelApplicationVersion(to_text_string(version)) self.menu_options = QMenu('Application options') self.menu_versions = QMenu('Install specific version') self.movie_spinner = QMovie(SPINNER_WHITE_16_PATH) self.timer = QTimer() self.widget = WidgetApplication() # Widget setup self.button_version.setFocusPolicy(Qt.NoFocus) self.label_name.setToolTip(description) self.label_description.setAlignment(Qt.AlignCenter) self.movie_spinner.start() self.timer.setInterval(self.timeout) self.timer.setSingleShot(True) self.label_icon.setToolTip(description) self.label_icon.setPixmap( self.pixmap.scaled(self.ICON_SIZE, self.ICON_SIZE, Qt.KeepAspectRatio, Qt.SmoothTransformation)) self.label_icon.setAlignment(Qt.AlignCenter) self.label_name.setAlignment(Qt.AlignCenter) self.label_name.setWordWrap(True) self.label_description.setWordWrap(True) self.label_description.setAlignment(Qt.AlignTop | Qt.AlignHCenter) self.label_spinner.setVisible(False) self.label_spinner.setMinimumWidth(16) self.label_spinner.setMinimumHeight(16) # Layouts layout = QVBoxLayout() layout.addWidget(self.button_options, 0, Qt.AlignRight) layout.addWidget(self.label_icon, 0, Qt.AlignCenter) layout.addWidget(self.label_name, 0, Qt.AlignCenter) layout.addWidget(self.label_description, 0, Qt.AlignCenter) # hlayout = QHBoxLayout() # hlayout.addWidget(self.label_update) # hlayout.addWidget(self.label_version) # layout.addLayout(hlayout) # layout.addWidget(self.label_version, 0, Qt.AlignCenter) layout.addWidget(self.button_version, 0, Qt.AlignCenter) layout.addWidget(self.label_spinner, 0, Qt.AlignCenter) layout.addWidget(self.button_launch, 0, Qt.AlignCenter) layout.addWidget(self.button_install, 0, Qt.AlignCenter) self.widget.setLayout(layout) self.widget.setStyleSheet(load_style_sheet()) self.setSizeHint(self.widget.sizeHint()) # Signals self.button_install.clicked.connect(self.install_application) self.button_launch.clicked.connect(self.launch_application) self.button_options.clicked.connect(self.actions_menu_requested) self.timer.timeout.connect(self._application_launched) # Setup self.update_status() # --- Callbacks # ------------------------------------------------------------------------- def _application_launched(self): """ """ self.button_launch.setDisabled(False) update_pointer() def _application_installed(self, worker, output, error): """ """ self.handle_action_finished(worker, output, error) def _application_updated(self, worker, output, error): """ """ self.handle_action_finished(worker, output, error) def _application_removed(self, worker, output, error): """ """ self.handle_action_finished(worker, output, error) # --- Helpers # ------------------------------------------------------------------------- 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.widget.sig_status_updated.emit(message) def update_style_sheet(self, style_sheet=None): if style_sheet is None: style_sheet = load_style_sheet() self.menu_options.setStyleSheet(style_sheet) self.menu_versions.setStyleSheet(style_sheet) def actions_menu_requested(self): """ Create and display options menu for the currently selected application. """ self.menu_options.clear() self.menu_versions.clear() # Add versions menu versions = self.versions if self.versions else [] version_actions = [] for version in reversed(versions): action = create_action(self.widget, version, triggered=lambda value, version=version: self.install_application(version=version)) action.setCheckable(True) if self.version == version: action.setChecked(True) action.setDisabled(True) version_actions.append(action) update_action = create_action( self.widget, 'Update application', triggered=lambda: self.update_application()) if versions and versions[-1] == self.version: update_action.setDisabled(True) else: update_action.setDisabled(False) remove_action = create_action( self.widget, 'Remove application', triggered=lambda: self.remove_application()) remove_action.setEnabled(self.installed) actions = [update_action, remove_action, None, self.menu_versions] add_actions(self.menu_options, actions) add_actions(self.menu_versions, version_actions) offset = QPoint(self.button_options.width(), 0) position = self.button_options.mapToGlobal(QPoint(0, 0)) self.menu_versions.setEnabled(bool(versions)) self.menu_options.move(position + offset) self.menu_options.exec_() def update_status(self): if self.prefix: self.version = self.api.conda_package_version(self.prefix, pkg=self.name) self.installed = bool(self.version) if (self.versions and self.version != self.versions[-1] and self.installed): self.button_version.setIcon(QIcon(CONDA_MANAGER_UPGRADE_ARROW)) self.button_version.setStyleSheet( "ButtonApplicationVersion {color: #0071a0;}") self.button_version.setToolTip('Version {0} available'.format( self.versions[-1])) else: self.button_version.setIcon(QIcon()) self.button_version.setStyleSheet( "ButtonApplicationVersion {color: black;}") self.button_install.setVisible(not self.installed) self.button_launch.setVisible(self.installed) def set_loading(self, value): self.button_launch.setDisabled(value) self.button_install.setDisabled(value) self.button_options.setDisabled(value) if value: self.label_spinner.setMovie(self.movie_spinner) else: self.label_spinner.setMovie(None) if self.version is None: version = self.versions[-1] else: version = self.version self.button_version.setText(version) self.label_spinner.setVisible(value) self.button_version.setVisible(not value) def handle_action_finished(self, worker, output, error): if not isinstance(output, dict): output = {} success = output.get('success', True) if error or not success: # Error might be harmless if no decoding was possible... # Success deserves some sort of messagebox logger.error(error) self.widget.sig_application_updated.emit(self.name, self.version) self.update_status() self.set_loading(False) def update_versions(self, version=None, versions=None): """ Update button visibility depending on update availability. """ update = versions[-1] != version logger.debug(str((self.name, self.dev_tool, self.installed))) if self.installed and version: self.button_options.setVisible(True) self.button_version.setText(version) self.button_version.setVisible(True) elif not self.installed and versions: self.button_install.setEnabled(True) self.button_version.setText(versions[-1]) self.button_version.setVisible(True) self.versions = versions self.version = version self.update_status() # --- Public API # ------------------------------------------------------------------------ def install_application(self, value=None, version=None): """ Update the application on the defined prefix environment. This is used for both normal install and specific version install. """ if version: self.version = version else: self.version = self.versions[-1] version = self.versions[-1] pkg = '{0}={1}'.format(self.name, version) pkgs = [pkg] logger.debug(str((pkg, self.dev_tool))) # Check if environment exists and then create or install # is_installed = self.api.conda_package_version(prefix=self.prefix, # pkg=self.name) # pkgs = [pkg] + self.BASIC_PACKAGES # if is_installed: # worker = self.api.conda_install(prefix=self.prefix, pkgs=pkgs) # else: # worker = self.api.conda_create(prefix=self.prefix, pkgs=pkgs) worker = self.api.conda_install(prefix=self.prefix, pkgs=pkgs) worker.sig_finished.connect(self._application_installed) worker.sig_partial.connect(self._partial_output_ready) self.set_loading(True) self.widget.sig_status_updated.emit('Installing application ' '<b>{0}</b>'.format(self.name)) def launch_application(self): """ Launch application installed in prefix environment. """ if self.command is not None: if self.command.startswith('open'): command = self.command.replace("${PREFIX}", self.prefix) elif self.prefix: command = os.sep.join([self.prefix, 'bin', self.command]) else: command = self.command self.button_launch.setDisabled(True) self.timer.setInterval(self.timeout) self.timer.start() update_pointer(Qt.BusyCursor) launch(self.path, command) def remove_application(self): """ Remove the application from the defined prefix environment. """ pkg = self.name pkgs = [pkg] logger.debug(str((self.name, self.dev_tool))) worker = self.api.conda_remove(prefix=self.prefix, pkgs=pkgs) worker.sig_finished.connect(self._application_removed) worker.sig_partial.connect(self._partial_output_ready) self.set_loading(True) self.widget.sig_status_updated.emit('Removing application ' '<b>{0}</b>'.format(self.name)) def update_application(self): """ Update the application on the defined prefix environment. """ logger.debug(str((self.name, self.dev_tool, self.installed))) self.install_application(version=self.versions[-1]) self.widget.sig_status_updated.emit('Updating application ' '<b>{0}</b>'.format(self.name))