def test_autofit_figure_viewer(figbrowser, tmpdir, fmt): """ Test figure diplayed when `Fit plots to window` is True. """ fig = add_figures_to_browser(figbrowser, 1, tmpdir, fmt)[0] figviewer = figbrowser.figviewer figcanvas = figviewer.figcanvas # Calculate original figure size in pixels. qpix = QPixmap() qpix.loadFromData(fig, fmt.upper()) fwidth, fheight = qpix.width(), qpix.height() # Test when `Fit plots to window` is set to True. # Otherwise, test should fall into `test_zoom_figure_viewer` figbrowser.change_auto_fit_plotting(True) size = figviewer.size() style = figviewer.style() width = (size.width() - style.pixelMetric(QStyle.PM_LayoutLeftMargin) - style.pixelMetric(QStyle.PM_LayoutRightMargin)) height = (size.height() - style.pixelMetric(QStyle.PM_LayoutTopMargin) - style.pixelMetric(QStyle.PM_LayoutBottomMargin)) if (fwidth / fheight) > (width / height): new_width = int(width) new_height = int(width / fwidth * fheight) else: new_height = int(height) new_width = int(height / fheight * fwidth) assert (figbrowser.zoom_disp.value() == np.round(figcanvas.width() / fwidth * 100)) assert figcanvas.width() == new_width assert figcanvas.height() == new_height
def __init__(self, parent): super(KiteIntegrationInfo, self).__init__(parent) # Images images_layout = QHBoxLayout() if is_dark_interface(): icon_filename = 'spyder_kite.svg' else: icon_filename = 'spyder_kite_dark.svg' image_path = get_image_path(icon_filename) image = QPixmap(image_path) image_label = QLabel() screen = QApplication.primaryScreen() device_image_ratio = screen.devicePixelRatio() if device_image_ratio > 1: image.setDevicePixelRatio(device_image_ratio) else: image_height = image.height() * 0.5 image_width = image.width() * 0.5 image = image.scaled(image_width, image_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) image_label.setPixmap(image) images_layout.addStretch() images_layout.addWidget(image_label) images_layout.addStretch() # Label integration_label = QLabel( _("Now Spyder can use <a href=\"{kite_url}\">Kite</a> to " "provide better and more accurate code completions in its " "editor <br>for the most important packages in the Python " "scientific ecosystem, such as Numpy, <br>Matplotlib and " "Pandas.<br><br>Would you like to install it or learn more " "about it?<br><br><i>Note:</i> Kite is free to use " "but is not an open source program.").format( kite_url=KITE_SPYDER_URL)) integration_label.setOpenExternalLinks(True) # Buttons buttons_layout = QHBoxLayout() learn_more_button = QPushButton(_('Learn more')) learn_more_button.setAutoDefault(False) install_button = QPushButton(_('Install Kite')) install_button.setAutoDefault(False) dismiss_button = QPushButton(_('Dismiss')) dismiss_button.setAutoDefault(False) buttons_layout.addStretch() buttons_layout.addWidget(install_button) buttons_layout.addWidget(learn_more_button) buttons_layout.addWidget(dismiss_button) general_layout = QVBoxLayout() general_layout.addLayout(images_layout) general_layout.addWidget(integration_label) general_layout.addLayout(buttons_layout) self.setLayout(general_layout) learn_more_button.clicked.connect(self.sig_learn_more_button_clicked) install_button.clicked.connect(self.sig_install_button_clicked) dismiss_button.clicked.connect(self.sig_dismiss_button_clicked)
def test_zoom_figure_viewer(figbrowser, tmpdir, fmt): """ Test zooming in and out the figure diplayed in the figure viewer. """ fig = add_figures_to_browser(figbrowser, 1, tmpdir, fmt)[0] figcanvas = figbrowser.figviewer.figcanvas # Set `Fit plots to windows` to False before the test. figbrowser.change_auto_fit_plotting(False) # Calculate original figure size in pixels. qpix = QPixmap() qpix.loadFromData(fig, fmt.upper()) fwidth, fheight = qpix.width(), qpix.height() assert figbrowser.zoom_disp.value() == 100 assert figcanvas.width() == fwidth assert figcanvas.height() == fheight # Zoom in and out the figure in the figure viewer. scaling_factor = 0 scaling_step = figbrowser.figviewer._scalestep for zoom_step in [1, 1, -1, -1, -1]: if zoom_step == 1: figbrowser.zoom_in() elif zoom_step == -1: figbrowser.zoom_out() scaling_factor += zoom_step scale = scaling_step**scaling_factor assert figbrowser.zoom_disp.value() == np.floor(scale * 100) assert figcanvas.width() == np.floor(fwidth * scale) assert figcanvas.height() == np.floor(fheight * scale)
def test_autofit_figure_viewer(figbrowser, tmpdir, fmt): """ Test figure diplayed when `Fit plots to window` is True. """ fig = add_figures_to_browser(figbrowser, 1, tmpdir, fmt)[0] figviewer = figbrowser.figviewer figcanvas = figviewer.figcanvas # Calculate original figure size in pixels. qpix = QPixmap() qpix.loadFromData(fig, fmt.upper()) fwidth, fheight = qpix.width(), qpix.height() # Test when `Fit plots to window` is set to True. # Otherwise, test should fall into `test_zoom_figure_viewer` figbrowser.change_auto_fit_plotting(True) size = figviewer.size() scrollbar_width = figviewer.verticalScrollBar().sizeHint().width() width = size.width() - scrollbar_width scrollbar_height = figviewer.horizontalScrollBar().sizeHint().height() height = size.height() - scrollbar_height if (fwidth / fheight) > (width / height): new_width = int(width) new_height = int(width / fwidth * fheight) else: new_height = int(height) new_width = int(height / fheight * fwidth) assert figcanvas.width() == new_width assert figcanvas.height() == new_height
def findAscent(self, font): dummy = "E" white = QColor(Qt.white) fm = self.fontmetrics(font) pm = QPixmap(fm.width(dummy), fm.height()) pm.fill(white) p = QPainter(pm) p.setFont(font) p.drawText(0, 0, pm.width(), pm.height(), 0, dummy) p.end() img = pm.toImage() w = pm.width() linebytes = w * 4 for row in range(img.height()): if PYSIDE2: line = bytes(img.scanLine(row)) else: line = img.scanLine(row).asstring(linebytes) for col in range(w): color = struct.unpack("I", line[col * 4:(col + 1) * 4])[0] if color != white.rgb(): return fm.ascent() - row + 1 return fm.ascent()
def test_zoom_figure_viewer(figbrowser, tmpdir, fmt): """ Test zooming in and out the figure diplayed in the figure viewer. """ fig = add_figures_to_browser(figbrowser, 1, tmpdir, fmt)[0] figcanvas = figbrowser.figviewer.figcanvas # Calculate original figure size in pixels. qpix = QPixmap() qpix.loadFromData(fig, fmt.upper()) fwidth, fheight = qpix.width(), qpix.height() assert figbrowser.zoom_disp.value() == 100 assert figcanvas.width() == fwidth assert figcanvas.height() == fheight # Zoom in and out the figure in the figure viewer. scaling_factor = 0 scaling_step = figbrowser.figviewer._scalestep for zoom_step in [1, 1, -1, -1, -1]: if zoom_step == 1: figbrowser.zoom_in() elif zoom_step == -1: figbrowser.zoom_out() scaling_factor += zoom_step scale = scaling_step**scaling_factor assert figbrowser.zoom_disp.value() == np.floor(scale * 100) assert figcanvas.width() == np.floor(fwidth * scale) assert figcanvas.height() == np.floor(fheight * scale)
def _set_image(self, index): if index > len( self._all_images) - 1 or index < -1 * len(self._all_images): l.log("error: resetting again") index = 0 self._current_index = index l.log("setting image") image_pix_map = QPixmap(self._all_images[self._current_index]) print("image: ", image_pix_map.width(), image_pix_map.height()) self._image_label.setPixmap(image_pix_map) self.set_title(self._all_images[self._current_index])
class SearchLineEdit(QLineEdit): """Line edit search widget with icon and remove all button""" def __init__(self, parent=None, icon=True): super(SearchLineEdit, self).__init__(parent) self.setTextMargins(1, 0, 20, 0) if icon: self.setTextMargins(18, 0, 20, 0) self._label = QLabel(self) self._pixmap_icon = QPixmap(get_image_path('conda_search.png')) self._label.setPixmap(self._pixmap_icon) self._label.setStyleSheet('''border: 0px; padding-bottom: 0px; padding-left: 2px;''') self._pixmap = QPixmap(get_image_path('conda_del.png')) self.button_clear = QToolButton(self) self.button_clear.setIcon(QIcon(self._pixmap)) self.button_clear.setIconSize(QSize(18, 18)) self.button_clear.setCursor(Qt.ArrowCursor) self.button_clear.setStyleSheet("""QToolButton {background: transparent; padding-right: 2px; border: none; margin:0px; }""") self.button_clear.setVisible(False) # Layout self._layout = QHBoxLayout(self) self._layout.addWidget(self.button_clear, 0, Qt.AlignRight) self._layout.setSpacing(0) self._layout.setContentsMargins(0, 2, 2, 0) # Signals and slots self.button_clear.clicked.connect(self.clear_text) self.textChanged.connect(self._toggle_visibility) self.textEdited.connect(self._toggle_visibility) def _toggle_visibility(self): """ """ if len(self.text()) == 0: self.button_clear.setVisible(False) else: self.button_clear.setVisible(True) def sizeHint(self): return QSize(200, self._pixmap_icon.height()) # Public api # ---------- def clear_text(self): """ """ self.setText('') self.setFocus()
class SearchLineEdit(QLineEdit): """Line edit search widget with icon and remove all button""" def __init__(self, parent=None, icon=True): super(SearchLineEdit, self).__init__(parent) self.setTextMargins(1, 0, 20, 0) if icon: self.setTextMargins(18, 0, 20, 0) self._label = QLabel(self) self._pixmap_icon = QPixmap(get_image_path('conda_search.png')) self._label.setPixmap(self._pixmap_icon) self._label.setStyleSheet('''border: 0px; padding-bottom: 0px; padding-left: 2px;''') self._pixmap = QPixmap(get_image_path('conda_del.png')) self.button_clear = QToolButton(self) self.button_clear.setIcon(QIcon(self._pixmap)) self.button_clear.setIconSize(QSize(18, 18)) self.button_clear.setCursor(Qt.ArrowCursor) self.button_clear.setStyleSheet("""QToolButton {background: transparent; padding-right: 2px; border: none; margin:0px; }""") self.button_clear.setVisible(False) # Layout self._layout = QHBoxLayout(self) self._layout.addWidget(self.button_clear, 0, Qt.AlignRight) self._layout.setSpacing(0) self._layout.setContentsMargins(0, 2, 2, 0) # Signals and slots self.button_clear.clicked.connect(self.clear_text) self.textChanged.connect(self._toggle_visibility) self.textEdited.connect(self._toggle_visibility) def _toggle_visibility(self): """ """ if len(self.text()) == 0: self.button_clear.setVisible(False) else: self.button_clear.setVisible(True) def sizeHint(self): return QSize(200, self._pixmap_icon.height()) # Public api # ---------- def clear_text(self): """ """ self.setText('') self.setFocus()
class FigureCanvas(QFrame): """ A basic widget on which can be painted a custom png, jpg, or svg image. """ def __init__(self, parent=None): super(FigureCanvas, self).__init__(parent) self.setLineWidth(2) self.setMidLineWidth(1) self.setStyleSheet("background-color: white") self.fig = None self.fmt = None self.fwidth, self.fheight = 200, 200 def clear_canvas(self): """Clear the figure that was painted on the widget.""" self.fig = None self.fmt = None self._qpix_buffer = [] self.repaint() def load_figure(self, fig, fmt): """ Load the figure from a png, jpg, or svg image, convert it in a QPixmap, and force a repaint of the widget. """ self.fig = fig self.fmt = fmt if fmt in ['image/png', 'image/jpeg']: self._qpix_orig = QPixmap() self._qpix_orig.loadFromData(fig, fmt.upper()) elif fmt == 'image/svg+xml': self._qpix_orig = QPixmap(svg_to_image(fig)) self._qpix_buffer = [self._qpix_orig] self.fwidth = self._qpix_orig.width() self.fheight = self._qpix_orig.height() def paintEvent(self, event): """Qt method override to paint a custom image on the Widget.""" super(FigureCanvas, self).paintEvent(event) # Prepare the rect on which the image is going to be painted : fw = self.frameWidth() rect = QRect(0 + fw, 0 + fw, self.size().width() - 2 * fw, self.size().height() - 2 * fw) if self.fig is None: return # Check/update the qpixmap buffer : qpix2paint = None for qpix in self._qpix_buffer: if qpix.size().width() == rect.width(): qpix2paint = qpix break else: if self.fmt in ['image/png', 'image/jpeg']: qpix2paint = self._qpix_orig.scaledToWidth( rect.width(), mode=Qt.SmoothTransformation) elif self.fmt == 'image/svg+xml': qpix2paint = QPixmap(svg_to_image(self.fig, rect.size())) self._qpix_buffer.append(qpix2paint) if qpix2paint is not None: # Paint the image on the widget : qp = QPainter() qp.begin(self) qp.drawPixmap(rect, qpix2paint) qp.end()
def __init__(self, parent): super(KiteIntegrationInfo, self).__init__(parent) # Images images_layout = QHBoxLayout() icon_filename = 'kite_completions.png' image_path = get_image_path(icon_filename) image = QPixmap(image_path) image_label = QLabel() screen = QApplication.primaryScreen() image_label = QLabel() image_height = int(image.height() * self.ICON_SCALE_FACTOR) image_width = int(image.width() * self.ICON_SCALE_FACTOR) image = image.scaled(image_width, image_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) image_label.setPixmap(image) images_layout.addStretch() images_layout.addWidget(image_label) images_layout.addStretch() ilayout = QHBoxLayout() ilayout.addLayout(images_layout) # Label integration_label_title = QLabel( "Get better code completions in Spyder") integration_label_title.setStyleSheet( f"font-size: {self.TITLE_FONT_SIZE}") integration_label_title.setWordWrap(True) integration_label = QLabel( _("Now Spyder can use Kite to provide better code " "completions for key packages in the scientific Python " "Ecosystem. Install Kite for a better editor experience in " "Spyder. <br><br>Kite is free to use but is not open " "source. <a href=\"{kite_url}\">Learn more about Kite </a>"). format(kite_url=KITE_SPYDER_URL)) integration_label.setStyleSheet(f"font-size: {self.CONTENT_FONT_SIZE}") integration_label.setOpenExternalLinks(True) integration_label.setWordWrap(True) integration_label.setFixedWidth(360) label_layout = QVBoxLayout() label_layout.addWidget(integration_label_title) label_layout.addWidget(integration_label) # Buttons buttons_layout = QHBoxLayout() install_button = QPushButton(_('Install Kite')) install_button.setAutoDefault(False) install_button.setStyleSheet("background-color: #3775A9;" f"font-size: {self.BUTTONS_FONT_SIZE};" f"padding: {self.BUTTONS_PADDING}") dismiss_button = QPushButton(_('Dismiss')) dismiss_button.setAutoDefault(False) dismiss_button.setStyleSheet("background-color: #60798B;" f"font-size: {self.BUTTONS_FONT_SIZE};" f"padding: {self.BUTTONS_PADDING}") buttons_layout.addStretch() buttons_layout.addWidget(install_button) if not MAC: buttons_layout.addSpacing(10) buttons_layout.addWidget(dismiss_button) # Buttons with label vertical_layout = QVBoxLayout() if not MAC: vertical_layout.addStretch() vertical_layout.addLayout(label_layout) vertical_layout.addSpacing(20) vertical_layout.addLayout(buttons_layout) vertical_layout.addStretch() else: vertical_layout.addLayout(label_layout) vertical_layout.addLayout(buttons_layout) general_layout = QHBoxLayout() general_layout.addStretch() general_layout.addLayout(ilayout) general_layout.addSpacing(15) general_layout.addLayout(vertical_layout) general_layout.addStretch() self.setLayout(general_layout) # Signals install_button.clicked.connect(self.sig_install_button_clicked) dismiss_button.clicked.connect(self.sig_dismiss_button_clicked) if is_dark_interface(): self.setStyleSheet("background-color: #262E38") self.setContentsMargins(18, 40, 18, 40) if not MAC: self.setFixedSize(800, 350)
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 is_image_landscape(self, image: QPixmap): if image.width() / image.height() > 1: return True else: return False
class Canvas(QWidget): def __init__(self, parent): super(Canvas, self).__init__() self.SELECT, self.RECTANGLE, self.AUTO_POLYGON, self.POLYGON = 0, 1, 2, 3 self._painter = QPainter() self.pixmap = None self.cv2_image = None self.scale = 1.0 self.mode = self.SELECT self.label_dir = "" self.parent = parent self.reset() self.labelDialog = LabelDialog(parent=self, listItem=self.parent.labels) def reset(self): self.points = [] self.objects = [] self.cur_object = -1 self.parent.load_object_list(self.objects) # load image and label file def load_file(self, image_dir): self.reset() self.pixmap = QPixmap(image_dir) self.cv2_image = cv2.imread(image_dir) self.label_dir = os.path.splitext(image_dir)[0] + '.json' if QFile.exists(self.label_dir): with open(self.label_dir) as json_file: data = json.load(json_file) self.objects = [] for obj in data['objects']: points = [] for point in obj['points']: points.append(QPointF(point['x'], point['y'])) if (obj['type'] == 'rectangle'): self.objects.append(Rectangle(points, obj['label'])) elif (obj['type'] == 'polygon'): self.objects.append(Polygon(points, obj['label'])) self.cur_object = len(self.objects) - 1 self.parent.load_object_list(self.objects) self.parent.object_list_widget.setCurrentRow(self.cur_object) self.repaint() def offset_to_center(self): s = self.scale area = super(Canvas, self).size() w, h = self.pixmap.width() * s, self.pixmap.height() * s aw, ah = area.width(), area.height() x = (aw - w) / (2 * s) if aw > w else 0 y = (ah - h) / (2 * s) if ah > h else 0 return QPointF(x, y) def transform_pos(self, point): return point / self.scale - self.offset_to_center() def rescale(self, value): if value >= 0.2 and value <= 5: self.scale = value self.repaint() def in_pixmap(self, p): w, h = self.pixmap.width(), self.pixmap.height() return (0 <= p.x() < w and 0 <= p.y() < h) def close_enough(self, p1, p2): epsilon = 0.01 return abs(p1.x() - p2.x()) <= max(5, epsilon * self.pixmap.width()) and \ abs(p1.y() - p2.y()) <= max(5, epsilon * self.pixmap.height()) def next_obj(self): if len(self.objects) > 0: self.cur_object = (self.cur_object + 1) % len(self.objects) self.parent.object_list_widget.setCurrentRow(self.cur_object) def prev_obj(self): if len(self.objects) > 0: self.cur_object = (self.cur_object + len(self.objects) - 1) % len( self.objects) self.parent.object_list_widget.setCurrentRow(self.cur_object) def del_obj(self): if len(self.objects) > 0: if self.cur_object == len(self.objects) - 1: new_id = self.cur_object - 1 else: new_id = self.cur_object self.objects = self.objects[:self.cur_object] + self.objects[ self.cur_object + 1:] self.cur_object = new_id self.parent.load_object_list(self.objects) self.parent.object_list_widget.setCurrentRow(self.cur_object) self.repaint() self.auto_export_json() def add_obj(self, obj): self.objects.append(obj) self.cur_object = len(self.objects) - 1 self.parent.load_object_list(self.objects) self.parent.object_list_widget.setCurrentRow(self.cur_object) self.repaint() self.auto_export_json() def auto_export_json(self): data = {} data['date'] = str(datetime.datetime.now()) data['objects'] = [] for obj in self.objects: data_obj = {} obj.export_json(data_obj) data['objects'].append(data_obj) with open(self.label_dir, 'w') as json_file: json.dump(data, json_file) self.parent.load_file_list() self.parent.file_list_widget.setCurrentRow(self.parent.file_id) def paintEvent(self, event): if self.pixmap == None: return super(Canvas, self).paintEvent(event) p = self._painter p.begin(self) p.setRenderHint(QPainter.Antialiasing) p.setRenderHint(QPainter.HighQualityAntialiasing) p.setRenderHint(QPainter.SmoothPixmapTransform) p.scale(self.scale, self.scale) p.translate(self.offset_to_center()) p.drawPixmap(0, 0, self.pixmap) pen = QPen() pen.setWidth(2 / self.scale) p.setPen(pen) for i, obj in enumerate(self.objects): if self.cur_object == i: p.setBrush(QColor(255, 0, 0, 100)) else: p.setBrush(QColor(255, 0, 0, 0)) obj.draw(p) if self.mode != self.SELECT: for point in self.points: p.drawEllipse(point, 2 / self.scale, 2 / self.scale) for i in range(len(self.points) - 1): p.drawLine(self.points[i], self.points[i + 1]) p.end() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: pos = self.transform_pos(event.localPos()) if self.in_pixmap(pos): self.points.append(pos) self.repaint() if self.mode == self.RECTANGLE and len(self.points) == 2: left_x = int(min(self.points[0].x(), self.points[1].x())) right_x = int(max(self.points[0].x(), self.points[1].x())) left_y = int(min(self.points[0].y(), self.points[1].y())) right_y = int(max(self.points[0].y(), self.points[1].y())) text = self.labelDialog.pop_up() self.add_obj(Rectangle(self.points, text)) self.points = [] if self.mode == self.AUTO_POLYGON and len(self.points) == 2: left_x = int(min(self.points[0].x(), self.points[1].x())) right_x = int(max(self.points[0].x(), self.points[1].x())) left_y = int(min(self.points[0].y(), self.points[1].y())) right_y = int(max(self.points[0].y(), self.points[1].y())) tmp = rectilinear_polygon.main( self.cv2_image[left_y:right_y, left_x:right_x]) points = [QPointF(left_x + x, left_y + y) for x, y in tmp] if (len(points) > 0): text = self.labelDialog.pop_up() self.add_obj(Polygon(points, text)) self.points = [] if self.mode == self.POLYGON and len(self.points) >= 4 \ and self.close_enough(self.points[0], self.points[-1]): text = self.labelDialog.pop_up() self.add_obj(Polygon(self.points[:-1], text)) self.points = []
def __init__(self, parent, tour_function): super().__init__(parent) if MAC: flags = (self.windowFlags() | Qt.WindowStaysOnTopHint & ~Qt.WindowContextHelpButtonHint) else: flags = self.windowFlags() & ~Qt.WindowContextHelpButtonHint self.setWindowFlags(flags) self.tour_function = tour_function # Image images_layout = QHBoxLayout() icon_filename = 'tour-spyder-logo' image_path = get_image_path(icon_filename) image = QPixmap(image_path) image_label = QLabel() image_height = image.height() * self.ICON_SCALE_FACTOR image_width = image.width() * self.ICON_SCALE_FACTOR image = image.scaled(image_width, image_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) image_label.setPixmap(image) images_layout.addStretch() images_layout.addWidget(image_label) images_layout.addStretch() if MAC: images_layout.setContentsMargins(0, -5, 20, 0) else: images_layout.setContentsMargins(0, -8, 35, 0) # Label tour_label_title = QLabel(_("Welcome to Spyder!")) tour_label_title.setStyleSheet(f"font-size: {self.TITLE_FONT_SIZE}") tour_label_title.setWordWrap(True) tour_label = QLabel( _("Check out our interactive tour to " "explore some of Spyder's panes and features.")) tour_label.setStyleSheet(f"font-size: {self.CONTENT_FONT_SIZE}") tour_label.setWordWrap(True) tour_label.setFixedWidth(340) # Buttons buttons_layout = QHBoxLayout() dialog_tour_color = QStylePalette.COLOR_BACKGROUND_2 start_tour_color = QStylePalette.COLOR_ACCENT_2 start_tour_hover = QStylePalette.COLOR_ACCENT_3 start_tour_pressed = QStylePalette.COLOR_ACCENT_4 dismiss_tour_color = QStylePalette.COLOR_BACKGROUND_4 dismiss_tour_hover = QStylePalette.COLOR_BACKGROUND_5 dismiss_tour_pressed = QStylePalette.COLOR_BACKGROUND_6 font_color = QStylePalette.COLOR_TEXT_1 self.launch_tour_button = QPushButton(_('Start tour')) self.launch_tour_button.setStyleSheet( ("QPushButton {{ " "background-color: {background_color};" "border-color: {border_color};" "font-size: {font_size};" "color: {font_color};" "padding: {padding}}}" "QPushButton:hover:!pressed {{ " "background-color: {color_hover}}}" "QPushButton:pressed {{ " "background-color: {color_pressed}}}").format( background_color=start_tour_color, border_color=start_tour_color, font_size=self.BUTTONS_FONT_SIZE, font_color=font_color, padding=self.BUTTONS_PADDING, color_hover=start_tour_hover, color_pressed=start_tour_pressed)) self.launch_tour_button.setAutoDefault(False) self.dismiss_button = QPushButton(_('Dismiss')) self.dismiss_button.setStyleSheet( ("QPushButton {{ " "background-color: {background_color};" "border-color: {border_color};" "font-size: {font_size};" "color: {font_color};" "padding: {padding}}}" "QPushButton:hover:!pressed {{ " "background-color: {color_hover}}}" "QPushButton:pressed {{ " "background-color: {color_pressed}}}").format( background_color=dismiss_tour_color, border_color=dismiss_tour_color, font_size=self.BUTTONS_FONT_SIZE, font_color=font_color, padding=self.BUTTONS_PADDING, color_hover=dismiss_tour_hover, color_pressed=dismiss_tour_pressed)) self.dismiss_button.setAutoDefault(False) buttons_layout.addStretch() buttons_layout.addWidget(self.launch_tour_button) if not MAC: buttons_layout.addSpacing(10) buttons_layout.addWidget(self.dismiss_button) layout = QHBoxLayout() layout.addLayout(images_layout) label_layout = QVBoxLayout() label_layout.addWidget(tour_label_title) if not MAC: label_layout.addSpacing(3) label_layout.addWidget(tour_label) else: label_layout.addWidget(tour_label) label_layout.addSpacing(10) vertical_layout = QVBoxLayout() if not MAC: vertical_layout.addStretch() vertical_layout.addLayout(label_layout) vertical_layout.addSpacing(20) vertical_layout.addLayout(buttons_layout) vertical_layout.addStretch() else: vertical_layout.addLayout(label_layout) vertical_layout.addLayout(buttons_layout) general_layout = QHBoxLayout() if not MAC: general_layout.addStretch() general_layout.addLayout(layout) general_layout.addSpacing(1) general_layout.addLayout(vertical_layout) general_layout.addStretch() else: general_layout.addLayout(layout) general_layout.addLayout(vertical_layout) self.setLayout(general_layout) self.launch_tour_button.clicked.connect(self._start_tour) self.dismiss_button.clicked.connect(self.close) self.setStyleSheet(f"background-color:{dialog_tour_color}") self.setContentsMargins(18, 40, 18, 40) if not MAC: self.setFixedSize(640, 280)
class AboutView(QDialog): def __init__(self, parent, presenter, version_text, date_text = None): super(AboutView, self).__init__(parent) self.background_pixmap = QPixmap(':/images/First_use_Background.png') self.mantid_pixmap = QPixmap(':/images/mantid_smaller.png') self.lbl_version = QLabel() self.clb_release_notes = QCommandLinkButton() self.clb_sample_datasets = QCommandLinkButton() self.clb_mantid_introduction = QCommandLinkButton() self.clb_python_introduction = QCommandLinkButton() self.clb_python_in_mantid = QCommandLinkButton() self.clb_extending_mantid = QCommandLinkButton() self.cb_facility = QComboBox() self.cb_instrument = QComboBox() self.pb_manage_user_directories = QPushButton() self.chk_allow_usage_data = QCheckBox() self.lbl_privacy_policy = QLabel() self.chk_do_not_show_until_next_release = QCheckBox() self.pb_close = QPushButton() self.setupUI() self.customize_layout(version_text, date_text) self.presenter = presenter def paintEvent(self, event): scaled_background = self.background_pixmap.scaled(self.rescale_w(self.background_pixmap.width()), self.rescale_h(self.background_pixmap.height()), Qt.KeepAspectRatio, Qt.SmoothTransformation) scaled_mantid = self.mantid_pixmap.scaled(self.rescale_w(self.mantid_pixmap.width()), self.rescale_h(self.mantid_pixmap.height()), Qt.KeepAspectRatio, Qt.SmoothTransformation) qp = QPainter() qp.begin(self) qp.drawPixmap(0, 0, scaled_background) qp.drawPixmap(self.width() - scaled_mantid.width(), self.height()-scaled_mantid.height(), scaled_mantid) qp.end() def determine_dialog_dimensions(self): width = REFERENCE_WIDTH height = REFERENCE_HEIGHT screen = None try: if hasattr(QGuiApplication,"screenAt"): screen = QGuiApplication.screenAt(self.parent().geometry().center()) else: # get the screen from the last top level window windows = QGuiApplication.topLevelWindows() screen = windows[-1].screen() except Exception: # something failed just take the primary screen screen = QGuiApplication.primaryScreen() if screen is not None: screen_width = screen.size().width() screen_height = screen.size().height() # the proportion of the whole window size for the about screen window_scaling = 0.4 width = int(screen_width * window_scaling) # also calculate the intended width but using the hieght and a standard screen aspect ratio width_by_height= int(screen_height * WIDESCREEN_ASPECT_RATIO * window_scaling) # take the smaller of the width from the screen width and height if width_by_height < width: width = width_by_height # set a minimum size if width < REFERENCE_WIDTH: width = REFERENCE_WIDTH # calculate height from the width and aspect ratio height = int(width / REFERENCE_ASPECT_RATIO) return width, height def rescale_w(self, value): return int(value * (self.width() / REFERENCE_WIDTH)) def rescale_h(self, value): return int(value * (self.height() / REFERENCE_HEIGHT)) def setupUI(self): width, height = self.determine_dialog_dimensions() self.setMinimumSize(width, height) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.setWindowTitle("About Mantid Workbench") self.setStyleSheet(f"""QDialog {{ background-color: rgb(190, 230, 190); }} QLabel{{ font: {self.rescale_w(14)}px; }} QPushButton{{ font: {self.rescale_w(14)}px; }} QCommandLinkButton{{ font: {self.rescale_w(22)}px; background-color: rgba(255, 255, 255, 0); border-radius: {self.rescale_w(15)}px; }} QCommandLinkButton:hover {{ background-color: rgba(45, 105, 45, 40); }}""") # version label section at th etop parent_layout = QVBoxLayout() parent_layout.addSpacerItem(QSpacerItem(self.rescale_w(20),self.rescale_h(70),vPolicy=QSizePolicy.Fixed)) self.lbl_version.setText("version ") self.lbl_version.setIndent(self.rescale_w(115)) self.lbl_version.setStyleSheet(f"""color: rgb(215, 215, 215); font: {self.rescale_w(28)}pt; font-weight: bold; font-size: {self.rescale_w(28)}px""") parent_layout.addWidget(self.lbl_version) parent_layout.addSpacerItem(QSpacerItem(self.rescale_w(20),self.rescale_h(40), vPolicy=QSizePolicy.MinimumExpanding)) # split into the two columns two_box_layout = QHBoxLayout() # left side Welcome and Tutorial left_layout = QVBoxLayout() left_layout.setContentsMargins(self.rescale_w(5), 0, self.rescale_w(10), 0) left_layout.setSpacing(0) # welcome label lbl_welcome = QLabel() lbl_welcome.setStyleSheet(f"color: rgb(45, 105, 45); font-size: {self.rescale_w(28)}px;") lbl_welcome.setText("Welcome") left_layout.addWidget(lbl_welcome) # release notes self.setup_command_link_button(self.clb_release_notes, "Release Notes", ':/images/Notepad-Bloc-notes-icon-48x48.png') left_layout.addWidget(self.clb_release_notes) # sample datasets self.setup_command_link_button(self.clb_sample_datasets, "Sample Datasets", ':/images/download-icon-48x48.png') left_layout.addWidget(self.clb_sample_datasets) # Tutorials Label lbl_tutorials = QLabel() lbl_tutorials.setStyleSheet(f"color: rgb(45, 105, 45); font-size: {self.rescale_w(28)}px;") lbl_tutorials.setText("Tutorials") left_layout.addWidget(lbl_tutorials) # Mantid Introduction self.setup_command_link_button(self.clb_mantid_introduction, "Mantid Introduction", ':/images/Misc-Tutorial-icon-48x48.png') left_layout.addWidget(self.clb_mantid_introduction) # Introduction to python self.setup_command_link_button(self.clb_python_introduction, "Introduction to Python", ':/images/Python-icon-48x48.png') left_layout.addWidget(self.clb_python_introduction) # Python in Mantid self.setup_command_link_button(self.clb_python_in_mantid, "Python In Mantid", ':/images/Circle_cog_48x48.png') left_layout.addWidget(self.clb_python_in_mantid) # Extending Mantid with python self.setup_command_link_button(self.clb_extending_mantid, "Extending Mantid with Python", ':/images/Plugin-Python-icon-48x48.png') left_layout.addWidget(self.clb_extending_mantid) # right hand side Setup and facility icons right_layout = QVBoxLayout() right_layout.setSpacing(0) # personal setup grp_personal_setup = QGroupBox() grp_personal_setup.setStyleSheet(f"""QGroupBox {{ border: {self.rescale_w(3)}px solid rgb(38, 128, 20);; border-radius: {self.rescale_w(10)}px; background-color: rgb(240, 240, 240); }} QGroupBox QLabel{{ font: {self.rescale_w(12)}px; color: rgb(121, 121, 121); }} QGroupBox QComboBox{{ font: {self.rescale_w(12)}px; }} font: {self.rescale_w(12)}px; """) grp_personal_setup_layout = QVBoxLayout() grp_personal_setup_layout.setContentsMargins(self.rescale_w(9), self.rescale_h(1), self.rescale_w(9), self.rescale_h(9)) grp_personal_setup_layout.setSpacing(0) grp_personal_setup.setLayout(grp_personal_setup_layout) lbl_personal_setup = QLabel() lbl_personal_setup.setStyleSheet(f"color: rgb(38, 128, 20);\nfont-size: {self.rescale_w(18)}px;") lbl_personal_setup.setText("Personal Setup") lbl_personal_setup.setAlignment(Qt.AlignHCenter) grp_personal_setup_layout.addWidget(lbl_personal_setup) personal_setup_form_layout = QFormLayout() personal_setup_form_layout.setHorizontalSpacing(self.rescale_w(5)) personal_setup_form_layout.setVerticalSpacing(self.rescale_h(5)) personal_setup_form_layout.setLabelAlignment(Qt.AlignRight) # default Facility lbl_default_facilty = QLabel() lbl_default_facilty.setText("Default Facility") personal_setup_form_layout.addRow(lbl_default_facilty, self.cb_facility) # default instrument lbl_default_instrument = QLabel() lbl_default_instrument.setText("Default Instrument") personal_setup_form_layout.addRow(lbl_default_instrument, self.cb_instrument) # Set Data Directories lbl_mud = QLabel() lbl_mud.setText("Set data directories") self.pb_manage_user_directories.setText("Manage User Directories") personal_setup_form_layout.addRow(lbl_mud, self.pb_manage_user_directories) # Usage data lbl_allow_usage_data = QLabel() lbl_allow_usage_data.setText("Report Usage Data") usagelayout = QHBoxLayout() usagelayout.setContentsMargins(0, 0, 0, 0) self.chk_allow_usage_data.setChecked(True) self.chk_allow_usage_data.setStyleSheet(f"padding: {self.rescale_w(4)}px;") usagelayout.addWidget(self.chk_allow_usage_data) usagelayout.addSpacerItem(QSpacerItem(self.rescale_w(40), self.rescale_h(20), hPolicy=QSizePolicy.Expanding)) self.lbl_privacy_policy.setText(r'<html><head/><body><p>' r'<a href="https://www.mantidproject.org/MantidProject:Privacy_policy' r'#Usage_Data_recorded_in_Mantid">' r'<span style=" text-decoration: underline; color:#0000ff;">' r'Privacy Policy</span></a></p></body></html>') self.lbl_privacy_policy.setOpenExternalLinks(False) usagelayout.addWidget(self.lbl_privacy_policy) personal_setup_form_layout.addRow(lbl_allow_usage_data,usagelayout) grp_personal_setup_layout.addLayout(personal_setup_form_layout) right_layout.addWidget(grp_personal_setup) right_layout.addSpacerItem(QSpacerItem(self.rescale_w(20), self.rescale_h(40), vPolicy=QSizePolicy.Expanding)) # facility icons # Row one icon_layout_top = QHBoxLayout() icon_layout_top.setContentsMargins(0, self.rescale_h(10), 0, 0) icon_layout_top.setSpacing(0) icon_layout_top.addWidget(self.create_label_with_image(112, 50, ':/images/ISIS_Logo_Transparent.gif')) icon_layout_top.addSpacerItem(QSpacerItem(self.rescale_w(10), self.rescale_h(20), hPolicy=QSizePolicy.Fixed)) icon_layout_top.addWidget(self.create_label_with_image(94, 50, ':/images/ess_logo_transparent_small.png')) icon_layout_top.addSpacerItem(QSpacerItem(self.rescale_w(40), 20,hPolicy=QSizePolicy.Expanding)) right_layout.addLayout(icon_layout_top) # Row two icon_layout_middle = QHBoxLayout() icon_layout_middle.setContentsMargins(0, self.rescale_h(10), 0, 0) icon_layout_middle.setSpacing(0) icon_layout_middle.addWidget(self.create_label_with_image(200, 30, ':/images/Ornl_hfir_sns_logo_small.png')) icon_layout_middle.addSpacerItem(QSpacerItem(self.rescale_w(40), self.rescale_h(20), hPolicy=QSizePolicy.Expanding)) right_layout.addLayout(icon_layout_middle) # Row three icon_layout_bottom = QHBoxLayout() icon_layout_bottom.setContentsMargins(0, self.rescale_h(10), 0, 0) icon_layout_bottom.setSpacing(0) icon_layout_bottom.addWidget(self.create_label_with_image(110, 40, ':/images/Tessella_Logo_Transparent.gif')) icon_layout_bottom.addSpacerItem(QSpacerItem(self.rescale_w(10), self.rescale_h(20), hPolicy=QSizePolicy.Fixed)) icon_layout_bottom.addWidget(self.create_label_with_image(50, 50, ':/images/ILL_logo.png')) icon_layout_bottom.addSpacerItem(QSpacerItem(self.rescale_w(10), self.rescale_h(20), hPolicy=QSizePolicy.Fixed)) icon_layout_bottom.addWidget(self.create_label_with_image(92, 50, ':/images/CSNS_Logo_Short.png')) icon_layout_bottom.addSpacerItem(QSpacerItem(self.rescale_w(40), self.rescale_h(20), hPolicy=QSizePolicy.Expanding)) right_layout.addLayout(icon_layout_bottom) # end the two box layout two_box_layout.addLayout(left_layout) two_box_layout.addLayout(right_layout) parent_layout.addLayout(two_box_layout) # footer footer_layout = QHBoxLayout() # do not show again do_not_show_layout = QVBoxLayout() do_not_show_layout.setContentsMargins(self.rescale_w(15), 0, 0, 0) do_not_show_layout.setSpacing(self.rescale_w(2)) do_not_show_layout.addSpacerItem(QSpacerItem(1,self.rescale_h(1), vPolicy=QSizePolicy.Expanding)) lbl_update = QLabel() lbl_update.setMinimumSize(self.rescale_w(400),0) lbl_update.setStyleSheet("color: rgb(25,125,25);") lbl_update.setText('You can revisit this dialog by selecting "About" on the Help menu.') lbl_update.setAlignment(Qt.AlignBottom) do_not_show_layout.addWidget(lbl_update) do_not_show_checkbox_layout = QHBoxLayout() self.chk_do_not_show_until_next_release.setChecked(True) do_not_show_checkbox_layout.addWidget(self.chk_do_not_show_until_next_release) do_not_show_checkbox_layout.addSpacerItem(QSpacerItem(self.rescale_w(10), self.rescale_h(2), hPolicy=QSizePolicy.Fixed)) lbl_do_not_show = QLabel() lbl_do_not_show.setStyleSheet("color: rgb(25,125,25);") lbl_do_not_show.setText('Do not show again until next release') do_not_show_checkbox_layout.addWidget(lbl_do_not_show) do_not_show_checkbox_layout.addSpacerItem(QSpacerItem(self.rescale_w(40),10, hPolicy=QSizePolicy.Expanding)) do_not_show_layout.addLayout(do_not_show_checkbox_layout) footer_layout.addLayout(do_not_show_layout) # Close button close_button_layout = QVBoxLayout() close_button_layout.addSpacerItem(QSpacerItem(20,self.rescale_h(15), vPolicy=QSizePolicy.Expanding)) self.pb_close.setText("Close") self.pb_close.setDefault(True) close_button_layout.addWidget(self.pb_close) footer_layout.addLayout(close_button_layout) footer_layout.addSpacerItem(QSpacerItem(self.rescale_w(100), self.rescale_h(20), hPolicy=QSizePolicy.Fixed)) parent_layout.addLayout(footer_layout) self.setLayout(parent_layout) self.setAttribute(Qt.WA_DeleteOnClose, True) def setup_command_link_button(self, link_button, text, image_location, width=40, height=40): link_button.setText(text) link_button.setIconSize(QSize(self.rescale_w(width), self.rescale_h(height))) link_button.setIcon(QIcon(QPixmap(image_location))) return link_button def create_label_with_image(self, width, height, image_location): label_with_image = QLabel() label_with_image.setMinimumSize(self.rescale_w(width), self.rescale_h(height)) label_with_image.setMaximumSize(self.rescale_w(width), self.rescale_h(height)) label_with_image.setPixmap(QPixmap(image_location)) label_with_image.setScaledContents(True) return label_with_image def customize_layout(self, version_text, date_text): self.setWindowTitle(self.windowTitle() + " " + version_text) version_label = version_text # add a date if it is an official release if date_text and len(version_text) < 10: # strip off the first few characters that will be "day, " version_label += " ({0})".format(date_text[5:]) self.lbl_version.setText(self.lbl_version.text() + version_label) def closeEvent(self, event): self.presenter.save_on_closing() self.deleteLater() super(AboutView, self).closeEvent(event)
class FigureCanvas(QFrame): """ A basic widget on which can be painted a custom png, jpg, or svg image. """ def __init__(self, parent=None): super(FigureCanvas, self).__init__(parent) self.setLineWidth(2) self.setMidLineWidth(1) self.setStyleSheet("background-color: white") self.fig = None self.fmt = None self.fwidth, self.fheight = 200, 200 def clear_canvas(self): """Clear the figure that was painted on the widget.""" self.fig = None self.fmt = None self._qpix_buffer = [] self.repaint() def load_figure(self, fig, fmt): """ Load the figure from a png, jpg, or svg image, convert it in a QPixmap, and force a repaint of the widget. """ self.fig = fig self.fmt = fmt if fmt in ['image/png', 'image/jpeg']: self._qpix_orig = QPixmap() self._qpix_orig.loadFromData(fig, fmt.upper()) elif fmt == 'image/svg+xml': self._qpix_orig = QPixmap(svg_to_image(fig)) self._qpix_buffer = [self._qpix_orig] self.fwidth = self._qpix_orig.width() self.fheight = self._qpix_orig.height() def paintEvent(self, event): """Qt method override to paint a custom image on the Widget.""" super(FigureCanvas, self).paintEvent(event) # Prepare the rect on which the image is going to be painted : fw = self.frameWidth() rect = QRect(0 + fw, 0 + fw, self.size().width() - 2 * fw, self.size().height() - 2 * fw) if self.fig is None: return # Check/update the qpixmap buffer : qpix2paint = None for qpix in self._qpix_buffer: if qpix.size().width() == rect.width(): qpix2paint = qpix break else: if self.fmt in ['image/png', 'image/jpeg']: qpix2paint = self._qpix_orig.scaledToWidth( rect.width(), mode=Qt.SmoothTransformation) elif self.fmt == 'image/svg+xml': qpix2paint = QPixmap(svg_to_image(self.fig, rect.size())) self._qpix_buffer.append(qpix2paint) if qpix2paint is not None: # Paint the image on the widget : qp = QPainter() qp.begin(self) qp.drawPixmap(rect, qpix2paint) qp.end()
class FigureCanvas(QFrame): """ A basic widget on which can be painted a custom png, jpg, or svg image. """ def __init__(self, parent=None, background_color=None): super(FigureCanvas, self).__init__(parent) self.setLineWidth(2) self.setMidLineWidth(1) self.setObjectName("figcanvas") self.setStyleSheet( "#figcanvas {background-color:" + str(background_color) + "}") self.fig = None self.fmt = None self.fwidth, self.fheight = 200, 200 self._blink_flag = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu_requested) def context_menu_requested(self, event): """Popup context menu.""" if self.fig: pos = QPoint(event.x(), event.y()) context_menu = QMenu(self) context_menu.addAction(ima.icon('editcopy'), "Copy Image", self.copy_figure, QKeySequence( get_shortcut('plots', 'copy'))) context_menu.popup(self.mapToGlobal(pos)) @Slot() def copy_figure(self): """Copy figure to clipboard.""" if self.fmt in ['image/png', 'image/jpeg']: qpixmap = QPixmap() qpixmap.loadFromData(self.fig, self.fmt.upper()) QApplication.clipboard().setImage(qpixmap.toImage()) elif self.fmt == 'image/svg+xml': svg_to_clipboard(self.fig) else: return self.blink_figure() def blink_figure(self): """Blink figure once.""" if self.fig: self._blink_flag = not self._blink_flag self.repaint() if self._blink_flag: timer = QTimer() timer.singleShot(40, self.blink_figure) def clear_canvas(self): """Clear the figure that was painted on the widget.""" self.fig = None self.fmt = None self._qpix_buffer = [] self.repaint() def load_figure(self, fig, fmt): """ Load the figure from a png, jpg, or svg image, convert it in a QPixmap, and force a repaint of the widget. """ self.fig = fig self.fmt = fmt if fmt in ['image/png', 'image/jpeg']: self._qpix_orig = QPixmap() self._qpix_orig.loadFromData(fig, fmt.upper()) elif fmt == 'image/svg+xml': self._qpix_orig = QPixmap(svg_to_image(fig)) self._qpix_buffer = [self._qpix_orig] self.fwidth = self._qpix_orig.width() self.fheight = self._qpix_orig.height() def paintEvent(self, event): """Qt method override to paint a custom image on the Widget.""" super(FigureCanvas, self).paintEvent(event) # Prepare the rect on which the image is going to be painted : fw = self.frameWidth() rect = QRect(0 + fw, 0 + fw, self.size().width() - 2 * fw, self.size().height() - 2 * fw) if self.fig is None or self._blink_flag: return # Check/update the qpixmap buffer : qpix2paint = None for qpix in self._qpix_buffer: if qpix.size().width() == rect.width(): qpix2paint = qpix break else: if self.fmt in ['image/png', 'image/jpeg']: qpix2paint = self._qpix_orig.scaledToWidth( rect.width(), mode=Qt.SmoothTransformation) elif self.fmt == 'image/svg+xml': qpix2paint = QPixmap(svg_to_image(self.fig, rect.size())) self._qpix_buffer.append(qpix2paint) if qpix2paint is not None: # Paint the image on the widget : qp = QPainter() qp.begin(self) qp.drawPixmap(rect, qpix2paint) qp.end()
class FigureCanvas(QFrame): """ A basic widget on which can be painted a custom png, jpg, or svg image. """ sig_context_menu_requested = Signal(QPoint) """ This signal is emitted to request a context menu. Parameters ---------- point: QPoint The QPoint in global coordinates where the menu was requested. """ def __init__(self, parent=None, background_color=None): super().__init__(parent) self.setLineWidth(2) self.setMidLineWidth(1) self.setObjectName("figcanvas") self.setStyleSheet( "#figcanvas {background-color:" + str(background_color) + "}") self.fig = None self.fmt = None self.fwidth, self.fheight = 200, 200 self._blink_flag = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect( self.sig_context_menu_requested) @Slot() def copy_figure(self): """Copy figure to clipboard.""" if self.fmt in ['image/png', 'image/jpeg']: qpixmap = QPixmap() qpixmap.loadFromData(self.fig, self.fmt.upper()) QApplication.clipboard().setImage(qpixmap.toImage()) elif self.fmt == 'image/svg+xml': svg_to_clipboard(self.fig) else: return self.blink_figure() def blink_figure(self): """Blink figure once.""" if self.fig: self._blink_flag = not self._blink_flag self.repaint() if self._blink_flag: timer = QTimer() timer.singleShot(40, self.blink_figure) def clear_canvas(self): """Clear the figure that was painted on the widget.""" self.fig = None self.fmt = None self._qpix_scaled = None self.repaint() def load_figure(self, fig, fmt): """ Load the figure from a png, jpg, or svg image, convert it in a QPixmap, and force a repaint of the widget. """ self.fig = fig self.fmt = fmt if fmt in ['image/png', 'image/jpeg']: self._qpix_orig = QPixmap() self._qpix_orig.loadFromData(fig, fmt.upper()) elif fmt == 'image/svg+xml': self._qpix_orig = QPixmap(svg_to_image(fig)) self._qpix_scaled = self._qpix_orig self.fwidth = self._qpix_orig.width() self.fheight = self._qpix_orig.height() def paintEvent(self, event): """Qt method override to paint a custom image on the Widget.""" super().paintEvent(event) # Prepare the rect on which the image is going to be painted. fw = self.frameWidth() rect = QRect(0 + fw, 0 + fw, self.size().width() - 2 * fw, self.size().height() - 2 * fw) if self.fig is None or self._blink_flag: return # Prepare the scaled qpixmap to paint on the widget. if (self._qpix_scaled is None or self._qpix_scaled.size().width() != rect.width()): if self.fmt in ['image/png', 'image/jpeg']: self._qpix_scaled = self._qpix_orig.scaledToWidth( rect.width(), mode=Qt.SmoothTransformation) elif self.fmt == 'image/svg+xml': self._qpix_scaled = QPixmap(svg_to_image( self.fig, rect.size())) if self._qpix_scaled is not None: # Paint the image on the widget. qp = QPainter() qp.begin(self) qp.drawPixmap(rect, self._qpix_scaled) qp.end()
class FigureCanvas(QFrame): """ A basic widget on which can be painted a custom png, jpg, or svg image. """ def __init__(self, parent=None, background_color=None): super(FigureCanvas, self).__init__(parent) self.setLineWidth(2) self.setMidLineWidth(1) self.setObjectName("figcanvas") self.setStyleSheet("#figcanvas {background-color:" + str(background_color) + "}") self.fig = None self.fmt = None self.fwidth, self.fheight = 200, 200 self._blink_flag = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu_requested) def context_menu_requested(self, event): """Popup context menu.""" if self.fig: pos = QPoint(event.x(), event.y()) context_menu = QMenu(self) context_menu.addAction(ima.icon('editcopy'), "Copy Image", self.copy_figure, QKeySequence(get_shortcut('plots', 'copy'))) context_menu.popup(self.mapToGlobal(pos)) @Slot() def copy_figure(self): """Copy figure to clipboard.""" if self.fmt in ['image/png', 'image/jpeg']: qpixmap = QPixmap() qpixmap.loadFromData(self.fig, self.fmt.upper()) QApplication.clipboard().setImage(qpixmap.toImage()) elif self.fmt == 'image/svg+xml': svg_to_clipboard(self.fig) else: return self.blink_figure() def blink_figure(self): """Blink figure once.""" if self.fig: self._blink_flag = not self._blink_flag self.repaint() if self._blink_flag: timer = QTimer() timer.singleShot(40, self.blink_figure) def clear_canvas(self): """Clear the figure that was painted on the widget.""" self.fig = None self.fmt = None self._qpix_buffer = [] self.repaint() def load_figure(self, fig, fmt): """ Load the figure from a png, jpg, or svg image, convert it in a QPixmap, and force a repaint of the widget. """ self.fig = fig self.fmt = fmt if fmt in ['image/png', 'image/jpeg']: self._qpix_orig = QPixmap() self._qpix_orig.loadFromData(fig, fmt.upper()) elif fmt == 'image/svg+xml': self._qpix_orig = QPixmap(svg_to_image(fig)) self._qpix_buffer = [self._qpix_orig] self.fwidth = self._qpix_orig.width() self.fheight = self._qpix_orig.height() def paintEvent(self, event): """Qt method override to paint a custom image on the Widget.""" super(FigureCanvas, self).paintEvent(event) # Prepare the rect on which the image is going to be painted : fw = self.frameWidth() rect = QRect(0 + fw, 0 + fw, self.size().width() - 2 * fw, self.size().height() - 2 * fw) if self.fig is None or self._blink_flag: return # Check/update the qpixmap buffer : qpix2paint = None for qpix in self._qpix_buffer: if qpix.size().width() == rect.width(): qpix2paint = qpix break else: if self.fmt in ['image/png', 'image/jpeg']: qpix2paint = self._qpix_orig.scaledToWidth( rect.width(), mode=Qt.SmoothTransformation) elif self.fmt == 'image/svg+xml': qpix2paint = QPixmap(svg_to_image(self.fig, rect.size())) self._qpix_buffer.append(qpix2paint) if qpix2paint is not None: # Paint the image on the widget : qp = QPainter() qp.begin(self) qp.drawPixmap(rect, qpix2paint) qp.end()
def __init__(self, parent): super(KiteIntegrationInfo, self).__init__(parent) # Images images_layout = QHBoxLayout() icon_filename = 'kite_completions' image_path = get_image_path(icon_filename) image = QPixmap(image_path) image_label = QLabel() image_label = QLabel() image_height = int(image.height() * DialogStyle.IconScaleFactor) image_width = int(image.width() * DialogStyle.IconScaleFactor) image = image.scaled(image_width, image_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) image_label.setPixmap(image) images_layout.addStretch() images_layout.addWidget(image_label) images_layout.addStretch() ilayout = QHBoxLayout() ilayout.addLayout(images_layout) # Label integration_label_title = QLabel( "Get better code completions in Spyder") integration_label_title.setStyleSheet( f"font-size: {DialogStyle.TitleFontSize}") integration_label_title.setWordWrap(True) integration_label = QLabel( _("Now Spyder can use Kite to provide better code " "completions for key packages in the scientific Python " "Ecosystem. Install Kite for a better editor experience in " "Spyder. <br><br>Kite is free to use but is not open " "source. <a href=\"{kite_url}\">Learn more about Kite </a>"). format(kite_url=KITE_SPYDER_URL)) integration_label.setStyleSheet( f"font-size: {DialogStyle.ContentFontSize}") integration_label.setOpenExternalLinks(True) integration_label.setWordWrap(True) integration_label.setFixedWidth(360) label_layout = QVBoxLayout() label_layout.addWidget(integration_label_title) label_layout.addWidget(integration_label) # Buttons install_button_color = QStylePalette.COLOR_ACCENT_2 install_button_hover = QStylePalette.COLOR_ACCENT_3 install_button_pressed = QStylePalette.COLOR_ACCENT_4 dismiss_button_color = QStylePalette.COLOR_BACKGROUND_4 dismiss_button_hover = QStylePalette.COLOR_BACKGROUND_5 dismiss_button_pressed = QStylePalette.COLOR_BACKGROUND_6 font_color = QStylePalette.COLOR_TEXT_1 buttons_layout = QHBoxLayout() install_button = QPushButton(_('Install Kite')) install_button.setAutoDefault(False) install_button.setStyleSheet( ("QPushButton {{ " "background-color: {background_color};" "border-color: {border_color};" "font-size: {font_size};" "color: {font_color};" "padding: {padding}}}" "QPushButton:hover:!pressed {{ " "background-color: {color_hover}}}" "QPushButton:pressed {{ " "background-color: {color_pressed}}}").format( background_color=install_button_color, border_color=install_button_color, font_size=DialogStyle.ButtonsFontSize, font_color=font_color, padding=DialogStyle.ButtonsPadding, color_hover=install_button_hover, color_pressed=install_button_pressed)) dismiss_button = QPushButton(_('Dismiss')) dismiss_button.setAutoDefault(False) dismiss_button.setStyleSheet( ("QPushButton {{ " "background-color: {background_color};" "border-color: {border_color};" "font-size: {font_size};" "color: {font_color};" "padding: {padding}}}" "QPushButton:hover:!pressed {{ " "background-color: {color_hover}}}" "QPushButton:pressed {{ " "background-color: {color_pressed}}}").format( background_color=dismiss_button_color, border_color=dismiss_button_color, font_size=DialogStyle.ButtonsFontSize, font_color=font_color, padding=DialogStyle.ButtonsPadding, color_hover=dismiss_button_hover, color_pressed=dismiss_button_pressed)) buttons_layout.addStretch() buttons_layout.addWidget(install_button) if not MAC: buttons_layout.addSpacing(10) buttons_layout.addWidget(dismiss_button) # Buttons with label vertical_layout = QVBoxLayout() if not MAC: vertical_layout.addStretch() vertical_layout.addLayout(label_layout) vertical_layout.addSpacing(20) vertical_layout.addLayout(buttons_layout) vertical_layout.addStretch() else: vertical_layout.addLayout(label_layout) vertical_layout.addLayout(buttons_layout) general_layout = QHBoxLayout() general_layout.addStretch() general_layout.addLayout(ilayout) general_layout.addSpacing(15) general_layout.addLayout(vertical_layout) general_layout.addStretch() self.setLayout(general_layout) # Signals install_button.clicked.connect(self.sig_install_button_clicked) dismiss_button.clicked.connect(self.sig_dismiss_button_clicked) self.setStyleSheet( f"background-color: {QStylePalette.COLOR_BACKGROUND_2}") self.setContentsMargins(18, 40, 18, 40) if not MAC: self.setFixedSize(800, 350)