class LoadingDialog(QDialog): def __init__(self, title, maximum, parent=None): super(LoadingDialog, self).__init__(parent) #self.setAttribute(Qt.WA_DeleteOnClose) self._may_close = False layout = QVBoxLayout() layout.addWidget(QLabel("Loading...", self)) self.progress = QProgressBar(self) self.progress.setMinimum(0) self.progress.setMaximum(maximum) layout.addWidget(self.progress) self.setWindowTitle(title) self.setLayout(layout) @Slot(int) def update(self, value): self.progress.setValue(value) @Slot(int) def done(self, r): self._may_close = True super(LoadingDialog, self).done(r) def closeEvent(self, event): if self._may_close: super(LoadingDialog, self).closeEvent(event) else: event.ignore()
class ProgressBar(QWidget): def __init__(self, parent=None) -> None: super().__init__(parent) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.pbar = QProgressBar() self.description_label = QLabel() self.eta_label = QLabel() layout = QHBoxLayout() layout.addWidget(self.description_label) layout.addWidget(self.pbar) layout.addWidget(self.eta_label) self.setLayout(layout) def setRange(self, min, max): self.pbar.setRange(min, max) def _set_value(self, value): self.pbar.setValue(value) QApplication.processEvents() def _get_value(self): return self.pbar.value() def _set_description(self, desc): self.description_label.setText(desc) QApplication.processEvents() def _set_eta(self, eta): self.eta_label.setText(eta)
class SLogWidget(QWidget): """Widget to log the STracking plugins messages in the graphical interface""" def __init__(self): super().__init__() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.progress_bar = QProgressBar() self.log_area = QTextEdit() layout.addWidget(self.progress_bar) layout.addWidget(self.log_area) self.setLayout(layout) def set_advanced(self, mode: bool): """Show hide the log area depending on the plugin mode""" if mode: self.log_area.setVisible(True) else: self.log_area.setVisible(False) def set_progress(self, value: int): """Callback to update the progress bar""" self.progress_bar.setValue(value) def add_log(self, value: str): """Callback to add a new message in the log area""" self.log_area.append(value) def clear_log(self): """Callback to clear all the log area""" self.log_area.clear()
class QtLabeledProgressBar(QWidget): """QProgressBar with QLabels for description and ETA.""" def __init__(self, parent: Optional[QWidget] = None, prog: progress = None) -> None: super().__init__(parent) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.progress = prog self.qt_progress_bar = QProgressBar() self.description_label = QLabel() self.eta_label = QLabel() base_layout = QVBoxLayout() pbar_layout = QHBoxLayout() pbar_layout.addWidget(self.description_label) pbar_layout.addWidget(self.qt_progress_bar) pbar_layout.addWidget(self.eta_label) base_layout.addLayout(pbar_layout) line = QFrame(self) line.setObjectName("QtCustomTitleBarLine") line.setFixedHeight(1) base_layout.addWidget(line) self.setLayout(base_layout) def setRange(self, min, max): self.qt_progress_bar.setRange(min, max) def setValue(self, value): self.qt_progress_bar.setValue(value) QApplication.processEvents() def setDescription(self, value): if not value.endswith(': '): value = f'{value}: ' self.description_label.setText(value) QApplication.processEvents() def _set_value(self, event): self.setValue(event.value) def _get_value(self): return self.qt_progress_bar.value() def _set_description(self, event): self.setDescription(event.value) def _make_indeterminate(self, event): self.setRange(0, 0) def _set_eta(self, event): self.eta_label.setText(event.value) def _set_total(self, event): self.setRange(0, event.value)
def createScaleBar(self, barlen): pb = QProgressBar(self) pb.setAlignment(Qt.AlignCenter) pb.setStyleSheet("QProgressBar {border: solid grey; border-radius: 0px; color: black; } \ QProgressBar::chunk {background-color: #00AAFF; border-radius:0px;}") pb.setValue(100) pb.setFixedSize(barlen, 6) pb.setTextVisible(False) return pb
def __init__(self, theme='dark', emphasized=False): super().__init__(None) self.setProperty('emphasized', emphasized) self.setStyleSheet(template(raw_stylesheet, **palettes[theme])) lay = QVBoxLayout() self.setLayout(lay) lay.addWidget(QPushButton('push button')) box = QComboBox() box.addItems(['a', 'b', 'c', 'cd']) lay.addWidget(box) lay.addWidget(QFontComboBox()) hbox = QHBoxLayout() chk = QCheckBox('tristate') chk.setToolTip('I am a tooltip') chk.setTristate(True) chk.setCheckState(Qt.PartiallyChecked) chk3 = QCheckBox('checked') chk3.setChecked(True) hbox.addWidget(QCheckBox('unchecked')) hbox.addWidget(chk) hbox.addWidget(chk3) lay.addLayout(hbox) lay.addWidget(TabDemo(emphasized=emphasized)) sld = QSlider(Qt.Horizontal) sld.setValue(50) lay.addWidget(sld) scroll = QScrollBar(Qt.Horizontal) scroll.setValue(50) lay.addWidget(scroll) lay.addWidget(QHRangeSlider(parent=self)) text = QTextEdit() text.setMaximumHeight(100) text.setHtml(blurb) lay.addWidget(text) lay.addWidget(QTimeEdit()) edit = QLineEdit() edit.setPlaceholderText('LineEdit placeholder...') lay.addWidget(edit) lay.addWidget(QLabel('label')) prog = QProgressBar() prog.setValue(50) lay.addWidget(prog) groupBox = QGroupBox("Exclusive Radio Buttons") radio1 = QRadioButton("&Radio button 1") radio2 = QRadioButton("R&adio button 2") radio3 = QRadioButton("Ra&dio button 3") radio1.setChecked(True) hbox = QHBoxLayout() hbox.addWidget(radio1) hbox.addWidget(radio2) hbox.addWidget(radio3) hbox.addStretch(1) groupBox.setLayout(hbox) lay.addWidget(groupBox)
def __init__(self): """Create our windgets. """ super().__init__() layout = QVBoxLayout() # For our "uptime" timer. self.start_time = time.time() # Label for our progress bar. bar_label = QLabel("Draw Time:") layout.addWidget(bar_label) # Progress bar is not used for "progress", it's just a bar graph to show # the "draw time", the duration of the "UpdateRequest" event. bar = QProgressBar() bar.setRange(0, 100) bar.setValue(50) bar.setFormat("%vms") layout.addWidget(bar) self.bar = bar # We let the user set the "slow event" threshold. self.thresh_ms = self.THRESH_DEFAULT self.thresh_combo = QComboBox() self.thresh_combo.addItems(self.THRESH_OPTIONS) self.thresh_combo.activated[str].connect(self._change_thresh) self.thresh_combo.setCurrentText(str(self.thresh_ms)) combo_layout = QHBoxLayout() combo_layout.addWidget(QLabel("Show Events Slower Than:")) combo_layout.addWidget(self.thresh_combo) combo_layout.addWidget(QLabel("milliseconds")) combo_layout.addItem( QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) ) layout.addLayout(combo_layout) # We log slow events to this window. self.log = TextLog() layout.addWidget(self.log) # Uptime label. To indicate if the widget is getting updated. label = QLabel('') layout.addWidget(label) self.timer_label = label self.setLayout(layout) # Update us with a timer. self.timer = QTimer(self) self.timer.timeout.connect(self.update) self.timer.setInterval(self.UPDATE_MS) self.timer.start()
class FileProgressBar(QWidget): """Simple progress bar with a label""" MAX_LABEL_LENGTH = 60 def __init__(self, parent, *args, **kwargs): QWidget.__init__(self, parent) self.pap = parent self.status_text = QLabel(self) self.bar = QProgressBar(self) self.bar.setRange(0, 0) layout = QVBoxLayout() layout.addWidget(self.status_text) layout.addWidget(self.bar) self.setLayout(layout) def __truncate(self, text): ellipsis = '...' part_len = (self.MAX_LABEL_LENGTH - len(ellipsis)) / 2.0 left_text = text[:int(math.ceil(part_len))] right_text = text[-int(math.floor(part_len)):] return left_text + ellipsis + right_text @Slot(str, int) def set_label_file(self, file, size): text = self.__truncate(file) status_str = 'Downloading file list: {0} ({1})'.format( text, humanize.naturalsize(size)) self.status_text.setText(status_str) def set_bounds(self, a, b): self.bar.setRange(a, b) def reset_files(self): self.status_text.setText(" Downloading file(s)...") self.bar.show() def reset_status(self): self.status_text.setText(" Download Complete!") self.bar.hide() @Slot(str, int, int, int) def update_progress(self, file, num_chunks, bytes_recv, total_bytes): text = " Downloading {0} - {1}/{2} (Chunk {3})" self.status_text.setText( text.format(file, humanize.naturalsize(bytes_recv), humanize.naturalsize(total_bytes), num_chunks)) self.bar.setValue(bytes_recv)
class MemoryView(QWidget): set_value = Signal(int, float, float) """ Initializes and updates the view of memory(progress) bar. """ def __init__(self, parent): super(MemoryView, self).__init__(parent) self.critical = CRITICAL_PERCENTAGE self.memory_bar = QProgressBar(self) self.memory_bar.setAlignment(Qt.AlignCenter) self.set_value.connect(self._set_value) def set_bar_color(self, current_value: int, new_value: int): """ Updates the memory(progress) bar style if needed :param current_value: Used system memory in percentage from previous update :param new_value: Latest used system memory in percentage """ if from_normal_to_critical(self.critical, current_value, new_value): self.memory_bar.setStyleSheet(CRITICAL_STYLE) elif from_critical_to_normal(self.critical, current_value, new_value): self.memory_bar.setStyleSheet(NORMAL_STYLE) else: pass def invoke_set_value(self, new_value: int, mem_used: float, mem_avail: float): self.set_value.emit(new_value, mem_used, mem_avail) @Slot(int, float, float) def _set_value(self, new_value, mem_used, mem_avail): """ Receives memory usage information passed by memory presenter and updates the displayed content as well as the style if needed :param new_value: Latest used system memory in percentage :param mem_used: Used system memory in Gigabytes(GB) :param mem_avail: Available system memory in GB """ # newValue is the latest mem_used_percent current_value = self.memory_bar.value() if current_value == new_value: return self.set_bar_color(current_value, new_value) self.memory_bar.setValue(new_value) display_str = f"{mem_used:3.1f}/{mem_avail:3.1f} GB ({new_value:d}%)" self.memory_bar.setFormat(display_str)
class ProgressBar(QWidget): """QProgressBar with QLabels for description and ETA.""" def __init__(self, parent=None) -> None: super().__init__(parent) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.pbar = QProgressBar() self.description_label = QLabel() self.eta_label = QLabel() base_layout = QVBoxLayout() pbar_layout = QHBoxLayout() pbar_layout.addWidget(self.description_label) pbar_layout.addWidget(self.pbar) pbar_layout.addWidget(self.eta_label) base_layout.addLayout(pbar_layout) line = QFrame(self) line.setObjectName("QtCustomTitleBarLine") line.setFixedHeight(1) base_layout.addWidget(line) self.setLayout(base_layout) def setRange(self, min, max): self.pbar.setRange(min, max) def _set_value(self, value): self.pbar.setValue(value) QApplication.processEvents() def _get_value(self): return self.pbar.value() def _set_description(self, desc): self.description_label.setText(desc) QApplication.processEvents() def _set_eta(self, eta): self.eta_label.setText(eta)
class ProgressBar(QWidget): style = """ QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; } """ def __init__(self, parent=None, title=None, minimum=0, maximum=1, value=0): super(ProgressBar, self).__init__(parent) layout = QGridLayout(self) self.progressbar = QProgressBar(self) self.progressbar.setMinimum(minimum) self.progressbar.setMaximum(maximum) self.progressbar.setValue(value) self.progressbar.setStyleSheet(self.style) self.label = QLabel("") self.label.setStyleSheet("Qlabel { font-size: 20px }") self.label.setAlignment(QtCore.Qt.AlignCenter) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.setFixedSize(256, 64) # self.progressbar.setValue(1) layout.addWidget(self.progressbar, 0, 0) layout.addWidget(self.label, 0, 0) self.setLayout(layout) if title: self.setWindowTitle(title) def setValue(self, value): self.progressbar.setValue(value) def setMaximumValue(self, value): self.progressbar.setMaximum(value) def incrementValue(self, increment=1): self.progressbar.setValue(self.progressbar.value() + increment) def onStart(self): self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) self.show() # self.setWindowState(self.windowState() | QtCore.Qt.WindowActive) def onFinished(self): self.hide() def updateIndicatorText(self, string): self.label.setText(string)
class KiteInstallation(QWidget): """Kite progress installation widget.""" def __init__(self, parent): super(KiteInstallation, self).__init__(parent) # Left side action_layout = QVBoxLayout() progress_layout = QHBoxLayout() self._progress_widget = QWidget(self) self._progress_widget.setFixedHeight(50) self._progress_filter = HoverEventFilter() self._progress_bar = QProgressBar(self) self._progress_bar.setFixedWidth(180) self._progress_widget.installEventFilter(self._progress_filter) self.cancel_button = QPushButton() self.cancel_button.setIcon(ima.icon('DialogCloseButton')) self.cancel_button.hide() progress_layout.addWidget(self._progress_bar, alignment=Qt.AlignLeft) progress_layout.addWidget(self.cancel_button) self._progress_widget.setLayout(progress_layout) self._progress_label = QLabel(_('Downloading')) install_info = QLabel( _("Kite comes with a native app called the Copilot <br>" "which provides you with real time <br>" "documentation as you code.<br><br>" "When Kite is done installing, the Copilot will <br>" "launch automatically and guide you throught the <br>" "rest of the setup process.")) button_layout = QHBoxLayout() self.ok_button = QPushButton(_('OK')) button_layout.addStretch() button_layout.addWidget(self.ok_button) button_layout.addStretch() action_layout.addStretch() action_layout.addWidget(self._progress_label) action_layout.addWidget(self._progress_widget) action_layout.addWidget(install_info) action_layout.addSpacing(10) action_layout.addLayout(button_layout) action_layout.addStretch() # Right side copilot_image_source = get_image_path('kite_copilot.png') copilot_image = QPixmap(copilot_image_source) copilot_label = QLabel() screen = QApplication.primaryScreen() device_pixel_ratio = screen.devicePixelRatio() if device_pixel_ratio > 1: copilot_image.setDevicePixelRatio(device_pixel_ratio) copilot_label.setPixmap(copilot_image) else: image_height = int(copilot_image.height() * 0.4) image_width = int(copilot_image.width() * 0.4) copilot_label.setPixmap( copilot_image.scaled(image_width, image_height, Qt.KeepAspectRatio, Qt.SmoothTransformation)) # Layout general_layout = QHBoxLayout() general_layout.addLayout(action_layout) general_layout.addWidget(copilot_label) self.setLayout(general_layout) # Signals self._progress_filter.sig_hover_enter.connect( lambda: self.cancel_button.show()) self._progress_filter.sig_hover_leave.connect( lambda: self.cancel_button.hide()) def update_installation_status(self, status): """Update installation status (downloading, installing, finished).""" self._progress_label.setText(status) if status == INSTALLING: self._progress_bar.setRange(0, 0) def update_installation_progress(self, current_value, total): """Update installation progress bar.""" self._progress_bar.setMaximum(total) self._progress_bar.setValue(current_value)
class AbortWindow(QDialog): """ Displays busy message and provides abort button. The class serves SmoothCube, WorkerThread and SelectSmoothing. """ def __init__(self, parent=None): """ init abort or notification ui. Displays while smoothing freezes the application. Allows abort button to be added if needed. """ super(AbortWindow, self).__init__(parent) self.setModal(False) self.setWindowFlags(self.windowFlags() | Qt.Tool) self.parent = parent self.label_a_1 = QLabel("Executing smoothing algorithm.") self.label_a_2 = QLabel("This may take several minutes.") self.abort_button = QPushButton("Abort") self.abort_button.clicked.connect(self.abort) self.pb = QProgressBar(self) self.pb_counter = 0 self.abort_flag = False # vbl is short for Vertical Box Layout vbl = QVBoxLayout() vbl.addWidget(self.label_a_1) vbl.addWidget(self.label_a_2) vbl.addWidget(self.pb) vbl.addWidget(self.abort_button) self.setLayout(vbl) self.show() def init_pb(self, start, end): """ Init the progress bar :param start: Start Value :param end: End Value """ self.pb.setRange(start, end) self.pb_counter = start def update_pb(self): """ This function is called in the worker thread to update the progress bar and checks if the local class variable abort_flag is active. If the abort button is clicked, the main thread will set abort_flag to True. The next time the worker thread calls this function, a custom exception is raised terminating the calculation. The exception is handled by the WorkerThread class. :raises: AbortException: terminating smoothing calculation """ if self.abort_flag: raise AbortException("Abort Calculation") self.pb_counter += 1 self.pb.setValue(self.pb_counter) QApplication.processEvents() def abort(self): """Abort calculation""" self.abort_flag = True self.parent.clean_up() def smoothing_done(self, component_id=None): """Notify user success""" self.hide() if component_id is None: message = "The result has been added as a" \ " new component of the input Data." \ " The new component can be accessed" \ " in the viewer drop-downs." else: message = "The result has been added as" \ " \"{0}\" and can be selected" \ " in the viewer drop-down menu.".format(component_id) info = QMessageBox.information(self, "Success", message) self.clean_up() def print_error(self, exception): """Print error message""" if "signal only works in main thread" in str(exception): message = "Smoothing Failed!\n\n" + "Please update your SpectralCube package" else: message = "Smoothing Failed!\n\n" + str(exception) info = QMessageBox.critical(self, "Error", message) self.clean_up() def clean_up(self): self.parent.clean_up() def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.abort()
class ProgressDialog(QDialog): """Progress dialog. Parameters ---------- label - string or list of strings task - QThread or list of QThread (implementing task interface) parent - QObject """ canceled = Signal() def __init__(self, label, task, parent=None): """Constructor.""" super().__init__(parent) self._label = label self._task = task if (isinstance(self._label, (list, tuple)) and isinstance(self._task, (list, tuple)) and len(self._label) == len(self._task)) or \ (isinstance(self._label, str) and isinstance(self._task, QThread)): pass else: raise ValueError() self._setup_ui() def _setup_ui(self): self.layout = QVBoxLayout() self.dlg_label = QLabel(self) self.cur_item_label = QLabel(self) self.progress_bar = QProgressBar(self) self.cancel = QPushButton('Cancel', self) self.layout.addWidget(self.dlg_label) self.layout.addWidget(self.cur_item_label) self.layout.addWidget(self.progress_bar) self.layout.addWidget(self.cancel) self.setLayout(self.layout) # Set initial value self.progress_bar.setValue(0) # Set progress bar limits and connect signals self.progress_bar.setMinimum(0) if hasattr(self._task, '__iter__'): self.dlg_label.setText(self._label[0]) self._task[0].currentItem.connect(self.cur_item_label.setText) self._task[0].itemDone.connect(self.inc_value) pb_max = self._task[0].size() for i in range(1, len(self._task)): self._task[i].currentItem.connect(self.cur_item_label.setText) self._task[i].itemDone.connect(self.inc_value) self._task[i - 1].completed.connect(self._update_label) self._task[i - 1].completed.connect(self._task[i].start) pb_max += self._task[i].size() self.progress_bar.setMaximum(pb_max) self._task[-1].completed.connect(lambda: self._exit_dlg(1)) else: self.dlg_label.setText(self._label) self._task.currentItem.connect(self.cur_item_label.setText) self._task.itemDone.connect(self.inc_value) self._task.completed.connect(lambda: self._exit_dlg(1)) self.progress_bar.setMaximum(self._task.size()) self.cancel.pressed.connect(self.canceled.emit) self.canceled.connect(lambda: self._exit_dlg(0)) self.progress_bar.valueChanged.connect(self._is_finished) def _update_label(self): next_idx = self._label.index(self.dlg_label.text()) + 1 if next_idx < len(self._label): self.dlg_label.setText(self._label[next_idx]) def _exit_dlg(self, result): if hasattr(self._task, '__iter__'): for task in self._task: task.exit_task() self._wait_task(self._task[-1]) self._task[-1].deleteLater() else: self._task.exit_task() self._wait_task(self._task) self._task.deleteLater() if result == 1: self.accept() elif result == 0: self.reject() def _wait_task(self, task): init = time.time() try: while task.isRunning(): time.sleep(0.1) if time.time() - init > 10: self._exit_dlg(0) raise Exception('Thread will not leave') except RuntimeError: pass def _is_finished(self): if self.progress_bar.value() == self.progress_bar.maximum(): self._exit_dlg(1) def set_value(self, value): """Set progress bar value.""" self.progress_bar.setValue(value) def inc_value(self): """Increase value.""" self.progress_bar.setValue(self.progress_bar.value()+1) def exec_(self): """Override.""" if hasattr(self._task, '__iter__'): self._task[0].start() else: self._task.start() return super().exec_()
class SupervoxelCard(Card): def __init__(self, svid, svname, parent=None): super().__init__(title=svname, collapsible=True, removable=True, editable=True, parent=parent) self.svid = svid self.svname = svname from qtpy.QtWidgets import QProgressBar self.pbar = QProgressBar(self) self.add_row(self.pbar) from survos2.frontend.plugins.features import FeatureComboBox self.svsource = FeatureComboBox() self.svsource.setMaximumWidth(250) self.svshape = LineEdit(parse=int, default=10) self.svshape.setMaximumWidth(250) self.svspacing = LineEdit3D(parse=float, default=1) self.svspacing.setMaximumWidth(250) self.svcompactness = LineEdit(parse=float, default=20) self.svcompactness.setMaximumWidth(250) self.int64_checkbox = CheckBox(checked=False) self.compute_btn = PushButton("Compute") self.view_btn = PushButton("View", accent=True) self.add_row(HWidgets("Source:", self.svsource, stretch=1)) self.add_row(HWidgets("Shape:", self.svshape, stretch=1)) self.add_row(HWidgets("Spacing:", self.svspacing, stretch=1)) self.add_row(HWidgets("Compactness:", self.svcompactness, stretch=1)) self.add_row(HWidgets("Int64:", self.int64_checkbox, stretch=1)) self.add_row(HWidgets(None, self.compute_btn)) self.add_row(HWidgets(None, self.view_btn)) self.compute_btn.clicked.connect(self.compute_supervoxels) self.view_btn.clicked.connect(self.view_regions) def card_deleted(self): params = dict(region_id=self.svid, workspace=True) result = Launcher.g.run("regions", "remove", **params) if result["done"]: self.setParent(None) cfg.ppw.clientEvent.emit({ "source": "regions", "data": "remove_layer", "layer_name": self.svid, }) def card_title_edited(self, newtitle): logger.debug(f"Edited region title {newtitle}") params = dict(region_id=self.svid, new_name=newtitle, workspace=True) result = Launcher.g.run("regions", "rename", **params) return result["done"] def view_regions(self): logger.debug(f"Transferring supervoxels {self.svid} to viewer") print(f"Current Supervoxels: {cfg.current_supervoxels}") cfg.ppw.clientEvent.emit({ "source": "regions", "data": "view_regions", "region_id": self.svid }) def compute_supervoxels(self): self.pbar.setValue(10) # src = [ # DataModel.g.dataset_uri("features/" + s) for s in [self.svsource.value()] # ] src = DataModel.g.dataset_uri(self.svsource.value(), group="features") dst = DataModel.g.dataset_uri(self.svid, group="regions") logger.debug(f"Compute sv: Src {src} Dst {dst}") from survos2.model import Workspace ws = Workspace(DataModel.g.current_workspace) num_chunks = np.prod(np.array(ws.metadata()["chunk_grid"])) chunk_size = ws.metadata()["chunk_size"] logger.debug( f"Using chunk_size {chunk_size} to compute number of supervoxel segments for num_chunks: {num_chunks}." ) with DatasetManager(src, out=None, dtype="float32", fillvalue=0) as DM: src_dataset_shape = DM.sources[0][:].shape # n_segments = int(np.prod(chunk_size) // (self.svshape.value() ** 3)) n_segments = int(np.prod(src_dataset_shape) / self.svshape.value()**3) if self.int64_checkbox.value(): out_dtype = "uint64" else: out_dtype = "uint32" params = dict( src=src, dst=dst, compactness=round(self.svcompactness.value() / 100, 3), # shape=self.svshape.value(), n_segments=n_segments, spacing=self.svspacing.value(), modal=False, out_dtype=out_dtype, ) logger.debug(f"Compute supervoxels with params {params}") self.pbar.setValue(20) result = Launcher.g.run("regions", "supervoxels", **params) if result is not None: self.pbar.setValue(100) def update_params(self, params): if "shape" in params: self.svshape.setValue(params["shape"]) if "compactness" in params: self.svcompactness.setValue(params["compactness"] * 100) if "spacing" in params: self.svspacing.setValue(params["spacing"]) if "source" in params: for source in params["source"]: self.svsource.select(source)
class BatchProcessingView(View): btn_run_batch: QPushButton progress_bar: QProgressBar field_channel: QLineEdit field_workflow_config: FileInput field_input_dir: FileInput field_output_dir: FileInput def __init__(self, controller: IBatchProcessingController): super().__init__(template_class=MainTemplate) if controller is None: raise ValueError("controller") self._controller = controller self.setObjectName("batchProcessingView") def load(self, model=None): self._setup_ui() def _setup_ui(self): """ Set up the UI for the BatchProcessingView """ layout = QVBoxLayout() self.setLayout(layout) # Workflow config self.field_workflow_config = FileInput( mode=FileInputMode.FILE, filter="Json file (*.json)", placeholder_text="Load a JSON workflow file..." ) self.field_workflow_config.file_selected.connect(self._form_field_changed) row1 = FormRow("1. Load workflow:", self.field_workflow_config) # Channel index self.field_channel = QLineEdit("0") self.field_channel.setValidator(QIntValidator(bottom=0)) self.field_channel.textChanged.connect(self._form_field_changed) row2 = FormRow("2. Structure channel index:", self.field_channel) # Input dir self.field_input_dir = FileInput(mode=FileInputMode.DIRECTORY, placeholder_text="Select a directory...") self.field_input_dir.file_selected.connect(self._form_field_changed) row3 = FormRow("3. Input directory:", self.field_input_dir) # Output dir self.field_output_dir = FileInput(mode=FileInputMode.DIRECTORY, placeholder_text="Select a directory...") self.field_output_dir.file_selected.connect(self._form_field_changed) row4 = FormRow("4. Output directory:", self.field_output_dir) # Help label = QLabel() label.setText("Supported file formats: .tif, .tiff, .czi, .ome.tif, .ome.tiff") form = QWidget() form.setLayout(Form([row1, row2, row3, row4])) layout.addWidget(form) layout.addWidget(label) # Submit self.btn_run_batch = QPushButton("Run Batch") self.btn_run_batch.clicked.connect(self._btn_run_batch_clicked) self.update_button(enabled=False) layout.addWidget(self.btn_run_batch) # Progress bar self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) self.progress_bar.setTextVisible(True) self.progress_bar.setVisible(False) layout.addWidget(self.progress_bar) def update_button(self, enabled: bool): """ Update state of process button Inputs: enabled: True to enable the button, false to disable it """ self.btn_run_batch.setEnabled(enabled) def set_run_batch_in_progress(self): """ Update page to reflect that a batch run is in progress """ # TODO make a CancelButton widget to avoid repeating this connect / disconnect pattern self.btn_run_batch.setText("Cancel") self.btn_run_batch.clicked.disconnect() self.btn_run_batch.clicked.connect(self._btn_run_batch_cancel_clicked) self.progress_bar.setVisible(True) def reset_run_batch(self): """ Reset page state to reflect that there is no batch run in progress """ self.progress_bar.setValue(0) self.btn_run_batch.setText("Run Batch") self.btn_run_batch.clicked.disconnect() self.btn_run_batch.clicked.connect(self._btn_run_batch_clicked) self.progress_bar.setVisible(False) def set_progress(self, progress: int): """ Update progress bar Inputs: progress (int): numerical value to set the progress bar to """ self.progress_bar.setValue(progress) ##################################################################### # Event handlers ##################################################################### def _btn_run_batch_clicked(self): self._controller.run_batch() def _btn_run_batch_cancel_clicked(self): self.btn_run_batch.setText("Canceling...") self._controller.cancel_run_batch() def _form_field_changed(self, value): workflow_config = self.field_workflow_config.selected_file channel_index = int(self.field_channel.text()) if self.field_channel.text() else None input_dir = self.field_input_dir.selected_file output_dir = self.field_output_dir.selected_file self._controller.update_batch_parameters(workflow_config, channel_index, input_dir, output_dir)
class Example(QWidget): def __init__(self): super(Example, self).__init__() self.initUI() self.timer.start(10, self) def initUI(self): ml = QVBoxLayout(self) ul = QHBoxLayout(self) dl = QHBoxLayout(self) ml.addLayout(ul) ml.addLayout(dl) col = QColor(0, 0, 0) self.colorText = 'Color Nmae: {}' self.btn = QPushButton('Dialog', self) self.btn.move(20, 20) self.btn.clicked.connect(self.showDialog) self.btn2 = QPushButton('Color', self) self.btn2.move(20, 40) self.btn2.clicked.connect(self.showColorDialoge) self.frm = QFrame(self) self.frm.setStyleSheet("QWidget { background-color: %s }" % col.name()) self.frm.setGeometry(130, 50, 100, 100) self.le = QLineEdit(self) self.le.move(130, 22) self.label = QLabel(self.colorText.format(None), self) self.label.setGeometry(20, 80, 100, 30) self.timer = QBasicTimer() self.pb = QProgressBar(self) self.step = 0 self.pb.setGeometry(20, 170, 300, 20) # calendar self.timeLable = QLabel('time is: ', self) # self.timeLable.setGeometry(20, 120, 100, 30) cal = QCalendarWidget(self) cal.clicked[QDate].connect(self.showDate) ul.addWidget(self.timeLable) dl.addWidget(cal) self.setLayout(ml) self.setGeometry(300, 300, 550, 550) self.setWindowTitle('Dialog') self.show() def showDialog(self): # 通过下面的语句来实现QInputDialog的显示 text, ok = QInputDialog.getText(self, 'Input Dialog', 'Input some thing') if ok: self.le.setText(text) def showColorDialoge(self): col = QColorDialog.getColor() # 获取colorDialog的颜色 if col.isValid(): self.label.setText(self.colorText.format(col.name())) self.frm.setStyleSheet("QWidget { background-color: %s}" % col.name()) def timerEvent(self, e): if self.step >= 100: self.timer.stop() else: self.step += 1 self.pb.setValue(self.step) def showDate(self, date): d = date.toString(Qt.ISODate) self.timeLable.setText(d)
class ProgressView(QWidget): """ :type batch_manager: CalculationManager """ def __init__(self, parent, batch_manager): super().__init__(parent) self.task_count = 0 self.calculation_manager = batch_manager self.whole_progress = QProgressBar(self) self.whole_progress.setMinimum(0) self.whole_progress.setMaximum(1) self.whole_progress.setFormat("%v of %m") self.whole_progress.setTextVisible(True) self.part_progress = QProgressBar(self) self.part_progress.setMinimum(0) self.part_progress.setMaximum(1) self.part_progress.setFormat("%v of %m") self.whole_label = QLabel("All batch progress:", self) self.part_label = QLabel("Single batch progress:", self) self.cancel_remove_btn = QPushButton("Remove task") self.cancel_remove_btn.setDisabled(True) self.logs = ExceptionList(self) self.logs.setToolTip("Logs") self.task_view = QListView() self.task_que = QStandardItemModel(self) self.task_view.setModel(self.task_que) self.process_num_timer = QTimer() self.process_num_timer.setInterval(1000) self.process_num_timer.setSingleShot(True) self.process_num_timer.timeout.connect(self.change_number_of_workers) self.number_of_process = QSpinBox(self) self.number_of_process.setRange(1, multiprocessing.cpu_count()) self.number_of_process.setValue(1) self.number_of_process.setToolTip( "Number of process used in batch calculation") self.number_of_process.valueChanged.connect( self.process_num_timer_start) self.progress_item_dict = {} layout = QGridLayout() layout.addWidget(self.whole_label, 0, 0, Qt.AlignRight) layout.addWidget(self.whole_progress, 0, 1, 1, 2) layout.addWidget(self.part_label, 1, 0, Qt.AlignRight) layout.addWidget(self.part_progress, 1, 1, 1, 2) lab = QLabel("Number of process:") lab.setToolTip("Number of process used in batch calculation") layout.addWidget(lab, 2, 0) layout.addWidget(self.number_of_process, 2, 1) layout.addWidget(self.logs, 3, 0, 2, 3) layout.addWidget(self.task_view, 0, 4, 4, 1) layout.addWidget(self.cancel_remove_btn, 4, 4, 1, 1) layout.setColumnMinimumWidth(2, 10) layout.setColumnStretch(2, 1) self.setLayout(layout) self.preview_timer = QTimer() self.preview_timer.setInterval(1000) self.preview_timer.timeout.connect(self.update_info) self.task_view.selectionModel().currentChanged.connect( self.task_selection_change) self.cancel_remove_btn.clicked.connect(self.task_cancel_remove) def task_selection_change(self, new, old): task: CalculationProcessItem = self.task_que.item( new.row(), new.column()) if task is None: self.cancel_remove_btn.setDisabled(True) return self.cancel_remove_btn.setEnabled(True) if task.is_finished(): self.cancel_remove_btn.setText(f"Remove task {task.num}") else: self.cancel_remove_btn.setText(f"Cancel task {task.num}") def task_cancel_remove(self): index = self.task_view.selectionModel().currentIndex() task: CalculationProcessItem = self.task_que.item( index.row(), index.column()) if task.is_finished(): self.calculation_manager.remove_calculation(task.calculation) self.task_que.takeRow(index.row()) else: self.calculation_manager.cancel_calculation(task.calculation) print(task) def new_task(self): self.whole_progress.setMaximum( self.calculation_manager.calculation_size) if not self.preview_timer.isActive(): self.update_info() self.preview_timer.start() def update_info(self): res = self.calculation_manager.get_results() for el in res.errors: if el[0]: QListWidgetItem(el[0], self.logs) ExceptionListItem(el[1], self.logs) if (state_store.report_errors and parsed_version.is_devrelease and not isinstance(el[1][0], SegmentationLimitException) and isinstance(el[1][1], tuple)): with sentry_sdk.push_scope() as scope: scope.set_tag("auto_report", "true") sentry_sdk.capture_event(el[1][1][0]) self.whole_progress.setValue(res.global_counter) working_search = True for uuid, progress in res.jobs_status.items(): calculation = self.calculation_manager.calculation_dict[uuid] total = len(calculation.file_list) if uuid in self.progress_item_dict: item = self.progress_item_dict[uuid] item.update_count(progress) else: item = CalculationProcessItem(calculation, self.task_count, progress) self.task_count += 1 self.task_que.appendRow(item) self.progress_item_dict[uuid] = item if working_search and progress != total: self.part_progress.setMaximum(total) self.part_progress.setValue(progress) working_search = False if not self.calculation_manager.has_work: self.part_progress.setValue(self.part_progress.maximum()) self.preview_timer.stop() logging.info("Progress stop") def process_num_timer_start(self): self.process_num_timer.start() def update_progress(self, total_progress, part_progress): self.whole_progress.setValue(total_progress) self.part_progress.setValue(part_progress) def set_total_size(self, size): self.whole_progress.setMaximum(size) def set_part_size(self, size): self.part_progress.setMaximum(size) def change_number_of_workers(self): self.calculation_manager.set_number_of_workers( self.number_of_process.value())
class GcodeBackplot(QBackPlot): line_selected = Signal(int) gcode_error = Signal(str) def __init__(self, parent=None, standalone=False): super(GcodeBackplot, self).__init__(parent) # This prevents doing unneeded initialization # when QtDesginer loads the plugin. if parent is None and not standalone: return self.show_overlay = False # no DRO or DRO overlay self.program_alpha = True self.grid_size = 1 self._reload_filename = None # Add loading progress bar and abort button self.progressBar = QProgressBar(visible=False) self.progressBar.setFormat("Loading backplot: %p%") self.abortButton = QPushButton('Abort', visible=False) hBox = QHBoxLayout() hBox.addWidget(self.progressBar) hBox.addWidget(self.abortButton) vBox = QVBoxLayout(self) vBox.addStretch() vBox.addLayout(hBox) self.abortButton.clicked.connect(self.abort) STATUS.actual_position.onValueChanged(self.update) STATUS.joint_actual_position.onValueChanged(self.update) STATUS.homed.onValueChanged(self.update) STATUS.limit.onValueChanged(self.update) STATUS.tool_in_spindle.onValueChanged(self.update) STATUS.motion_mode.onValueChanged(self.update) STATUS.current_vel.onValueChanged(self.update) STATUS.g5x_offset.onValueChanged(self.reloadBackplot) STATUS.g92_offset.onValueChanged(self.reloadBackplot) # Connect status signals STATUS.file.notify(self.loadBackplot) # STATUS.reload_backplot.notify(self.reloadBackplot) STATUS.program_units.notify(lambda v: self.setMetricUnits(v == 2)) def loadBackplot(self, fname): LOG.debug('load the display: {}'.format(fname.encode('utf-8'))) self._reload_filename = fname self.load(fname) @Slot() def reloadBackplot(self): QTimer.singleShot(100, lambda: self._reloadBackplot()) def _reloadBackplot(self): LOG.debug('reload the display: {}'.format(self._reload_filename)) dist = self.get_zoom_distance() try: self.load(self._reload_filename) self.set_zoom_distance(dist) except: LOG.warning("Problem reloading backplot file: {}".format(self._reload_filename), exc_info=True) # ========================================================================== # Override QBackPlot methods # ========================================================================== def report_loading_started(self): self.progressBar.show() self.abortButton.show() self.start = time.time() def report_progress_percentage(self, percentage): QApplication.processEvents() self.progressBar.setValue(percentage) def report_loading_finished(self): print((time.time() - self.start)) self.progressBar.hide() self.abortButton.hide() # overriding functions def report_gcode_error(self, result, seq, filename): error = gcode.strerror(result) file = os.path.basename(filename) line = seq - 1 msg = "G-code error in '{}' near line {}: {}".format(file, line, error) LOG.error(msg) STATUS.backplot_gcode_error.emit(msg) # Override gremlin's / glcannon.py function so we can emit a GObject signal def update_highlight_variable(self, line): self.highlight_line = line if line is None: line = -1 STATUS.backplot_line_selected.emit(line) # ============================================================================== # QtDesigner property setters/getters # ============================================================================== @Slot(str) def setView(self, view): view = view.lower() if self.is_lathe: if view not in ['p', 'y', 'y2']: return False elif view not in ['p', 'x', 'y', 'z', 'z2']: return False self.current_view = view if self.initialised: self.set_current_view() def getView(self): return self.current_view defaultView = Property(str, getView, setView) @Slot() def setViewP(self): self.setView('p') @Slot() def setViewX(self): self.setView('x') @Slot() def setViewY(self): self.setView('y') @Slot() def setViewZ(self): self.setView('z') @Slot() def setViewZ2(self): self.setView('z2') @Slot() def clearLivePlot(self): self.clear_live_plotter() @Slot() def zoomIn(self): self.zoomin() @Slot() def zoomOut(self): self.zoomout() @Slot(bool) def alphaBlend(self, alpha): self.program_alpha = alpha self.update() @Slot(bool) def showGrid(self, grid): self.grid_size = int(grid) # ugly hack for now self.update() # @Slot(str) Fixme check for the correct data type def setdro(self, state): self.enable_dro = state self.updateGL() def getdro(self): return self.enable_dro _dro = Property(bool, getdro, setdro) # DTG # @Slot(str) Fixme check for the correct data type def setdtg(self, state): self.show_dtg = state self.updateGL() def getdtg(self): return self.show_dtg _dtg = Property(bool, getdtg, setdtg) # METRIC # @Slot(str) Fixme check for the correct data type def setMetricUnits(self, metric): self.metric_units = metric self.updateGL() def getMetricUnits(self): return self.metric_units metricUnits = Property(bool, getMetricUnits, setMetricUnits) # @Slot(str) Fixme check for the correct data type def setProgramAlpha(self, alpha): self.program_alpha = alpha self.updateGL() def getProgramAlpha(self): return self.program_alpha renderProgramAlpha = Property(bool, getProgramAlpha, setProgramAlpha) # @Slot(str) Fixme check for the correct data type def setBackgroundColor(self, color): self.colors['back'] = color.getRgbF()[:3] self.updateGL() def getBackgroundColor(self): r, g, b = self.colors['back'] color = QColor() color.setRgbF(r, g, b, 1.0) return color backgroundColor = Property(QColor, getBackgroundColor, setBackgroundColor)
class PackagesDialog(DialogBase): """Package dependencies dialog.""" sig_setup_ready = Signal() def __init__( self, parent=None, packages=None, pip_packages=None, remove_only=False, update_only=False, ): """About dialog.""" super(PackagesDialog, self).__init__(parent=parent) # Variables self.api = AnacondaAPI() self.actions = None self.packages = packages or [] self.pip_packages = pip_packages or [] # Widgets self.stack = QStackedWidget() self.table = QTableWidget() self.text = QTextEdit() self.label_description = LabelBase() self.label_status = LabelBase() self.progress_bar = QProgressBar() self.button_ok = ButtonPrimary('Apply') self.button_cancel = ButtonNormal('Cancel') # Widget setup self.text.setReadOnly(True) self.stack.addWidget(self.table) self.stack.addWidget(self.text) if remove_only: text = 'The following packages will be removed:<br>' else: text = 'The following packages will be modified:<br>' self.label_description.setText(text) self.label_description.setWordWrap(True) self.label_description.setWordWrap(True) self.label_status.setWordWrap(True) self.table.horizontalScrollBar().setVisible(False) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setAlternatingRowColors(True) self.table.setSelectionMode(QAbstractItemView.NoSelection) self.table.setSortingEnabled(True) self._hheader = self.table.horizontalHeader() self._vheader = self.table.verticalHeader() self._hheader.setStretchLastSection(True) self._hheader.setDefaultAlignment(Qt.AlignLeft) self._hheader.setSectionResizeMode(self._hheader.Fixed) self._vheader.setSectionResizeMode(self._vheader.Fixed) self.button_ok.setMinimumWidth(70) self.button_ok.setDefault(True) self.base_minimum_width = 300 if remove_only else 420 if remove_only: self.setWindowTitle("Remove Packages") elif update_only: self.setWindowTitle("Update Packages") else: self.setWindowTitle("Install Packages") self.setMinimumWidth(self.base_minimum_width) # Layouts layout_progress = QHBoxLayout() layout_progress.addWidget(self.label_status) layout_progress.addWidget(SpacerHorizontal()) layout_progress.addWidget(self.progress_bar) layout_buttons = QHBoxLayout() layout_buttons.addStretch() layout_buttons.addWidget(self.button_cancel) layout_buttons.addWidget(SpacerHorizontal()) layout_buttons.addWidget(self.button_ok) layout = QVBoxLayout() layout.addWidget(self.label_description) layout.addWidget(SpacerVertical()) layout.addWidget(self.stack) layout.addWidget(SpacerVertical()) layout.addLayout(layout_progress) layout.addWidget(SpacerVertical()) layout.addWidget(SpacerVertical()) layout.addLayout(layout_buttons) self.setLayout(layout) # Signals self.button_ok.clicked.connect(self.accept) self.button_cancel.clicked.connect(self.reject) self.button_ok.setDisabled(True) # Setup self.table.setDisabled(True) self.update_status('Solving package specifications', value=0, max_value=0) def setup(self, worker, output, error): """Setup the widget to include the list of dependencies.""" if not isinstance(output, dict): output = {} packages = sorted(pkg.split('==')[0] for pkg in self.packages) success = output.get('success') error = output.get('error', '') exception_name = output.get('exception_name', '') actions = output.get('actions', []) prefix = worker.prefix if exception_name: message = exception_name else: # All requested packages already installed message = output.get('message', ' ') navi_deps_error = self.api.check_navigator_dependencies( actions, prefix) description = self.label_description.text() if error: description = 'No packages will be modified.' self.stack.setCurrentIndex(1) self.button_ok.setDisabled(True) if self.api.is_offline(): error = ("Some of the functionality of Anaconda Navigator " "will be limited in <b>offline mode</b>. <br><br>" "Installation and upgrade actions will be subject to " "the packages currently available on your package " "cache.") self.text.setText(error) elif navi_deps_error: description = 'No packages will be modified.' error = ('Downgrading/removing these packages will modify ' 'Anaconda Navigator dependencies.') self.text.setText(error) self.stack.setCurrentIndex(1) message = 'NavigatorDependenciesError' self.button_ok.setDisabled(True) elif success and actions: self.stack.setCurrentIndex(0) # Conda 4.3.x if isinstance(actions, list): actions_link = actions[0].get('LINK', []) actions_unlink = actions[0].get('UNLINK', []) # Conda 4.4.x else: actions_link = actions.get('LINK', []) actions_unlink = actions.get('UNLINK', []) deps = set() deps = deps.union({p['name'] for p in actions_link}) deps = deps.union({p['name'] for p in actions_unlink}) deps = deps - set(packages) deps = sorted(list(deps)) count_total_packages = len(packages) + len(deps) plural_total = 's' if count_total_packages != 1 else '' plural_selected = 's' if len(packages) != 1 else '' self.table.setRowCount(count_total_packages) self.table.setColumnCount(4) if actions_link: description = '{0} package{1} will be installed'.format( count_total_packages, plural_total) self.table.showColumn(2) self.table.showColumn(3) elif actions_unlink and not actions_link: self.table.hideColumn(2) self.table.hideColumn(3) self.table.setHorizontalHeaderLabels( ['Name', 'Unlink', 'Link', 'Channel']) description = '{0} package{1} will be removed'.format( count_total_packages, plural_total) for row, pkg in enumerate(packages + deps): link_item = [p for p in actions_link if p['name'] == pkg] if not link_item: link_item = { 'version': '-'.center(len('link')), 'channel': '-'.center(len('channel')), } else: link_item = link_item[0] unlink_item = [p for p in actions_unlink if p['name'] == pkg] if not unlink_item: unlink_item = { 'version': '-'.center(len('link')), } else: unlink_item = unlink_item[0] unlink_version = str(unlink_item['version']) link_version = str(link_item['version']) item_unlink_v = QTableWidgetItem(unlink_version) item_link_v = QTableWidgetItem(link_version) item_link_c = QTableWidgetItem(link_item['channel']) if pkg in packages: item_name = QTableWidgetItem(pkg) else: item_name = QTableWidgetItem('*' + pkg) items = [item_name, item_unlink_v, item_link_v, item_link_c] for column, item in enumerate(items): item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) self.table.setItem(row, column, item) if deps: message = ( '<b>*</b> indicates the package is a dependency of a ' 'selected package{0}<br>').format(plural_selected) self.button_ok.setEnabled(True) self.table.resizeColumnsToContents() unlink_width = self.table.columnWidth(1) if unlink_width < 60: self.table.setColumnWidth(1, 60) self.table.setHorizontalHeaderLabels( ['Name ', 'Unlink ', 'Link ', 'Channel ']) self.table.setEnabled(True) self.update_status(message=message) self.label_description.setText(description) # Adjust size after data has populated the table self.table.resizeColumnsToContents() width = sum( self.table.columnWidth(i) for i in range(self.table.columnCount())) delta = (self.width() - self.table.width() + self.table.verticalHeader().width() + 10) new_width = width + delta if new_width < self.base_minimum_width: new_width = self.base_minimum_width self.setMinimumWidth(new_width) self.setMaximumWidth(new_width) self.sig_setup_ready.emit() def update_status(self, message='', value=None, max_value=None): """Update status of packages dialog.""" self.label_status.setText(message) if max_value is None and value is None: self.progress_bar.setVisible(False) else: self.progress_bar.setVisible(True) self.progress_bar.setMaximum(max_value) self.progress_bar.setValue(value)
class EmailReport(QWidget): emailSentSignal = Signal() def __init__(self, info='', report='', toemail=None, fromemail=None, password=None, server=None, port=25, subject='Report', parent=None): super().__init__(parent=parent) self._info = str(info) # infomation reported self._report = str(report) # report self._toemail = str(toemail) self._subject = str(subject) mainLayout = QVBoxLayout(self) mainLayout.setContentsMargins(0, 0, 0, 0) mainLayout.setSpacing(0) self.setStyleSheet("background-color: white;") # information self.infoText = QLabel(self._info) self.infoText.setWordWrap(True) self.infoText.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.infoText.setFont(QFont('Mcorsoft YaHei', 11, 50)) self.infoText.setStyleSheet("color:rgb(130,130,130); padding:10px;") self.infoText.setFrameStyle(QFrame.Box|QFrame.Raised) # report self.reportText = QLabel(self._report) self.reportText.setWordWrap(True) self.reportText.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.reportText.setFont(QFont('Mcorsoft YaHei', 11, 50)) self.reportText.setStyleSheet("color:rgb(130,130,130); padding:10px;") self.reportText.setFrameStyle(QFrame.Box|QFrame.Raised) self.reportText.hide() # 邮件地址 self.emailWidget = Email(email=fromemail, password=password, server=server, port=port, parent=self) # 邮件发送按钮 buttonLayout = QHBoxLayout() buttonLayout.setContentsMargins(10, 10, 10, 10) buttonLayout.setSpacing(0) buttonLayout.setAlignment(Qt.AlignHCenter) emailSendButton = QPushButton() emailSendButton.setText("发送邮件") # 绑定点击事件处理函数 emailSendButton.clicked.connect(self.sendEmail) emailSendButton.setStyleSheet( "QPushButton{font:63 11pt 微软雅黑;border:0px; border-radius:3px; color:#FFFFFF; background-color:#2278C6}" "QPushButton:hover{font:63 11pt 微软雅黑;border:0px; border-radius:3px;background-color:#1891FF;}" ) buttonLayout.addWidget(emailSendButton) # 进度条 self.progressBar = QProgressBar(self) self.progressBar.setRange(0, 10) self.progressBar.setTextVisible(False) self.progressBar.setMaximumHeight(5) # 将控件放入布局中 mainLayout.addWidget(self.infoText) mainLayout.addWidget(self.reportText) mainLayout.addWidget(self.emailWidget) mainLayout.addLayout(buttonLayout) mainLayout.addWidget(self.progressBar) def sendEmail(self): successFlag = True subject = self.subject() report = self.report() toemail = self.toemail() server = self.server() port = self.port() fromemail = self.fromemail() password = self.password() self.progressBar.setValue(2) if successFlag: try: message = MIMEText(report, 'plain', 'utf-8') message['From'] = Header(formataddr(('Reporter', fromemail)), 'utf-8') # 发送者 message['To'] = Header(formataddr(('Handler', toemail)), 'utf-8') # 接收者 message['Subject'] = Header(subject, 'utf-8') self.progressBar.setValue(4) except: showQuickMessage(title='error', text='请输入合法的邮箱', icon=QMessageBox.Critical, parent=self) successFlag = False if successFlag: try: with smtplib.SMTP() as smtpObj: smtpObj.connect(server, port) smtpObj.login(fromemail, password) self.progressBar.setValue(6) smtpObj.sendmail(fromemail, [toemail], message.as_string()) self.progressBar.setValue(10) showQuickMessage(title='info', text="邮件发送完成", icon=QMessageBox.Information, parent=self) except: showQuickMessage(title='error', text=textwrap.dedent(""" 邮件发送失败,请检查 1)网络是否连接。 2)邮箱账号密码是否输入正确 3)邮箱是否与服务器对应"""), icon=QMessageBox.Critical, parent=self) successFlag = False if successFlag: self.emailSentSignal.emit() self.progressBar.reset() def subject(self): return self._subject def setSubject(self, subject): self._subject = str(subject) def info(self): return self._info def setInfo(self, info): self._info = str(info) self.infoText.setText(self._info) def report(self): return self._report def setReport(self, report): self._report = str(report) self.reportText.setText(self._report) def setReportVisible(self, visible=True): if visible: self.reportText.show() else: self.reportText.hide() def showReport(self): self.setReportVisible(True) def hideReport(self): self.setReportVisible(False) def server(self): return self.emailWidget.server() def setServer(self, server): self.emailWidget.setServer(server) def port(self): return self.emailWidget.port() def setPort(self, port): self.emailWidget.setPort(port) def fromemail(self): return self.emailWidget.email() def setFromemail(self, email): self.emailWidget.setEmail(email) def toemail(self): return self._toemail def setToemail(self, email): self._toemail = email def password(self): return self.emailWidget.password() def setPassword(self, password): self.emailWidget.setPassword(password)
class RunDialog(QDialog): simulation_done = Signal(bool, str) simulation_termination_request = Signal() def __init__(self, config_file, run_model, simulation_arguments, parent=None): QDialog.__init__(self, parent) self.setWindowFlags(Qt.Window) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.setModal(True) self.setWindowModality(Qt.WindowModal) self.setWindowTitle("Simulations - {}".format(config_file)) self._snapshot_model = SnapshotModel(self) self._run_model = run_model self._isDetailedDialog = False self._minimum_width = 1200 ert = None if isinstance(run_model, BaseRunModel): ert = run_model.ert() self._simulations_argments = simulation_arguments self._ticker = QTimer(self) self._ticker.timeout.connect(self._on_ticker) progress_proxy_model = ProgressProxyModel(self._snapshot_model, parent=self) self._total_progress_label = QLabel(_TOTAL_PROGRESS_TEMPLATE.format(0), self) self._total_progress_bar = QProgressBar(self) self._total_progress_bar.setRange(0, 100) self._total_progress_bar.setTextVisible(False) self._iteration_progress_label = QLabel(self) self._progress_view = ProgressView(self) self._progress_view.setModel(progress_proxy_model) self._progress_view.setIndeterminate(True) legend_view = LegendView(self) legend_view.setModel(progress_proxy_model) self._tab_widget = QTabWidget(self) self._snapshot_model.rowsInserted.connect(self.on_new_iteration) self._job_label = QLabel(self) self._job_model = JobListProxyModel(self, 0, 0, 0, 0) self._job_model.setSourceModel(self._snapshot_model) self._job_view = QTableView(self) self._job_view.clicked.connect(self._job_clicked) self._open_files = {} self._job_view.setModel(self._job_model) self.running_time = QLabel("") self.plot_tool = PlotTool(config_file) self.plot_tool.setParent(self) self.plot_button = QPushButton(self.plot_tool.getName()) self.plot_button.clicked.connect(self.plot_tool.trigger) self.plot_button.setEnabled(ert is not None) self.kill_button = QPushButton("Kill Simulations") self.done_button = QPushButton("Done") self.done_button.setHidden(True) self.restart_button = QPushButton("Restart") self.restart_button.setHidden(True) self.show_details_button = QPushButton("Show Details") self.show_details_button.setCheckable(True) size = 20 spin_movie = resourceMovie("ide/loading.gif") spin_movie.setSpeed(60) spin_movie.setScaledSize(QSize(size, size)) spin_movie.start() self.processing_animation = QLabel() self.processing_animation.setMaximumSize(QSize(size, size)) self.processing_animation.setMinimumSize(QSize(size, size)) self.processing_animation.setMovie(spin_movie) button_layout = QHBoxLayout() button_layout.addWidget(self.processing_animation) button_layout.addWidget(self.running_time) button_layout.addStretch() button_layout.addWidget(self.show_details_button) button_layout.addWidget(self.plot_button) button_layout.addWidget(self.kill_button) button_layout.addWidget(self.done_button) button_layout.addWidget(self.restart_button) button_widget_container = QWidget() button_widget_container.setLayout(button_layout) layout = QVBoxLayout() layout.addWidget(self._total_progress_label) layout.addWidget(self._total_progress_bar) layout.addWidget(self._iteration_progress_label) layout.addWidget(self._progress_view) layout.addWidget(legend_view) layout.addWidget(self._tab_widget) layout.addWidget(self._job_label) layout.addWidget(self._job_view) layout.addWidget(button_widget_container) self.setLayout(layout) self.kill_button.clicked.connect(self.killJobs) self.done_button.clicked.connect(self.accept) self.restart_button.clicked.connect(self.restart_failed_realizations) self.show_details_button.clicked.connect(self.toggle_detailed_progress) self.simulation_done.connect(self._on_simulation_done) self.setMinimumWidth(self._minimum_width) self._setSimpleDialog() def _setSimpleDialog(self) -> None: self._isDetailedDialog = False self._tab_widget.setVisible(False) self._job_label.setVisible(False) self._job_view.setVisible(False) self.show_details_button.setText("Show Details") def _setDetailedDialog(self) -> None: self._isDetailedDialog = True self._tab_widget.setVisible(True) self._job_label.setVisible(True) self._job_view.setVisible(True) self.show_details_button.setText("Hide Details") @Slot(QModelIndex, int, int) def on_new_iteration(self, parent: QModelIndex, start: int, end: int) -> None: if not parent.isValid(): iter = start self._iteration_progress_label.setText( f"Progress for iteration {iter}") widget = RealizationWidget(iter) widget.setSnapshotModel(self._snapshot_model) widget.currentChanged.connect(self._select_real) self._tab_widget.addTab(widget, f"Realizations for iteration {iter}") @Slot(QModelIndex) def _job_clicked(self, index): if not index.isValid(): return selected_file = index.data(FileRole) if selected_file and selected_file not in self._open_files: job_name = index.siblingAtColumn(0).data() viewer = FileDialog( selected_file, job_name, index.row(), index.model().get_real(), index.model().get_iter(), self, ) self._open_files[selected_file] = viewer viewer.finished.connect( lambda _, f=selected_file: self._open_files.pop(f)) elif selected_file in self._open_files: self._open_files[selected_file].raise_() @Slot(QModelIndex) def _select_real(self, index): step = 0 stage = 0 real = index.row() iter_ = index.model().get_iter() self._job_model.set_step(iter_, real, stage, step) self._job_label.setText( f"Realization id {index.data(RealIens)} in iteration {iter_}") self._job_view.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) # Clear the selection in the other tabs for i in range(0, self._tab_widget.count()): if i != self._tab_widget.currentIndex(): self._tab_widget.widget(i).clearSelection() def reject(self): return def closeEvent(self, QCloseEvent): if self._run_model.isFinished(): self.simulation_done.emit(self._run_model.hasRunFailed(), self._run_model.getFailMessage()) else: # Kill jobs if dialog is closed if self.killJobs() != QMessageBox.Yes: QCloseEvent.ignore() def startSimulation(self): self._run_model.reset() self._snapshot_model.reset() self._tab_widget.clear() def run(): asyncio.set_event_loop(asyncio.new_event_loop()) self._run_model.startSimulations(self._simulations_argments) simulation_thread = Thread(name="ert_gui_simulation_thread") simulation_thread.setDaemon(True) simulation_thread.run = run simulation_thread.start() self._ticker.start(1000) tracker = create_tracker( self._run_model, num_realizations=self._simulations_argments["active_realizations"]. count(), ee_config=self._simulations_argments.get("ee_config", None), ) worker = TrackerWorker(tracker) worker_thread = QThread() worker.done.connect(worker_thread.quit) worker.consumed_event.connect(self._on_tracker_event) worker.moveToThread(worker_thread) self.simulation_done.connect(worker.stop) self._worker = worker self._worker_thread = worker_thread worker_thread.started.connect(worker.consume_and_emit) self._worker_thread.start() def killJobs(self): msg = "Are you sure you want to kill the currently running simulations?" if self._run_model.getQueueStatus().get( JobStatusType.JOB_QUEUE_UNKNOWN, 0) > 0: msg += "\n\nKilling a simulation with unknown status will not kill the realizations already submitted!" kill_job = QMessageBox.question(self, "Kill simulations?", msg, QMessageBox.Yes | QMessageBox.No) if kill_job == QMessageBox.Yes: # Normally this slot would be invoked by the signal/slot system, # but the worker is busy tracking the evaluation. self._worker.request_termination() self.reject() return kill_job @Slot(bool, str) def _on_simulation_done(self, failed, failed_msg): self.processing_animation.hide() self.kill_button.setHidden(True) self.done_button.setHidden(False) self.restart_button.setVisible(self.has_failed_realizations()) self.restart_button.setEnabled(self._run_model.support_restart) self._total_progress_bar.setValue(100) self._total_progress_label.setText( _TOTAL_PROGRESS_TEMPLATE.format(100)) if failed: QMessageBox.critical( self, "Simulations failed!", f"The simulation failed with the following error:\n\n{failed_msg}", ) @Slot() def _on_ticker(self): runtime = self._run_model.get_runtime() self.running_time.setText(format_running_time(runtime)) @Slot(object) def _on_tracker_event(self, event): if isinstance(event, EndEvent): self.simulation_done.emit(event.failed, event.failed_msg) self._worker.stop() self._ticker.stop() elif isinstance(event, FullSnapshotEvent): if event.snapshot is not None: self._snapshot_model._add_snapshot(event.snapshot, event.iteration) self._progress_view.setIndeterminate(event.indeterminate) progress = int(event.progress * 100) self._total_progress_bar.setValue(progress) self._total_progress_label.setText( _TOTAL_PROGRESS_TEMPLATE.format(progress)) elif isinstance(event, SnapshotUpdateEvent): if event.partial_snapshot is not None: self._snapshot_model._add_partial_snapshot( event.partial_snapshot, event.iteration) self._progress_view.setIndeterminate(event.indeterminate) progress = int(event.progress * 100) self._total_progress_bar.setValue(progress) self._total_progress_label.setText( _TOTAL_PROGRESS_TEMPLATE.format(progress)) def has_failed_realizations(self): completed = self._run_model.completed_realizations_mask initial = self._run_model.initial_realizations_mask for (index, successful) in enumerate(completed): if initial[index] and not successful: return True return False def count_successful_realizations(self): """ Counts the realizations completed in the prevoius ensemble run :return: """ completed = self._run_model.completed_realizations_mask return completed.count(True) def create_mask_from_failed_realizations(self): """ Creates a BoolVector mask representing the failed realizations :return: Type BoolVector """ completed = self._run_model.completed_realizations_mask initial = self._run_model.initial_realizations_mask inverted_mask = BoolVector(default_value=False) for (index, successful) in enumerate(completed): inverted_mask[index] = initial[index] and not successful return inverted_mask def restart_failed_realizations(self): msg = QMessageBox(self) msg.setIcon(QMessageBox.Information) msg.setText( "Note that workflows will only be executed on the restarted realizations and that this might have unexpected consequences." ) msg.setWindowTitle("Restart Failed Realizations") msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) result = msg.exec_() if result == QMessageBox.Ok: self.restart_button.setVisible(False) self.kill_button.setVisible(True) self.done_button.setVisible(False) active_realizations = self.create_mask_from_failed_realizations() self._simulations_argments[ "active_realizations"] = active_realizations self._simulations_argments[ "prev_successful_realizations"] = self._simulations_argments.get( "prev_successful_realizations", 0) self._simulations_argments[ "prev_successful_realizations"] += self.count_successful_realizations( ) self.startSimulation() @Slot() def toggle_detailed_progress(self): if self._isDetailedDialog: self._setSimpleDialog() else: self._setDetailedDialog() self.adjustSize()
class ProgressView(QWidget): """ :type batch_manager: CalculationManager """ def __init__(self, parent, batch_manager): QWidget.__init__(self, parent) self.calculation_manager = batch_manager self.whole_progress = QProgressBar(self) self.whole_progress.setMinimum(0) self.whole_progress.setMaximum(1) self.whole_progress.setFormat("%v of %m") self.whole_progress.setTextVisible(True) self.part_progress = QProgressBar(self) self.part_progress.setMinimum(0) self.part_progress.setMaximum(1) self.part_progress.setFormat("%v of %m") self.whole_label = QLabel("All batch progress:", self) self.part_label = QLabel("Single batch progress:", self) self.logs = ExceptionList(self) self.logs.setToolTip("Logs") self.task_que = QListWidget() self.process_num_timer = QTimer() self.process_num_timer.setInterval(1000) self.process_num_timer.setSingleShot(True) self.process_num_timer.timeout.connect(self.change_number_of_workers) self.number_of_process = QSpinBox(self) self.number_of_process.setRange(1, multiprocessing.cpu_count()) self.number_of_process.setValue(1) self.number_of_process.setToolTip( "Number of process used in batch calculation") self.number_of_process.valueChanged.connect( self.process_num_timer_start) layout = QGridLayout() layout.addWidget(self.whole_label, 0, 0, Qt.AlignRight) layout.addWidget(self.whole_progress, 0, 1, 1, 2) layout.addWidget(self.part_label, 1, 0, Qt.AlignRight) layout.addWidget(self.part_progress, 1, 1, 1, 2) lab = QLabel("Number of process:") lab.setToolTip("Number of process used in batch calculation") layout.addWidget(lab, 2, 0) layout.addWidget(self.number_of_process, 2, 1) layout.addWidget(self.logs, 3, 0, 1, 3) layout.addWidget(self.task_que, 0, 4, 0, 1) layout.setColumnMinimumWidth(2, 10) layout.setColumnStretch(2, 1) self.setLayout(layout) self.preview_timer = QTimer() self.preview_timer.setInterval(1000) self.preview_timer.timeout.connect(self.update_info) def new_task(self): self.whole_progress.setMaximum( self.calculation_manager.calculation_size) if not self.preview_timer.isActive(): self.update_info() self.preview_timer.start() def update_info(self): res = self.calculation_manager.get_results() for el in res.errors: if el[0]: QListWidgetItem(el[0], self.logs) ExceptionListItem(el[1], self.logs) if (state_store.report_errors and parsed_version.is_devrelease and not isinstance(el[1][0], SegmentationLimitException) and isinstance(el[1][1], tuple)): with sentry_sdk.push_scope() as scope: scope.set_tag("auto_report", "true") sentry_sdk.capture_event(el[1][1][0]) self.whole_progress.setValue(res.global_counter) working_search = True for i, (progress, total) in enumerate(res.jobs_status): if working_search and progress != total: self.part_progress.setMaximum(total) self.part_progress.setValue(progress) working_search = False if i < self.task_que.count(): item = self.task_que.item(i) item.setText("Task {} ({}/{})".format(i, progress, total)) else: self.task_que.addItem("Task {} ({}/{})".format( i, progress, total)) if not self.calculation_manager.has_work: print( "[ProgressView.update_info]", self.calculation_manager.has_work, self.calculation_manager.batch_manager.has_work, self.calculation_manager.writer.writing_finished(), ) self.part_progress.setValue(self.part_progress.maximum()) self.preview_timer.stop() logging.info("Progress stop") def process_num_timer_start(self): self.process_num_timer.start() def update_progress(self, total_progress, part_progress): self.whole_progress.setValue(total_progress) self.part_progress.setValue(part_progress) def set_total_size(self, size): self.whole_progress.setMaximum(size) def set_part_size(self, size): self.part_progress.setMaximum(size) def change_number_of_workers(self): self.calculation_manager.set_number_of_workers( self.number_of_process.value())
class AbortWindow(QDialog): """ Displays busy message and provides abort button. The class serves SmoothCube, WorkerThread and SelectSmoothing. """ def __init__(self, parent=None): """ init abort or notification ui. Displays while smoothing freezes the application. Allows abort button to be added if needed. """ super(AbortWindow, self).__init__(parent) self.setModal(False) self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) self.parent = parent self.label_a_1 = QLabel("Executing smoothing algorithm.") self.label_a_2 = QLabel("This may take several minutes.") self.abort_button = QPushButton("Abort") self.abort_button.clicked.connect(self.abort) self.pb = QProgressBar(self) self.pb_counter = 0 self.abort_flag = False self.info_box = None # vbl is short for Vertical Box Layout vbl = QVBoxLayout() vbl.addWidget(self.label_a_1) vbl.addWidget(self.label_a_2) vbl.addWidget(self.pb) vbl.addWidget(self.abort_button) self.setLayout(vbl) self.show() def init_pb(self, start, end): """ Init the progress bar :param start: Start Value :param end: End Value """ self.pb.setRange(start, end) self.pb_counter = start def update_pb(self): """ This function is called in the worker thread to update the progress bar and checks if the local class variable abort_flag is active. If the abort button is clicked, the main thread will set abort_flag to True. The next time the worker thread calls this function, a custom exception is raised terminating the calculation. The exception is handled by the WorkerThread class. :raises: AbortException: terminating smoothing calculation """ if self.abort_flag: raise AbortException("Abort Calculation") self.pb_counter += 1 self.pb.setValue(self.pb_counter) QApplication.processEvents() def abort(self): """Abort calculation""" self.abort_flag = True self.parent.clean_up() def show_error_message(self, message, title, parent=None): self.info_box = QMessageBox(parent=parent) self.info_box.setIcon(QMessageBox.Information) self.info_box.setText(message) self.info_box.setWindowTitle(title) self.info_box.setStandardButtons(QMessageBox.Ok) self.info_box.show() def smoothing_done(self, component_id=None): """Notify user success""" self.hide() if component_id is None: message = "The result has been added as a" \ " new component of the input Data." \ " The new component can be accessed" \ " in the viewer drop-downs." else: message = "The result has been added as" \ " \"{0}\" and can be selected" \ " in the viewer drop-down menu.".format(component_id) self.show_error_message(message, "Success", self) self.clean_up() def print_error(self, exception): """Print error message""" if "signal only works in main thread" in str(exception): message = "Smoothing Failed!\n\n" + "Please update your SpectralCube package" else: message = "Smoothing Failed!\n\n" + str(exception) self.show_error_message(message, "Error", self) self.clean_up() def clean_up(self): self.parent.clean_up() def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.abort()
class MainDialog(DialogBase): """Main dialog for the anaconda navgator updater.""" # Signals sig_application_updated = Signal() sig_ready = Signal() # Class variables PACKAGE = 'anaconda-navigator' WIDTH = 450 HEIGHT = 200 def __init__(self, latest_version=None, prefix=None): """Main dialog for the anaconda navgator updater.""" super(MainDialog, self).__init__() # Variables self.api = CondaAPI() self.prefix = prefix or os.environ.get('CONDA_PREFIX', self.api.ROOT_PREFIX) self.info = {} self.first_run = True self.setup_ready = False self.busy = False self.up_to_date = False self.error = False self.success = False self.status = '' self.current_version = None self.latest_version = latest_version self.style_sheet = load_style_sheet() self.timer = QTimer() self.timer_2 = QTimer() self._windows_appusermodelid = None # Widgets self.message_box = None # For testing self.label_icon = QSvgWidget() self.label_message = LabelBase( "There's a new version of Anaconda Navigator available. " "We strongly recommend you to update.") self.label_status = LabelBase('') self.progress_bar = QProgressBar() self.button_cancel = ButtonNormal('Dismiss') self.button_update = ButtonPrimary('Update now') self.button_launch = ButtonPrimary('Launch Navigator') # Widgets setup if WIN: self._windows_appusermodelid = set_windows_appusermodelid() self.setMinimumSize(self.WIDTH, self.HEIGHT) self.label_message.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.label_message.setWordWrap(True) self.label_status.setWordWrap(True) self.button_update.setAutoDefault(True) self.button_launch.setAutoDefault(True) self.button_cancel.setFocusPolicy(Qt.NoFocus) self.timer.setInterval(1000) self.timer_2.setInterval(5000) self.progress_bar.setTextVisible(False) self.progress_bar.setStyleSheet(self.style_sheet) self.label_icon.load(images.ANACONDA_LOGO) self.label_icon.setMaximumSize(QSize(64, 64)) self.label_icon.setMinimumSize(QSize(64, 64)) self.setWindowTitle('Anaconda Navigator Updater') self.progress_bar.setMaximumWidth(self.WIDTH / 3) self.setMinimumWidth(self.WIDTH) self.setMaximumWidth(self.WIDTH) self.setMinimumHeight(self.HEIGHT) # Layouts layout_status = QHBoxLayout() layout_status.addWidget(self.label_status) layout_status.addWidget(SpacerHorizontal()) layout_status.addWidget(self.progress_bar) layout_text = QVBoxLayout() layout_text.addWidget(self.label_message) layout_text.addStretch() layout_text.addWidget(SpacerVertical()) layout_text.addLayout(layout_status) layout_icon = QVBoxLayout() layout_icon.addWidget(self.label_icon) layout_icon.addStretch() layout_top = QHBoxLayout() layout_top.addLayout(layout_icon) layout_top.addWidget(SpacerHorizontal()) layout_top.addLayout(layout_text) layout_buttons = QHBoxLayout() layout_buttons.addStretch() layout_buttons.addWidget(self.button_cancel) layout_buttons.addWidget(SpacerHorizontal()) layout_buttons.addWidget(self.button_update) layout_buttons.addWidget(self.button_launch) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addWidget(SpacerVertical()) layout.addWidget(SpacerVertical()) layout.addStretch() layout.addLayout(layout_buttons) self.setLayout(layout) # Signals self.button_update.clicked.connect(self.install_update) self.button_cancel.clicked.connect(self.reject) self.button_launch.clicked.connect(self.launch) self.timer.timeout.connect(self.refresh) self.timer_2.timeout.connect(self.check_conditions) # Setup self.timer.start() self.timer_2.start() self.check_conditions() self.refresh() def check_conditions(self): """Check every 5 seconds installed packages in case codna was used.""" packages = self.api.linked(prefix=self.prefix) package = [p for p in packages if self.PACKAGE in p] if package: n, v, b = self.api.split_canonical_name(package[0]) self.current_version = v else: self.current_version = None if self.latest_version is None: worker_search = self.api.search(self.PACKAGE, platform=self.api.get_platform()) worker_search.sig_finished.connect(self._search_callback) else: worker = self.api.info() worker.sig_finished.connect(self.setup) self.check_versions() def check_versions(self): """Check if navigator is up to date.""" if self.latest_version and self.current_version: from distutils.version import LooseVersion cur_ver = LooseVersion(self.current_version) lat_ver = LooseVersion(self.latest_version) self.up_to_date = cur_ver >= lat_ver else: self.up_to_date = False def _search_callback(self, worker, output, error): """Setup the widget.""" if isinstance(output, dict): packages = output.get(self.PACKAGE, []) versions = [package.get('version') for package in packages] unique_versions = [] for version in versions: if version not in unique_versions: unique_versions.append(version) if unique_versions: self.latest_version = unique_versions[-1] self.check_versions() worker = self.api.info() worker.sig_finished.connect(self.setup) self.refresh() def setup(self, worker, info, error): """Setup the widget.""" self.info = info self.setup_ready = True self.sig_ready.emit() self.refresh() if self.button_update.isVisible(): self.button_update.setFocus() if self.button_launch.isVisible(): self.button_launch.setFocus() def update_style_sheet(self): """Update custom CSS style sheet.""" self.style_sheet = load_style_sheet() self.setStyleSheet(self.style_sheet) def refresh(self): """Refresh enabled/disabled status of widgets.""" current_version = 'Not installed' if self.current_version: current_version = self.current_version latest_version = '-' if self.latest_version: latest_version = self.latest_version main_message = ( "Current version: <i>{0}</i><br>" "Available version: <b>{1}</b><br>").format( current_version, latest_version) message = self.status running = self.check_running() self.button_launch.setVisible(False) if not self.setup_ready: self.button_update.setDisabled(True) self.progress_bar.setVisible(True) message = 'Updating index...' self.update_status(message) elif self.busy: self.button_update.setDisabled(True) self.progress_bar.setVisible(True) else: self.progress_bar.setVisible(False) if running: message = 'Please close Anaconda Navigator before updating.' self.button_update.setDisabled(running) elif not running: self.button_update.setDisabled(False) if self.success and self.current_version: message = 'Anaconda Navigator was updated successfully.' self.button_update.setVisible(False) self.button_launch.setVisible(True) elif self.up_to_date: message = 'Anaconda Navigator is already up to date.' self.button_update.setVisible(False) self.button_launch.setVisible(True) elif not self.error: self.button_update.setVisible(True) if self.current_version: message = ('An update for Anaconda Navigator is now ' 'available.') self.button_update.setText('Update now') else: message = ( 'Anaconda Navigator is available for install.') self.button_update.setText('Install now') if self.error: self.button_update.setDisabled(False) message = 'Cannot update Anaconda Navigator, <b>{0}</b>' message = message.format(self.error) self.label_status.setText(message) self.label_message.setText(main_message) def update_status(self, status='', value=-1, max_val=-1): """Update progress bar and message status.""" if status: self.status = status self.label_status.setText(status) if value < 0 and max_val < 0: self.progress_bar.setRange(0, 0) else: self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(max_val) self.progress_bar.setValue(value) def check_running(self): """Check if Anaconda Navigator is running.""" # Create file lock lock = filelock.FileLock(NAVIGATOR_LOCKFILE) try: running = False with lock.acquire(timeout=0.01): pass except filelock.Timeout: running = True return running # --- Conda actions and helpers # ------------------------------------------------------------------------- def partial_output_ready(self, worker, output, error): """Handle conda partial output ready.""" self.busy = True # print(type(output)) # print(output) # Get errors and data from ouput if it exists fetch = None if output and isinstance(output, dict): fetch = output.get('fetch') max_val = output.get('maxval', -1) value = output.get('progress', -1) if fetch: status = 'Fetching <b>{0}</b>...'.format(fetch) self.update_status(status=status, max_val=max_val, value=value) def output_ready(self, worker, output, error): """Handle conda output ready.""" self.check_conditions() # Get errors and data from ouput if it exists error_text = output.get('error', '') exception_type = output.get('exception_type', '') exception_name = output.get('exception_name', '') success = output.get('success') actions = output.get('actions', {}) # op_order = output.get('op_order', []) # action_check_fetch = actions.get('CHECK_FETCH', []) # action_rm_fetch = actions.get('RM_FETCHED', []) # action_fetch = actions.get('FETCH', []) # action_check_extract = actions.get('CHECK_EXTRACT', []) # action_rm_extract = actions.get('RM_EXTRACTED', []) # action_extract = actions.get('EXTRACT', []) # action_unlink = actions.get('UNLINK', []) action_link = actions.get('LINK', []) # action_symlink_conda = actions.get('SYMLINK_CONDA', []) self.busy = False # Get errors from json output if error_text or exception_type or exception_name or not success: self.error = exception_name self.success = False self.up_to_date = False elif success and action_link: self.sig_application_updated.emit() self.error = None self.success = True self.up_to_date = False elif success: self.success = False self.error = None self.up_to_date = True worker.lock.release() self.refresh() def install_update(self): """Install the specified version or latest version of navigator.""" self.busy = True self.refresh() # conda_prefix = self.info.et('conda_prefix') # root_prefix = self.info.et('root_prefix') navigator_prefixes = [ # os.path.join(self.api.ROOT_PREFIX, 'envs', '_navigator_'), # os.path.join(self.api.ROOT_PREFIX, 'envs', '_conda_'), self.prefix, ] for prefix in navigator_prefixes: if self.api.environment_exists(prefix=prefix): break if self.latest_version: pkgs = ['{0}=={1}'.format(self.PACKAGE, self.latest_version)] else: pkgs = [self.PACKAGE.format(self.latest_version)] # Lock Navigator lock = filelock.FileLock(NAVIGATOR_LOCKFILE) lock.acquire() worker = self.api.install(prefix=prefix, pkgs=pkgs) worker.lock = lock worker.sig_partial.connect(self.partial_output_ready) worker.sig_finished.connect(self.output_ready) self.refresh() if self.prefix == self.api.ROOT_PREFIX: name = 'root' else: name = os.path.basename(self.prefix) self.button_launch.setFocus() if self.current_version: msg = 'Updating package on <b>{0}</b>...'.format(name) else: msg = 'Installing package on <b>{0}</b>...'.format(name) self.update_status(msg) def launch(self): """Launch Anaconda Navigator.""" leave_path_alone = True root_prefix = self.api.ROOT_PREFIX prefix = self.prefix command = ['anaconda-navigator'] # Use the app bundle on OSX if MAC: command = ['open', os.path.join(prefix, 'Anaconda-Navigator.app')] launch_cmd( prefix, command, leave_path_alone, package_name='anaconda-navigator-app', root_prefix=root_prefix, ) self.close() # --- Qt Overrides # ------------------------------------------------------------------------- def reject(self): """Override Qt method.""" if self.busy: msg_box = MessageBoxQuestion(title='Quit Navigator Updater?', text='Anaconda Navigator is being ' 'updated. <br><br>' 'Are you sure you want to quit?') if msg_box.exec_(): super(MainDialog, self).reject() else: super(MainDialog, self).reject()
class ProgressMonitor(QMainWindow): # def __init__(self, log_file, windows_title, percentage_pattern): super(ProgressMonitor, self).__init__() self.setGeometry(50, 50, 500, 100) self.setWindowTitle(windows_title) self.setWindowIcon(QtGui.QIcon("pythonlogo.png")) closeAction = QAction("close", self) closeAction.setShortcut("Ctrl+Q") closeAction.setStatusTip("close this window") closeAction.triggered.connect(self.close_application) self.polling_interval_in_seconds = 2 self.elapsed_time = 0.0 self.elapsed_time_label = QLabel() self.elapsed_time_label.setText("elapsed time: 0 second") self.progress_bar = QProgressBar(self) # self.progress_bar.resize(400, 25) # self.progress_bar.setGeometry(200, 50, 250, 20) self.progress = 0.0 # 100 as completion self.last_line_number = 0 self.percentage_pattern = percentage_pattern killbtn = QPushButton("Interrupt", self) killbtn.clicked.connect(self.interrupt_application) btn = QPushButton("Quit", self) btn.clicked.connect(self.close_application) btn.resize(btn.minimumSizeHint()) hbox = QHBoxLayout() hbox.addWidget(killbtn) hbox.addStretch() hbox.addWidget(btn) vbox = QVBoxLayout() vbox.addWidget(self.elapsed_time_label) vbox.addWidget(self.progress_bar) vbox.addLayout(hbox) widget = QWidget(self) widget.setLayout(vbox) self.setCentralWidget(widget) self.setup_monitor(log_file) def setup_monitor(self, log_file): # if string type, and os.path.exists() self.log_file = log_file self.monitor_timer = QTimer(self) self.monitor_timer.start(self.polling_interval_in_seconds * 1000) self.monitor_timer.timeout.connect(self.monitor) def get_lines(self): new_lines = [] with open(self.log_file, "r+") as f: # fcntl.flock(f, fcntl.LOCK_EX) # make no difference all_lines = f.readlines() # fcntl.flock(f, fcntl.LOCK_UN) new_line_number = len(all_lines) if new_line_number > self.last_line_number: new_lines = all_lines[self.last_line_number :] self.last_line_number = new_line_number # print(all_lines, new_lines) return new_lines def get_lines_pipe(self): # also work working cmdline = ["tail", self.log_file] p = subprocess.Popen(cmdline, stdout=subprocess.PIPE) out = p.communicate()[0] new_lines = out.decode("ascii").split("\n") print(new_lines[-1]) return new_lines def monitor(self): # new_lines = self.get_lines_pipe() new_lines = self.get_lines() self.progress = self.parse(new_lines) if self.progress >= 100: self.progress = 100 sys.exit() # auto close self.elapsed_time += self.polling_interval_in_seconds self.elapsed_time_label.setText( "elapsed time: " + str(self.elapsed_time) + " seconds" ) self.progress_bar.setValue(self.progress) def parse(self, new_lines): p = self.progress for l in reversed(new_lines): result = re.search(self.percentage_pattern, l) if result: match_float = result.group(1) # not a list if len(match_float) > 1: p = float(match_float) # print(l, p) if p >= self.progress: return p return self.progress def interrupt_application(self): sys.exit() def close_application(self): sys.exit()
class DialogChannels(DialogBase): """Dialog to add delete and select active conda package channels.""" sig_channels_updated = Signal(object, object) # added, removed sig_setup_ready = Signal() sig_check_ready = Signal() WIDTH = 550 def __init__(self, parent=None): """Dialog to add delete and select active conda pacakge channels .""" super(DialogChannels, self).__init__(parent) self._parent = parent self._conda_url = 'https://conda.anaconda.org' self.api = AnacondaAPI() self.initial_sources = None self.config_sources = None self.style_sheet = None self._setup_ready = False self._conda_url_setup_ready = False # Widgets self.list = ListWidgetChannels(parent=self, api=self.api) self.label_info = LabelBase( 'Manage channels you want Navigator to include.') self.label_status = LabelBase('Collecting sources...') self.progress_bar = QProgressBar(self) self.button_add = ButtonNormal('Add...') self.button_cancel = ButtonNormal('Cancel') self.button_ok = ButtonPrimary('Update channels') # Widget setup self.frame_title_bar.setVisible(False) self.list.setFrameStyle(QFrame.NoFrame) self.list.setFrameShape(QFrame.NoFrame) self.setWindowFlags(self.windowFlags() | Qt.Popup) self.setWindowOpacity(0.96) self.setMinimumHeight(300) self.setMinimumWidth(self.WIDTH) self.setModal(True) # Layout layout_button = QHBoxLayout() layout_button.addWidget(self.label_info) layout_button.addStretch() layout_button.addWidget(self.button_add) layout_ok = QHBoxLayout() layout_ok.addWidget(self.label_status) layout_ok.addWidget(SpacerHorizontal()) layout_ok.addWidget(self.progress_bar) layout_ok.addWidget(SpacerHorizontal()) layout_ok.addStretch() layout_ok.addWidget(self.button_cancel) layout_ok.addWidget(SpacerHorizontal()) layout_ok.addWidget(self.button_ok) layout = QVBoxLayout() layout.addLayout(layout_button) layout.addWidget(SpacerVertical()) layout.addWidget(self.list) layout.addWidget(SpacerVertical()) layout.addWidget(SpacerVertical()) layout.addLayout(layout_ok) self.setLayout(layout) # Signals self.button_add.clicked.connect(self.add_channel) self.button_ok.clicked.connect(self.update_channels) self.button_cancel.clicked.connect(self.reject) self.list.sig_status_updated.connect(self.update_status) self.list.sig_channel_added.connect( lambda v=None: self.set_tab_order()) self.list.sig_channel_added.connect( lambda v=None: self.button_ok.setFocus()) self.list.sig_channel_removed.connect( lambda v=None: self.set_tab_order()) self.list.sig_channel_removed.connect( lambda v=None: self.button_ok.setFocus()) self.list.sig_channel_checked.connect(self.sig_check_ready) self.list.sig_channel_status.connect(self.refresh) self.button_add.setDisabled(True) self.button_ok.setDisabled(True) self.button_cancel.setDisabled(True) self.update_status(action='Collecting sources...', value=0, max_value=0) @staticmethod def _group_sources_and_channels(sources): """ Flatten sources and channels dictionary to list of tuples. [(source, channel), (source, channel)...] """ grouped = [] for source, channels in sources.items(): for channel in channels: grouped.append((source, channel)) return grouped def keyPressEvent(self, event): """Override Qt method.""" key = event.key() if key in [Qt.Key_Escape]: if self.list.is_editing: self.refresh() self.list.is_editing = False else: self.reject() # --- Public API # ------------------------------------------------------------------------- def update_style_sheet(self, style_sheet=None): """Update custom css style sheets.""" if style_sheet is None: self.style_sheet = load_style_sheet() else: self.style_sheet = style_sheet self.setStyleSheet(self.style_sheet) self.setMinimumWidth(SASS_VARIABLES.WIDGET_CHANNEL_DIALOG_WIDTH) try: self.list.update_style_sheet(style_sheet) except Exception: pass def update_api(self, worker, api_info, error): """Update api info.""" self._conda_url = api_info.get('conda_url', 'https://conda.anaconda.org') self._conda_url_setup_ready = True if self._setup_ready: self.sig_setup_ready.emit() def setup(self, worker, conda_config_data, error): """Setup the channels widget.""" self.config_sources = conda_config_data.get('config_sources') self.button_add.setDisabled(False) for source, data in self.config_sources.items(): channels = data.get('channels', []) for channel in channels: item = ListWidgetItemChannel(channel=channel, location=source) item.set_editable(False) self.list.addItem(item) self.set_tab_order() self.button_add.setFocus() self.button_ok.setDefault(True) self.button_cancel.setEnabled(True) self.initial_sources = self.list.sources.copy() self.update_status() self._setup_ready = True if self._conda_url_setup_ready: self.sig_setup_ready.emit() def set_tab_order(self): """Fix the tab ordering in the list.""" if self.list._items: self.setTabOrder(self.button_add, self.list._items[0].button_remove) self.setTabOrder(self.list._items[-1].button_remove, self.button_cancel) self.setTabOrder(self.button_cancel, self.button_ok) self.refresh() def add_channel(self): """Add new conda channel.""" user_rc_path = self.api._conda_api.user_rc_path item = ListWidgetItemChannel(channel='', location=user_rc_path) self.list.addItem(item) self.refresh(False) def update_channels(self): """Update channels list and status.""" sources = self.list.sources original = self._group_sources_and_channels(self.initial_sources) updated = self._group_sources_and_channels(sources) if sorted(original) != sorted(updated): self.sig_channels_updated.emit(*self.sources) self.accept() else: self.reject() def refresh(self, channel_status=True): """Update enable/disable status based on item count.""" self.button_add.setEnabled(channel_status and bool(self.list.count)) self.button_ok.setEnabled(channel_status) self.button_cancel.setEnabled(True) if self.list.count() == 0: self.button_add.setEnabled(True) self.button_ok.setEnabled(False) def update_status(self, action='', message='', value=None, max_value=None): """Update the status and progress bar of the widget.""" visible = bool(action) self.label_status.setText(action) self.label_status.setVisible(visible) if value is not None and max_value is not None: self.progress_bar.setVisible(True) self.progress_bar.setRange(0, max_value) self.progress_bar.setValue(value) else: self.progress_bar.setVisible(False) @property def sources(self): """Return sources to add and remove from config.""" original = self._group_sources_and_channels(self.initial_sources) updated = self._group_sources_and_channels(self.list.sources) original = set(original) updated = set(updated) add = updated - original remove = original - updated return add, remove
class MainWindow(QMainWindow): sig_logged_in = Signal() sig_logged_out = Signal() DOCS_URL = 'https://docs.continuum.io/anaconda/navigator' VIDEOS_URL = "http://content.continuum.io/api/videos" EVENTS_URL = "http://content.continuum.io/api/events" WEBINARS_URL = "http://content.continuum.io/api/webinars" def __init__(self, splash=None): super(MainWindow, self).__init__() self.tracker = None self.splash = splash # Anaconda API self.api = AnacondaAPI() self.busy = False self.logged = False self.username = '' self._login_text = 'Sign in to Anaconda Cloud' self.first_run = CONF.get('main', 'first_run') self.application_update_version = None # Widgets self.frame_header = FrameHeader(self) self.frame_body = FrameBody(self) self.label_logo = LabelHeaderLogo('ANACONDA NAVIGATOR') self.button_logged_text = ButtonLabelLogin('') self.button_logged_username = ButtonLinkLogin('') self.label_update_available = LabelHeaderUpdate('Update available!') self.button_update_available = ButtonHeaderUpdate('Update') self.button_login = ButtonLogin(self._login_text) self.central_widget = QWidget() self.statusbar = self.statusBar() self.progressbar = QProgressBar() self.stack = TabWidgetBody(self) self.home_tab = HomeTab(parent=self) self.environments_tab = EnvironmentsTab(parent=self) self.learning_tab = CommunityTab( parent=self, tags=['webinar', 'documentation', 'video', 'training'], content_urls=[self.VIDEOS_URL, self.WEBINARS_URL]) self.community_tab = CommunityTab(parent=self, tags=['event', 'forum', 'social'], content_urls=[self.EVENTS_URL]) # self.projects_tab = ProjectsTab(parent=self) # Note: Icons are set in CSS self.stack.addTab(self.home_tab, text='Home') self.stack.addTab(self.environments_tab, text='Environments') self.stack.addTab(self.learning_tab, text='Learning') self.stack.addTab(self.community_tab, text='Community') # self.stack.addTab(self.projects_tab, 'Projects') # Widget setup self.button_login.setDefault(True) self.label_logo.setPixmap(QPixmap(images.ANACONDA_NAVIGATOR_LOGO)) self.setWindowTitle("Anaconda Navigator") self.statusbar.addPermanentWidget(self.progressbar) self.progressbar.setVisible(False) # Layout header_layout = QHBoxLayout() header_layout.addWidget(self.label_logo) header_layout.addSpacing(18) header_layout.addWidget(self.label_update_available, 0, Qt.AlignCenter) header_layout.addWidget(self.button_update_available, 0, Qt.AlignCenter) header_layout.addStretch() header_layout.addWidget(self.button_logged_text, 0, Qt.AlignTrailing) header_layout.addWidget(self.button_logged_username, 0, Qt.AlignTrailing) header_layout.addWidget(self.button_login, 0, Qt.AlignTrailing) header_layout.setContentsMargins(0, 0, 0, 0) self.frame_header.setLayout(header_layout) body_layout = QHBoxLayout() body_layout.addWidget(self.stack) body_layout.setContentsMargins(0, 0, 0, 0) self.frame_body.setLayout(body_layout) main_layout = QVBoxLayout() main_layout.addWidget(self.frame_header) main_layout.addWidget(self.frame_body) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) self.central_widget.setLayout(main_layout) self.setContentsMargins(0, 0, 0, 0) self.setCentralWidget(self.central_widget) # Signals self.button_login.clicked.connect(self.login) self.button_logged_username.clicked.connect(self.open_login_page) self.button_update_available.clicked.connect(self.update_application) self.stack.currentChanged.connect(self._track_tab) # This needs to be reworked! # self.projects_tab.sig_apps_updated.connect( # self.home_tab.set_applications) # self.projects_tab.sig_apps_changed.connect( # self.home_tab.set_applications) # self.projects_tab.sig_project_updated.connect( # self.home_tab.set_applications) # self.projects_tab.sig_status_updated.connect(self.update_status_bar) # Setup self.api.set_data_directory(CHANNELS_PATH) self.update_style_sheet() # Helpers # ------------------------------------------------------------------------- def _track_tab(self, index=None): """ Tracks the active tab by index, or set `Home` when no index has been provided. """ if index is None: index = self.stack.currentIndex() text = self.stack.currentText().lower() if self.tracker: page = '/{0}'.format(text) self.tracker.track_page(page) def _metadata_updated(self, worker, path, error): self.set_splash('Updating repodata...') if error: logger.error(str(error)) if path and os.path.isfile(path): with open(path, 'r') as f: data = f.read() try: self._metadata = json.loads(data) except Exception: self._metadata = {} channels = CONF.get('main', 'conda_channels', default=tuple()) if not channels: channels = self.api.conda_get_condarc_channels() CONF.set('main', 'conda_channels', channels) CONF.set('main', 'conda_active_channels', channels) self.api.update_repodata(channels=channels) self.api.sig_repodata_updated.connect(self._repodata_updated) def _repodata_updated(self, paths): self.set_splash('Loading repodata...') self.api.sig_repodata_updated.disconnect(self._repodata_updated) if self.first_run: self.set_splash('Initial configuration...') self.api.create_default_project() worker = self.api.client_load_repodata(paths, self._metadata) worker.sig_finished.connect(self.create_application_projects) # --- Public API # ------------------------------------------------------------------------- def setup(self): """ Perform initial setup and configuration. """ self.set_splash('Updating metadata...') user = self.api.client_set_domain() self.update_login_status(user) self.setup_toolbars() self.set_application_icon() worker = self.api.update_metadata() worker.sig_finished.connect(self._metadata_updated) statusbar = self.statusBar() statusbar.setVisible(False) statusbar.setMaximumHeight(0) statusbar.hide() def create_application_projects(self, worker, output, error): if error: logger.error(str(error)) packages, apps = output self.api.create_application_projects( apps, add_project=self.first_run, ) self.post_setup(apps) self.check_for_updates(packages) def post_setup(self, apps): CONF.set('main', 'first_run', False) self.set_splash('Loading applications...') self.home_tab.setup_tab(apps) # self.set_splash('Loading projects...') # self.projects_tab.setup_tab() self.set_splash('Loading environments...') self.environments_tab.setup_tab(metadata=self._metadata) self.set_splash('Loading content...') self.community_tab.setup_tab() self.set_splash('Loading content...') self.learning_tab.setup_tab() self.update_style_sheet() self.showMaximized() self.post_visible_setup() def set_application_icon(self): """ """ app = QCoreApplication.instance() app_icon = QIcon() app_icon.addFile(images.ANACONDA_ICON_16_PATH, QSize(16, 16)) app_icon.addFile(images.ANACONDA_ICON_24_PATH, QSize(24, 24)) app_icon.addFile(images.ANACONDA_ICON_32_PATH, QSize(32, 32)) app_icon.addFile(images.ANACONDA_ICON_48_PATH, QSize(48, 48)) app_icon.addFile(images.ANACONDA_ICON_256_PATH, QSize(256, 256)) app.setWindowIcon(app_icon) def setup_toolbars(self): menubar = self.menuBar() file_menu = menubar.addMenu('&File') file_menu.addAction( create_action(self, "&Preferences", triggered=self.show_preferences, shortcut="Ctrl+P")) file_menu.addAction( create_action(self, "&Quit", triggered=self.close, shortcut="Ctrl+Q")) helpmenu = menubar.addMenu('&Help') helpmenu.addAction( create_action(self, "&Online Documentation", triggered=lambda: self.open_url(self.DOCS_URL))) helpmenu.addAction( create_action(self, "&Logs viewer", triggered=self.show_log_viewer, shortcut="F6")) helpmenu.addSeparator() helpmenu.addAction( create_action(self, "&About", triggered=self.show_about)) def post_visible_setup(self): if self.splash: self.splash.hide() CONF.set('main', 'first_run', False) # Start the tracker only after post_visible_setup self.tracker = GATracker() self._track_tab(0) # Start tracking home self.fix_tab_ordering() self.show_welcome_screen() def check_for_updates(self, packages=None): # Check if there is an update for navigator! version = self.api.conda_package_version(name='root', pkg='anaconda-navigator') # Temporal mock test # mock_versions = [version, '1.1.0'] # packages['anaconda-navigator'] = {'versions': mock_versions} self.button_update_available.setVisible(False) self.label_update_available.setVisible(False) text = '' if packages: package_data = packages.get('anaconda-navigator') if package_data: versions = package_data.get('versions') if versions and version != versions[-1]: self.application_update_version = versions[-1] self.label_update_available.setText(text) self.label_update_available.setVisible(True) self.button_update_available.setVisible(True) def fix_tab_ordering(self): return for tab in [self.community_tab, self.learning_tab]: self.setTabOrder(self.stack.tabbar.buttons[-1], tab.filter_widgets[0]) for i in range(len(tab.filter_widgets) - 1): self.setTabOrder(tab.filter_widgets[i], tab.filter_widgets[i + 1]) self.setTabOrder(tab.filter_widgets[-1], tab.text_filter) self.setTabOrder(tab.text_filter.button_icon, tab.list) self.setTabOrder(tab.list, self.button_login) # self.button_login.setFocus() self.setTabOrder(self.stack.tabbar.buttons[-1], self.environments_tab.text_search) self.environments_tab.packages_widget.table_last_row.add_focus_widget( self.button_login) self.setTabOrder(self.environments_tab.packages_widget.table_last_row, self.button_login) def update_style_sheet(self): style_sheet = load_style_sheet() # self.home_tab.update_style_sheet(style_sheet) self.environments_tab.update_style_sheet(style_sheet) # self.community_tab.update_style_sheet(style_sheet) # self.learning_tab.update_style_sheet(style_sheet) self.setStyleSheet(style_sheet) def set_splash(self, message): """ Set splash message. """ if self.splash: self.splash.show_message(message) QApplication.processEvents() # --- Login # ------------------------------------------------------------------------- def update_login_status(self, user_data=None): """ Update login button and information. """ if user_data: self.username = user_data.get('login', '') self.logged = True if self.logged: username = self.username anaconda_api_url = CONF.get('main', 'anaconda_api_url') token = self.api.client_load_token(anaconda_api_url) self.button_logged_text.setText('Signed in as') self.button_logged_username.setText(username) url = "{0}/{1}".format(CONF.get('main', 'conda_url'), username) self.button_logged_username.setToolTip(url) self.button_login.setText('Sign out') self.environments_tab.packages_widget.set_token(token) else: self.button_logged_text.setText('') self.button_logged_username.setText('') self.button_login.setText(self._login_text) QApplication.restoreOverrideCursor() def login(self): """ Open up login dialog or log out depending on logged status. """ if self.logged: QApplication.setOverrideCursor(Qt.WaitCursor) self.api.client_logout() self.api.client_remove_token() self.logged = False self.sig_logged_out.emit() self.tracker.track_event('authenticate', 'logout', label=self.username) else: dlg = AuthenticationDialog(self.api, parent=self) if self.tracker: self.tracker.track_page('/login', pagetitle='Login dialog') if dlg.exec_(): self.api.client_store_token(dlg.token) self.username = dlg.username self.logged = True self.sig_logged_in.emit() if self.tracker: self.tracker.track_event('authenticate', 'login', label=self.username) self._track_tab() self.update_login_status() logger.debug(str((self.logged, self.username))) # --- Dialogs # ------------------------------------------------------------------------- def show_preferences(self): """ Display the preferences dialog and apply the needed actions. """ dlg = PreferencesDialog(self) self.tracker.track_page('/preferences', pagetitle='Preferences dialog') set_domains = self.environments_tab.packages_widget.update_domains set_domains = self.environments_tab.packages_widget.update_domains dlg.sig_urls_updated.connect(set_domains) dlg.sig_urls_updated.connect(lambda au, cu: self.login()) dlg.exec_() self._track_tab() def show_about(self): """ Display the `About` dialog with information on the project. """ dlg = AboutDialog(self) self.tracker.track_page('/about', pagetitle='About dialog') dlg.exec_() self._track_tab() def show_log_viewer(self): """ Display the logs viewer to the user """ dlg = LogViewerDialog() self.tracker.track_page('/logs', pagetitle='Log Viewer Dialog') dlg.exec_() self._track_tab() def show_welcome_screen(self): if getattr(self, 'showme', True) and CONF.get('main', 'show_startup', True): from anaconda_navigator.widgets.splash import FirstSplash self.showme = False self.splash.hide() dlg = FirstSplash() dlg.raise_() dlg.exec_() # --- Update Navigator # ------------------------------------------------------------------------- def _update_application(self, worker, output, error): self.button_update_available.setDisabled(False) if error: text = 'Anaconda Navigator Update error:' dlg = MessageBoxError(text=text, error=error, title='Application Update Error') self.tracker.track_page('/update/error', pagetitle='Update Application Error ' 'Message Box') dlg.exec_() else: text = ('Anaconda Navigator Updated succefully.\n\n' 'Please restart the application') dlg = MessageBoxInformation(text=text, title='Application Update') self.tracker.track_page('/update/successful', pagetitle='Application Update Succesful ' 'Message Box') dlg.exec_() self._track_tab() def update_application(self): version = self.application_update_version if version: dlg = DialogUpdateApplication(version=version) self.tracker.track_page('/update', pagetitle='Update Application Dialog') reply = dlg.exec_() if reply: self.tracker.track_event('application', 'updated', version) self.busy = True pkg = 'anaconda-navigator={}'.format(version) worker = self.api.conda_install(name='root', pkgs=[pkg]) worker.sig_finished.connect(self._update_application) self.button_update_available.setDisabled(True) self._track_tab() def update_status_bar(self, message='', timeout=0, val=-1, max_val=-1): """ """ statusbar = self.statusBar() if val != -1 and max_val != -1: self.progressbar.setVisible(True) self.progressbar.setValue(val) self.progressbar.setMaximum(max_val) else: self.progressbar.setVisible(False) if message: statusbar.showMessage(message, timeout) else: statusbar.clearMessage() statusbar.setVisible(False) statusbar.setMaximumHeight(0) statusbar.hide() # --- Url handling # ------------------------------------------------------------------------- def open_url(self, url): qurl = QUrl(url) QDesktopServices.openUrl(qurl) self.tracker.track_event('help', 'documentation', url) def open_login_page(self): """ """ conda_url = CONF.get('main', 'conda_url') url = "{0}/{1}".format(conda_url, self.username) qurl = QUrl(url) QDesktopServices.openUrl(qurl) self.tracker.track_event('content', 'clicked', url) # --- Qt methods # ------------------------------------------------------------------------- def closeEvent(self, event): """ Catch close event. """ # TODO: check if an update is not in progress or things might break!! # if self.busy: show_dialog = not CONF.get('main', 'hide_quit_dialog') if show_dialog: if self.tracker: self.tracker.track_page('/quit', pagetitle='Quit dialog') dlg = QuitApplicationDialog() reply = dlg.exec_() if not reply: event.ignore() self._track_tab() def keyPressEvent(self, event): """ Qt override. """ # if event.key() in [Qt.Key_F5]: # self.update_style_sheet() super(MainWindow, self).keyPressEvent(event) def paintEvent(self, event): """ Qt override. Draw lower left border of the main Stacked Widget. """ super(MainWindow, self).paintEvent(event) tab = self.stack.tabbar tab_pos = self.mapTo(self, tab.pos()) pane_pos = self.mapTo(self, self.stack.pos()) stack_height = self.stack.height() menu_height = self.menuBar().height() header_height = 49 # From css padding = 20 # From css left = 1 # From css extra = 8 # Still wondering where this extra Y delta is deltay = menu_height + header_height + tab.height() + padding + extra x0 = tab_pos.x() + tab.width() + padding - left y0 = tab_pos.y() + deltay y1 = pane_pos.y() + stack_height + deltay - tab.height() - padding painter = QPainter(self) painter.setPen(QPen(QColor('#006f43'), 1, Qt.SolidLine, Qt.RoundCap)) painter.drawLine(x0, y0, x0, y1)
class AlgorithmOptions(QWidget): def __init__(self, settings: StackSettings, image_view: StackImageView): super().__init__() self.settings = settings self.view_name = image_view.name self.show_result = QEnumComboBox( enum_class=LabelEnum) # QCheckBox("Show result") self._set_show_label_from_settings() self.opacity = QDoubleSpinBox() self.opacity.setRange(0, 1) self.opacity.setSingleStep(0.1) self._set_opacity_from_settings() self.only_borders = QCheckBox("Only borders") self._set_border_mode_from_settings() self.borders_thick = QSpinBox() self.borders_thick.setRange(1, 25) self.borders_thick.setSingleStep(1) self._set_border_thick_from_settings() self.execute_in_background_btn = QPushButton("Execute in background") self.execute_in_background_btn.setToolTip( "Run calculation in background. Put result in multiple files panel" ) self.execute_btn = QPushButton("Execute") self.execute_btn.setStyleSheet("QPushButton{font-weight: bold;}") self.execute_all_btn = QPushButton("Execute all") self.execute_all_btn.setToolTip( "Execute in batch mode segmentation with current parameter. File list need to be specified in image tab." ) self.execute_all_btn.setDisabled(True) self.save_parameters_btn = QPushButton("Save parameters") self.block_execute_all_btn = False self.algorithm_choose_widget = AlgorithmChoose(settings, mask_algorithm_dict) self.algorithm_choose_widget.result.connect(self.execution_result_set) self.algorithm_choose_widget.finished.connect(self.execution_finished) self.algorithm_choose_widget.progress_signal.connect( self.progress_info) # self.stack_layout = QStackedLayout() self.keep_chosen_components_chk = QCheckBox("Save selected components") self.keep_chosen_components_chk.setToolTip( "Save chosen components when loading segmentation form file\n or from multiple file widget." ) self.keep_chosen_components_chk.stateChanged.connect( self.set_keep_chosen_components) self.keep_chosen_components_chk.setChecked( settings.keep_chosen_components) self.show_parameters = QPushButton("Show parameters") self.show_parameters.setToolTip( "Show parameters of segmentation for each components") self.show_parameters_widget = SegmentationInfoDialog( self.settings, self.algorithm_choose_widget.change_algorithm) self.show_parameters.clicked.connect(self.show_parameters_widget.show) self.choose_components = ChosenComponents() self.choose_components.check_change_signal.connect( image_view.refresh_selected) self.choose_components.mouse_leave.connect(image_view.component_unmark) self.choose_components.mouse_enter.connect(image_view.component_mark) # WARNING works only with one channels algorithms # SynchronizeValues.add_synchronization("channels_chose", widgets_list) self.chosen_list = [] self.progress_bar2 = QProgressBar() self.progress_bar2.setHidden(True) self.progress_bar = QProgressBar() self.progress_bar.setHidden(True) self.progress_info_lab = QLabel() self.progress_info_lab.setHidden(True) self.file_list = [] self.batch_process = BatchProceed() self.batch_process.progress_signal.connect(self.progress_info) self.batch_process.error_signal.connect(self.execution_all_error) self.batch_process.execution_done.connect(self.execution_all_done) self.batch_process.range_signal.connect(self.progress_bar.setRange) self.is_batch_process = False self.setContentsMargins(0, 0, 0, 0) main_layout = QVBoxLayout() # main_layout.setSpacing(0) opt_layout = QHBoxLayout() opt_layout.setContentsMargins(0, 0, 0, 0) opt_layout.addWidget(self.show_result) opt_layout.addWidget(right_label("Opacity:")) opt_layout.addWidget(self.opacity) main_layout.addLayout(opt_layout) opt_layout2 = QHBoxLayout() opt_layout2.setContentsMargins(0, 0, 0, 0) opt_layout2.addWidget(self.only_borders) opt_layout2.addWidget(right_label("Border thick:")) opt_layout2.addWidget(self.borders_thick) main_layout.addLayout(opt_layout2) btn_layout = QGridLayout() btn_layout.setContentsMargins(0, 0, 0, 0) btn_layout.addWidget(self.execute_btn, 0, 0) btn_layout.addWidget(self.execute_in_background_btn, 0, 1) btn_layout.addWidget(self.execute_all_btn, 1, 0) btn_layout.addWidget(self.save_parameters_btn, 1, 1) main_layout.addLayout(btn_layout) main_layout.addWidget(self.progress_bar2) main_layout.addWidget(self.progress_bar) main_layout.addWidget(self.progress_info_lab) main_layout.addWidget(self.algorithm_choose_widget, 1) # main_layout.addWidget(self.algorithm_choose) # main_layout.addLayout(self.stack_layout, 1) main_layout.addWidget(self.choose_components) down_layout = QHBoxLayout() down_layout.addWidget(self.keep_chosen_components_chk) down_layout.addWidget(self.show_parameters) main_layout.addLayout(down_layout) main_layout.addStretch() main_layout.setContentsMargins(0, 0, 0, 0) # main_layout.setSpacing(0) self.setLayout(main_layout) # noinspection PyUnresolvedReferences self.execute_in_background_btn.clicked.connect( self.execute_in_background) self.execute_btn.clicked.connect(self.execute_action) self.execute_all_btn.clicked.connect(self.execute_all_action) self.save_parameters_btn.clicked.connect(self.save_parameters) # noinspection PyUnresolvedReferences self.opacity.valueChanged.connect(self._set_opacity) # noinspection PyUnresolvedReferences self.show_result.currentEnumChanged.connect(self._set_show_label) self.only_borders.stateChanged.connect(self._set_border_mode) # noinspection PyUnresolvedReferences self.borders_thick.valueChanged.connect(self._set_border_thick) image_view.component_clicked.connect( self.choose_components.other_component_choose) settings.chosen_components_widget = self.choose_components settings.components_change_list.connect( self.choose_components.new_choose) settings.image_changed.connect( self.choose_components.remove_components) settings.connect_to_profile( f"{self.view_name}.image_state.only_border", self._set_border_mode_from_settings) settings.connect_to_profile( f"{self.view_name}.image_state.border_thick", self._set_border_thick_from_settings) settings.connect_to_profile(f"{self.view_name}.image_state.opacity", self._set_opacity_from_settings) settings.connect_to_profile(f"{self.view_name}.image_state.show_label", self._set_show_label_from_settings) def _set_border_mode(self, value: bool): self.settings.set_in_profile( f"{self.view_name}.image_state.only_border", value) def _set_border_thick(self, value: int): self.settings.set_in_profile( f"{self.view_name}.image_state.border_thick", value) def _set_opacity(self, value: float): self.settings.set_in_profile(f"{self.view_name}.image_state.opacity", value) def _set_show_label(self, value: LabelEnum): self.settings.set_in_profile( f"{self.view_name}.image_state.show_label", value) def _set_border_mode_from_settings(self): self.only_borders.setChecked( self.settings.get_from_profile( f"{self.view_name}.image_state.only_border", True)) def _set_border_thick_from_settings(self): self.borders_thick.setValue( self.settings.get_from_profile( f"{self.view_name}.image_state.border_thick", 1)) def _set_opacity_from_settings(self): self.opacity.setValue( self.settings.get_from_profile( f"{self.view_name}.image_state.opacity", 1.0)) def _set_show_label_from_settings(self): self.show_result.setCurrentEnum( self.settings.get_from_profile( f"{self.view_name}.image_state.show_label", LabelEnum.Show_results)) @Slot(int) def set_keep_chosen_components(self, val): self.settings.set_keep_chosen_components(val) def save_parameters(self): dial = PSaveDialog(io_functions.save_parameters_dict, system_widget=False, settings=self.settings, path=IO_SAVE_DIRECTORY) if not dial.exec_(): return res = dial.get_result() res.save_class.save(res.save_destination, self.algorithm_choose_widget.current_parameters()) def file_list_change(self, val): self.file_list = val if len(self.file_list) > 0 and not self.block_execute_all_btn: self.execute_all_btn.setEnabled(True) else: self.execute_all_btn.setDisabled(True) def get_chosen_components(self): return sorted(self.choose_components.get_chosen()) @property def segmentation(self): return self.settings.roi @segmentation.setter def segmentation(self, val): self.settings.roi = val def _image_changed(self): self.settings.roi = None self.choose_components.set_chose([], []) def _execute_in_background_init(self): if self.batch_process.isRunning(): return self.progress_bar2.setVisible(True) self.progress_bar2.setRange(0, self.batch_process.queue.qsize()) self.progress_bar2.setValue(self.batch_process.index) self.progress_bar.setVisible(True) self.progress_bar.setValue(0) self.execute_btn.setDisabled(True) self.batch_process.start() def execute_in_background(self): # TODO check if components are properly passed widget = self.algorithm_choose_widget.current_widget() segmentation_profile = widget.get_segmentation_profile() task = BatchTask(self.settings.get_project_info(), segmentation_profile, None) self.batch_process.add_task(task) self.progress_bar2.setRange(0, self.progress_bar2.maximum() + 1) self._execute_in_background_init() def execute_all_action(self): dial = PSaveDialog( SaveROI, settings=self.settings, system_widget=False, path="io.save_batch", file_mode=PSaveDialog.Directory, ) if not dial.exec_(): return folder_path = str(dial.selectedFiles()[0]) widget = self.algorithm_choose_widget.current_widget() save_parameters = dial.values segmentation_profile = widget.get_segmentation_profile() for file_path in self.file_list: task = BatchTask(file_path, segmentation_profile, (folder_path, save_parameters)) self.batch_process.add_task(task) self.progress_bar2.setRange( 0, self.progress_bar2.maximum() + len(self.file_list)) self._execute_in_background_init() def execution_all_error(self, text): QMessageBox.warning(self, "Proceed error", text) def execution_all_done(self): if not self.batch_process.queue.empty(): self._execute_in_background_init() return self.execute_btn.setEnabled(True) self.block_execute_all_btn = False if len(self.file_list) > 0: self.execute_all_btn.setEnabled(True) self.progress_bar.setHidden(True) self.progress_bar2.setHidden(True) self.progress_info_lab.setHidden(True) def execute_action(self): self.execute_btn.setDisabled(True) self.execute_all_btn.setDisabled(True) self.block_execute_all_btn = True self.is_batch_process = False self.progress_bar.setRange(0, 0) self.choose_components.setDisabled(True) chosen = sorted(self.choose_components.get_chosen()) blank = get_mask(self.settings.roi, self.settings.mask, chosen) if blank is not None: # Problem with handling time data in algorithms # TODO Fix This blank = blank[0] self.progress_bar.setHidden(False) widget: AlgorithmSettingsWidget = self.algorithm_choose_widget.current_widget( ) widget.set_mask(blank) self.progress_bar.setRange(0, widget.algorithm.get_steps_num()) widget.execute() self.chosen_list = chosen def progress_info(self, text, num, file_name="", file_num=0): self.progress_info_lab.setVisible(True) if file_name != "": self.progress_info_lab.setText(file_name + "\n" + text) else: self.progress_info_lab.setText(text) self.progress_bar.setValue(num) self.progress_bar2.setValue(file_num) def execution_finished(self): self.execute_btn.setEnabled(True) self.block_execute_all_btn = False if len(self.file_list) > 0: self.execute_all_btn.setEnabled(True) self.progress_bar.setHidden(True) self.progress_info_lab.setHidden(True) self.choose_components.setDisabled(False) def execution_result_set(self, result): self.settings.set_segmentation_result(result) def showEvent(self, _): widget = self.algorithm_choose_widget.current_widget() widget.image_changed(self.settings.image)
class HomeTab(WidgetBase): """Home applications tab.""" # name, prefix, sender sig_item_selected = Signal(object, object, object) # button_widget, sender sig_channels_requested = Signal(object, object) # application_name, command, prefix, leave_path_alone, sender sig_launch_action_requested = Signal(object, object, bool, object, object) # action, application_name, version, sender sig_conda_action_requested = Signal(object, object, object, object) # url sig_url_clicked = Signal(object) # TODO: Connect these signals to have more granularity # [{'name': package_name, 'version': version}...], sender sig_install_action_requested = Signal(object, object) sig_remove_action_requested = Signal(object, object) def __init__(self, parent=None): """Home applications tab.""" super(HomeTab, self).__init__(parent) # Variables self._parent = parent self.api = AnacondaAPI() self.applications = None self.style_sheet = None self.app_timers = None self.current_prefix = None # Widgets self.list = ListWidgetApplication() self.button_channels = ButtonHomeChannels('Channels') self.button_refresh = ButtonHomeRefresh('Refresh') self.combo = ComboHomeEnvironment() self.frame_top = FrameTabHeader(self) self.frame_body = FrameTabContent(self) self.frame_bottom = FrameTabFooter(self) self.label_home = LabelHome('Applications on') self.label_status_action = QLabel('') self.label_status = QLabel('') self.progress_bar = QProgressBar() self.first_widget = self.combo # Widget setup self.setObjectName('Tab') self.progress_bar.setTextVisible(False) self.list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) # Layout layout_top = QHBoxLayout() layout_top.addWidget(self.label_home) layout_top.addWidget(SpacerHorizontal()) layout_top.addWidget(self.combo) layout_top.addWidget(SpacerHorizontal()) layout_top.addWidget(self.button_channels) layout_top.addWidget(SpacerHorizontal()) layout_top.addStretch() layout_top.addWidget(self.button_refresh) self.frame_top.setLayout(layout_top) layout_body = QVBoxLayout() layout_body.addWidget(self.list) self.frame_body.setLayout(layout_body) layout_bottom = QHBoxLayout() layout_bottom.addWidget(self.label_status_action) layout_bottom.addWidget(SpacerHorizontal()) layout_bottom.addWidget(self.label_status) layout_bottom.addStretch() layout_bottom.addWidget(self.progress_bar) self.frame_bottom.setLayout(layout_bottom) layout = QVBoxLayout() layout.addWidget(self.frame_top) layout.addWidget(self.frame_body) layout.addWidget(self.frame_bottom) self.setLayout(layout) # Signals self.list.sig_conda_action_requested.connect( self.sig_conda_action_requested) self.list.sig_url_clicked.connect(self.sig_url_clicked) self.list.sig_launch_action_requested.connect( self.sig_launch_action_requested) self.button_channels.clicked.connect(self.show_channels) self.button_refresh.clicked.connect(self.refresh_cards) self.progress_bar.setVisible(False) # --- Setup methods # ------------------------------------------------------------------------- def setup(self, conda_data): """Setup the tab content.""" conda_processed_info = conda_data.get('processed_info') environments = conda_processed_info.get('__environments') applications = conda_data.get('applications') self.current_prefix = conda_processed_info.get('default_prefix') self.set_environments(environments) self.set_applications(applications) def set_environments(self, environments): """Setup the environments list.""" # Disconnect to avoid triggering the signal when updating the content try: self.combo.currentIndexChanged.disconnect() except TypeError: pass self.combo.clear() for i, (env_prefix, env_name) in enumerate(environments.items()): self.combo.addItem(env_name, env_prefix) self.combo.setItemData(i, env_prefix, Qt.ToolTipRole) index = 0 for i, (env_prefix, env_name) in enumerate(environments.items()): if self.current_prefix == env_prefix: index = i break self.combo.setCurrentIndex(index) self.combo.currentIndexChanged.connect(self._item_selected) def set_applications(self, applications): """Build the list of applications present in the current conda env.""" apps = self.api.process_apps(applications, prefix=self.current_prefix) all_applications = [] installed_applications = [] not_installed_applications = [] # Check if some installed applications are not on the apps dict # for example when the channel was removed. linked_apps = self.api.conda_linked_apps_info(self.current_prefix) missing_apps = [app for app in linked_apps if app not in apps] for app in missing_apps: apps[app] = linked_apps[app] for app_name in sorted(list(apps.keys())): app = apps[app_name] item = ListItemApplication(name=app['name'], description=app['description'], versions=app['versions'], command=app['command'], image_path=app['image_path'], prefix=self.current_prefix, needs_license=app.get( 'needs_license', False)) if item.installed: installed_applications.append(item) else: not_installed_applications.append(item) all_applications = installed_applications + not_installed_applications self.list.clear() for i in all_applications: self.list.addItem(i) self.list.update_style_sheet(self.style_sheet) self.set_widgets_enabled(True) self.update_status() # --- Other methods # ------------------------------------------------------------------------- def current_environment(self): """Return the current selected environment.""" env_name = self.combo.currentText() return self.api.conda_get_prefix_envname(env_name) def refresh_cards(self): """Refresh application widgets. List widget items sometimes are hidden on resize. This method tries to compensate for that refreshing and repainting on user demand. """ self.list.update_style_sheet(self.style_sheet) self.list.repaint() for item in self.list.items(): if not item.widget.isVisible(): item.widget.repaint() def show_channels(self): """Emit signal requesting the channels dialog editor.""" self.sig_channels_requested.emit(self.button_channels, C.TAB_HOME) def update_list(self, name=None, version=None): """Update applications list.""" self.set_applications() self.label_status.setVisible(False) self.label_status_action.setVisible(False) self.progress_bar.setVisible(False) def update_versions(self, apps=None): """Update applications versions.""" self.items = [] for i in range(self.list.count()): item = self.list.item(i) self.items.append(item) if isinstance(item, ListItemApplication): name = item.name meta = apps.get(name) if meta: versions = meta['versions'] version = self.api.get_dev_tool_version(item.path) item.update_versions(version, versions) # --- Common Helpers (# FIXME: factor out to common base widget) # ------------------------------------------------------------------------- def _item_selected(self, index): """Notify that the item in combo (environment) changed.""" name = self.combo.itemText(index) prefix = self.combo.itemData(index) self.sig_item_selected.emit(name, prefix, C.TAB_HOME) @property def last_widget(self): """Return the last element of the list to be used in tab ordering.""" if self.list.items(): return self.list.items()[-1].widget def ordered_widgets(self, next_widget=None): """Return a list of the ordered widgets.""" ordered_widgets = [ self.combo, self.button_channels, self.button_refresh, ] ordered_widgets += self.list.ordered_widgets() return ordered_widgets def set_widgets_enabled(self, value): """Enable or disable widgets.""" self.combo.setEnabled(value) self.button_channels.setEnabled(value) self.button_refresh.setEnabled(value) for item in self.list.items(): item.button_install.setEnabled(value) item.button_options.setEnabled(value) if value: item.set_loading(not value) def update_items(self): """Update status of items in list.""" if self.list: for item in self.list.items(): item.update_status() def update_status(self, action='', message='', value=None, max_value=None): """Update the application action status.""" # Elide if too big width = QApplication.desktop().availableGeometry().width() max_status_length = round(width * (2.0 / 3.0), 0) msg_percent = 0.70 fm = self.label_status_action.fontMetrics() action = fm.elidedText(action, Qt.ElideRight, round(max_status_length * msg_percent, 0)) message = fm.elidedText( message, Qt.ElideRight, round(max_status_length * (1 - msg_percent), 0)) self.label_status_action.setText(action) self.label_status.setText(message) if max_value is None and value is None: self.progress_bar.setVisible(False) else: self.progress_bar.setVisible(True) self.progress_bar.setMaximum(max_value) self.progress_bar.setValue(value) def update_style_sheet(self, style_sheet=None): """Update custom CSS style sheet.""" if style_sheet is None: self.style_sheet = load_style_sheet() else: self.style_sheet = style_sheet self.list.update_style_sheet(style_sheet=self.style_sheet) self.setStyleSheet(self.style_sheet)
class WidgetGallery(QWidget): def __init__(self, parent=None): super(WidgetGallery, self).__init__(parent) self.originalPalette = QApplication.palette() styleComboBox = QComboBox() styleComboBox.addItems(QStyleFactory.keys()) styleLabel = QLabel("&Style:") styleLabel.setBuddy(styleComboBox) self.useStylePaletteCheckBox = QCheckBox( "&Use style's standard palette") self.useStylePaletteCheckBox.setChecked(True) disableWidgetsCheckBox = QCheckBox("&Disable widgets") self.createTopLeftGroupBox() self.createTopRightGroupBox() self.createBottomLeftTabWidget() self.createBottomRightGroupBox() self.createProgressBar() styleComboBox.activated[str].connect(self.changeStyle) self.useStylePaletteCheckBox.toggled.connect(self.changePalette) disableWidgetsCheckBox.toggled.connect( self.topLeftGroupBox.setDisabled) disableWidgetsCheckBox.toggled.connect( self.topRightGroupBox.setDisabled) disableWidgetsCheckBox.toggled.connect( self.bottomLeftTabWidget.setDisabled) disableWidgetsCheckBox.toggled.connect( self.bottomRightGroupBox.setDisabled) topLayout = QHBoxLayout() topLayout.addWidget(styleLabel) topLayout.addWidget(styleComboBox) topLayout.addStretch(1) topLayout.addWidget(self.useStylePaletteCheckBox) topLayout.addWidget(disableWidgetsCheckBox) mainLayout = QGridLayout() mainLayout.addLayout(topLayout, 0, 0, 1, 2) mainLayout.addWidget(self.topLeftGroupBox, 1, 0) mainLayout.addWidget(self.topRightGroupBox, 1, 1) mainLayout.addWidget(self.bottomLeftTabWidget, 2, 0) mainLayout.addWidget(self.bottomRightGroupBox, 2, 1) mainLayout.addWidget(self.progressBar, 3, 0, 1, 2) mainLayout.setRowStretch(1, 1) mainLayout.setRowStretch(2, 1) mainLayout.setColumnStretch(0, 1) mainLayout.setColumnStretch(1, 1) self.setLayout(mainLayout) self.changeStyle('Windows') self.val = 0 def changeStyle(self, styleName): QApplication.setStyle(QStyleFactory.create(styleName)) self.changePalette() def changePalette(self): if (self.useStylePaletteCheckBox.isChecked()): QApplication.setPalette(QApplication.style().standardPalette()) else: QApplication.setPalette(self.originalPalette) def advanceProgressBar(self): self.progressBar.setMaximum(100) self.val += 1 self.progressBar.setValue(self.val) def createTopLeftGroupBox(self): self.topLeftGroupBox = QGroupBox("Group 1") radioButton1 = QRadioButton("Radio button 1") radioButton2 = QRadioButton("Radio button 2") radioButton3 = QRadioButton("Radio button 3") radioButton1.setChecked(True) checkBox = QCheckBox("Tri-state check box") checkBox.setTristate(True) checkBox.setCheckState(Qt.PartiallyChecked) layout = QVBoxLayout() layout.addWidget(radioButton1) layout.addWidget(radioButton2) layout.addWidget(radioButton3) layout.addWidget(checkBox) layout.addStretch(1) self.topLeftGroupBox.setLayout(layout) def createTopRightGroupBox(self): self.topRightGroupBox = QGroupBox("Group 2") defaultPushButton = QPushButton("Default Push Button") defaultPushButton.setDefault(True) togglePushButton = QPushButton("Toggle Push Button") togglePushButton.setCheckable(True) togglePushButton.setChecked(True) flatPushButton = QPushButton("Flat Push Button") flatPushButton.setFlat(True) layout = QVBoxLayout() layout.addWidget(defaultPushButton) layout.addWidget(togglePushButton) layout.addWidget(flatPushButton) layout.addStretch(1) self.topRightGroupBox.setLayout(layout) def createBottomLeftTabWidget(self): self.bottomLeftTabWidget = QTabWidget() self.bottomLeftTabWidget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Ignored) tab1 = QWidget() tableWidget = QTableWidget(10, 10) tab1hbox = QHBoxLayout() tab1hbox.setContentsMargins(5, 5, 5, 5) tab1hbox.addWidget(tableWidget) tab1.setLayout(tab1hbox) tab2 = QWidget() textEdit = QTextEdit() textEdit.setPlainText("Twinkle, twinkle, little star,\n" "How I wonder what you are.\n" "Up above the world so high,\n" "Like a diamond in the sky.\n" "Twinkle, twinkle, little star,\n" "How I wonder what you are!\n") tab2hbox = QHBoxLayout() tab2hbox.setContentsMargins(5, 5, 5, 5) tab2hbox.addWidget(textEdit) tab2.setLayout(tab2hbox) self.bottomLeftTabWidget.addTab(tab1, "&Table") self.bottomLeftTabWidget.addTab(tab2, "Text &Edit") def createBottomRightGroupBox(self): self.bottomRightGroupBox = QGroupBox("Group 3") self.bottomRightGroupBox.setCheckable(True) self.bottomRightGroupBox.setChecked(True) lineEdit = QLineEdit('s3cRe7') lineEdit.setEchoMode(QLineEdit.Password) spinBox = QSpinBox(self.bottomRightGroupBox) spinBox.setValue(50) dateTimeEdit = QDateTimeEdit(self.bottomRightGroupBox) dateTimeEdit.setDateTime(QDateTime.currentDateTime()) slider = QSlider(Qt.Horizontal, self.bottomRightGroupBox) slider.setValue(40) scrollBar = QScrollBar(Qt.Horizontal, self.bottomRightGroupBox) scrollBar.setValue(60) dial = QDial(self.bottomRightGroupBox) dial.setValue(30) dial.setNotchesVisible(True) layout = QGridLayout() layout.addWidget(lineEdit, 0, 0, 1, 2) layout.addWidget(spinBox, 1, 0, 1, 2) layout.addWidget(dateTimeEdit, 2, 0, 1, 2) layout.addWidget(slider, 3, 0) layout.addWidget(scrollBar, 4, 0) layout.addWidget(dial, 3, 1, 2, 1) layout.setRowStretch(5, 1) self.bottomRightGroupBox.setLayout(layout) def createProgressBar(self): self.progressBar = QProgressBar() self.progressBar.setRange(0, 10000) self.progressBar.setValue(0) timer = QTimer(self) timer.timeout.connect(self.advanceProgressBar) timer.start(1000)