def __init__(self, parent): super(DockTitleBar, self).__init__(parent) icon_size = QApplication.style().standardIcon( QStyle.SP_TitleBarNormalButton).actualSize(QSize(100, 100)) button_size = icon_size + QSize(8, 8) left_spacer = QWidget(self) left_spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) if is_dark_interface(): left_spacer.setStyleSheet("background-color: #32414B") drag_button = DragButton(self, button_size) right_spacer = QWidget(self) right_spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) if is_dark_interface(): right_spacer.setStyleSheet("background-color: #32414B") close_button = CloseButton(self, button_size) close_button.clicked.connect(parent.sig_plugin_closed.emit) hlayout = QHBoxLayout(self) hlayout.setSpacing(0) hlayout.setContentsMargins(0, 0, 0, 0) hlayout.addWidget(left_spacer) hlayout.addWidget(drag_button) hlayout.addWidget(right_spacer) hlayout.addWidget(close_button) # To signal that dock widgets can be dragged from here self.setCursor(Qt.SizeAllCursor)
def get_icon_by_extension(fname, scale_factor): """Return the icon depending on the file extension""" application_icons = {} application_icons.update(BIN_FILES) application_icons.update(DOCUMENT_FILES) if osp.isdir(fname): return icon('DirOpenIcon', scale_factor) else: basename = osp.basename(fname) __, extension = osp.splitext(basename.lower()) mime_type, __ = mime.guess_type(basename) icon_by_extension = icon('FileIcon', scale_factor) if extension in OFFICE_FILES: icon_by_extension = icon(OFFICE_FILES[extension], scale_factor) if extension in LANGUAGE_ICONS: icon_by_extension = icon(LANGUAGE_ICONS[extension], scale_factor) else: if extension == '.ipynb': if is_dark_interface(): icon_by_extension = QIcon( get_image_path('notebook_dark.svg')) else: icon_by_extension = QIcon( get_image_path('notebook_light.svg')) elif extension == '.tex': if is_dark_interface(): icon_by_extension = QIcon( get_image_path('file_type_tex.svg')) else: icon_by_extension = QIcon( get_image_path('file_type_light_tex.svg')) elif mime_type is not None: try: # Fix for issue 5080. Even though # mimetypes.guess_type documentation states that # the return value will be None or a tuple of # the form type/subtype, in the Windows registry, # .sql has a mimetype of text\plain # instead of text/plain therefore mimetypes is # returning it incorrectly. file_type, bin_name = mime_type.split('/') except ValueError: file_type = 'text' if file_type == 'text': icon_by_extension = icon('TextFileIcon', scale_factor) elif file_type == 'audio': icon_by_extension = icon('AudioFileIcon', scale_factor) elif file_type == 'video': icon_by_extension = icon('VideoFileIcon', scale_factor) elif file_type == 'image': icon_by_extension = icon('ImageFileIcon', scale_factor) elif file_type == 'application': if bin_name in application_icons: icon_by_extension = icon(application_icons[bin_name], scale_factor) return icon_by_extension
def get_icon(name, default=None, resample=False, adjust_for_interface=False): """Return image inside a QIcon object. default: default image name or icon resample: if True, manually resample icon pixmaps for usual sizes (16, 24, 32, 48, 96, 128, 256). This is recommended for QMainWindow icons created from SVG images on non-Windows platforms due to a Qt bug. See spyder-ide/spyder#1314. """ if adjust_for_interface: name = (name + '_dark.svg' if is_dark_interface() else name + '_light.svg') icon_path = get_image_path(name, default=None) if icon_path is not None: icon = QIcon(icon_path) elif isinstance(default, QIcon): icon = default elif default is None: try: icon = get_std_icon(name[:-4]) except AttributeError: icon = QIcon(get_image_path(name, default)) else: icon = QIcon(get_image_path(name, default)) if resample: icon0 = QIcon() for size in (16, 24, 32, 48, 96, 128, 256, 512): icon0.addPixmap(icon.pixmap(size, size)) return icon0 else: return icon
def __init__(self, widget): super().__init__() self.widget = widget # Setting interface theme if is_dark_interface(): self.setStyleSheet(qdarkstyle.load_stylesheet())
def register_plugin(self): """Register plugin in Spyder's main window.""" super(UnitTestPlugin, self).register_plugin() # Get information from Spyder proper into plugin self.update_pythonpath() self.update_default_wdir() self.unittestwidget.use_dark_interface(is_dark_interface()) # Connect to relevant signals self.main.sig_pythonpath_changed.connect(self.update_pythonpath) self.main.workingdirectory.set_explorer_cwd.connect( self.update_default_wdir) self.main.projects.sig_project_created.connect( self.handle_project_change) self.main.projects.sig_project_loaded.connect( self.handle_project_change) self.main.projects.sig_project_closed.connect( self.handle_project_change) self.unittestwidget.sig_newconfig.connect(self.save_config) self.unittestwidget.sig_edit_goto.connect(self.goto_in_editor) # Create action and add it to Spyder's menu unittesting_act = create_action( self, _("Run unit tests"), icon=ima.icon('profiler'), shortcut="Shift+Alt+F11", triggered=self.maybe_configure_and_start) self.main.run_menu_actions += [unittesting_act] self.main.editor.pythonfile_dependent_actions += [unittesting_act] # Save all files before running tests self.unittestwidget.pre_test_hook = self.main.editor.save_all
def __init__(self, plugin): QMainWindow.__init__(self) self.plugin = plugin # Setting interface theme if is_dark_interface(): self.setStyleSheet(qdarkstyle.load_stylesheet_from_environment())
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 page(self, title, contents): """Format an HTML page.""" rich_text_font = get_font(option="rich_font").family() plain_text_font = get_font(option="font").family() if is_dark_interface(): css_path = "static/css/dark_pydoc.css" else: css_path = "static/css/light_pydoc.css" css_link = ('<link rel="stylesheet" type="text/css" href="%s">' % css_path) code_style = ('<style>code {font-family: "%s"}</style>' % plain_text_font) html_page = '''\ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html><head><title>Pydoc: %s</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> %s%s</head><body style="clear:both;font-family:'%s'"> %s<div style="clear:both;padding-top:.7em;">%s</div> </body></html>''' % (title, css_link, code_style, rich_text_font, html_navbar(), contents) return html_page
def __init__(self, parent=None): super(BasePluginWidgetMixin, self).__init__() # Actions to add to the Options menu self._plugin_actions = None # Attribute to keep track if the plugin is undocked in a # separate window self._undocked_window = None self._ismaximized = False self._default_margins = None self._isvisible = False self.shortcut = None # Options buttons self.options_button = create_toolbutton(self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) # Don't show menu arrow and remove padding if is_dark_interface(): self.options_button.setStyleSheet( ("QToolButton::menu-indicator{image: none;}\n" "QToolButton{padding: 3px;}")) else: self.options_button.setStyleSheet( "QToolButton::menu-indicator{image: none;}") # Options menu self._options_menu = QMenu(self) # We decided to create our own toggle action instead of using # the one that comes with dockwidget because it's not possible # to raise and focus the plugin with it. self._toggle_view_action = None # Default actions for Options menu self._dock_action = create_action( self, _("Dock"), icon=ima.icon('dock'), tip=_("Dock the pane"), triggered=self._close_window) self._undock_action = create_action( self, _("Undock"), icon=ima.icon('undock'), tip=_("Undock the pane"), triggered=self._create_window) self._close_plugin_action = create_action( self, _("Close"), icon=ima.icon('close_pane'), tip=_("Close the pane"), triggered=self._plugin_closed)
def get_toolbar_buttons(self): """Return toolbar buttons list.""" buttons = [] # Code to add the stop button if self.stop_button is None: self.stop_button = create_toolbutton( self, text=_("Stop"), icon=self.stop_icon, tip=_("Stop the current command")) self.disable_stop_button() # set click event handler self.stop_button.clicked.connect(self.stop_button_click_handler) if is_dark_interface(): self.stop_button.setStyleSheet("QToolButton{padding: 3px;}") if self.stop_button is not None: buttons.append(self.stop_button) # Reset namespace button if self.reset_button is None: self.reset_button = create_toolbutton( self, text=_("Remove"), icon=ima.icon('editdelete'), tip=_("Remove all variables"), triggered=self.reset_namespace) if is_dark_interface(): self.reset_button.setStyleSheet("QToolButton{padding: 3px;}") if self.reset_button is not None: buttons.append(self.reset_button) if self.options_button is None: options = self.get_options_menu() if options: self.options_button = create_toolbutton(self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) menu = QMenu(self) add_actions(menu, options) self.options_button.setMenu(menu) if self.options_button is not None: buttons.append(self.options_button) return buttons
def get_toolbar_buttons(self): """Return toolbar buttons list.""" buttons = [] # Code to add the stop button if self.stop_button is None: self.stop_button = create_toolbutton( self, text=_("Stop"), icon=self.stop_icon, tip=_("Stop the current command")) self.disable_stop_button() # set click event handler self.stop_button.clicked.connect(self.stop_button_click_handler) if is_dark_interface(): self.stop_button.setStyleSheet("QToolButton{padding: 3px;}") if self.stop_button is not None: buttons.append(self.stop_button) # Reset namespace button if self.reset_button is None: self.reset_button = create_toolbutton( self, text=_("Remove"), icon=ima.icon('editdelete'), tip=_("Remove all variables"), triggered=self.reset_namespace) if is_dark_interface(): self.reset_button.setStyleSheet("QToolButton{padding: 3px;}") if self.reset_button is not None: buttons.append(self.reset_button) if self.options_button is None: options = self.get_options_menu() if options: self.options_button = create_toolbutton( self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) menu = QMenu(self) add_actions(menu, options) self.options_button.setMenu(menu) if self.options_button is not None: buttons.append(self.options_button) return buttons
def set_layout(self): """Set layout for default widgets.""" # Icon if self.show_icon: self._icon = self.get_icon() self._pixmap = None self._icon_size = QSize(16, 16) # Should this be adjustable? self.label_icon = QLabel() self.set_icon() # Label if self.show_label: self.label_value = QLabel() self.set_value('') # See spyder-ide/spyder#9044. self.text_font = QFont(QFont().defaultFamily(), weight=QFont.Normal) self.label_value.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.label_value.setFont(self.text_font) # Custom widget if self.CUSTOM_WIDGET_CLASS: if not issubclass(self.CUSTOM_WIDGET_CLASS, QWidget): raise SpyderAPIError( 'Any custom status widget must subclass QWidget!') self.custom_widget = self.CUSTOM_WIDGET_CLASS(self._parent) # Spinner if self.show_spinner: self.spinner = create_waitspinner(size=14, parent=self) self.spinner.hide() # Layout setup layout = QHBoxLayout(self) layout.setSpacing(0) # Reduce space between icon and label if self.show_icon: layout.addWidget(self.label_icon) if self.show_label: layout.addWidget(self.label_value) if self.custom_widget: layout.addWidget(self.custom_widget) if self.show_spinner: layout.addWidget(self.spinner) if is_dark_interface(): layout.addSpacing(0) else: layout.addSpacing(10) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignVCenter) # Setup self.update_tooltip()
def add_image_path(self, path): """Add path to the image path list.""" if not osp.isdir(path): return for dirpath, __, _filenames in os.walk(path): if is_dark_interface() and osp.basename(dirpath) == 'light': continue elif not is_dark_interface() and osp.basename(dirpath) == 'dark': continue for filename in _filenames: if filename.startswith('.'): continue name, __ = osp.splitext(osp.basename(filename)) complete_path = osp.join(dirpath, filename) if name in self.IMG_PATH: warnings.warn( f'The icon located in {complete_path} is overriding ' f'the existing {name}') self.IMG_PATH[name] = complete_path
def _complete_options(self): """Find available completion options.""" text = to_text_string(self.currentText()) opts = glob.glob(text + "*") opts = sorted([opt for opt in opts if osp.isdir(opt)]) completer = QCompleter(opts, self) if is_dark_interface(): dark_qss = qdarkstyle.load_stylesheet_from_environment() completer.popup().setStyleSheet(dark_qss) self.setCompleter(completer) return opts
def get_icon_by_extension(fname, scale_factor): """Return the icon depending on the file extension""" application_icons = {} application_icons.update(BIN_FILES) application_icons.update(DOCUMENT_FILES) if osp.isdir(fname): return icon('DirOpenIcon', scale_factor) else: basename = osp.basename(fname) __, extension = osp.splitext(basename.lower()) mime_type, __ = mime.guess_type(basename) icon_by_extension = icon('FileIcon', scale_factor) if extension in OFFICE_FILES: icon_by_extension = icon(OFFICE_FILES[extension], scale_factor) if extension in LANGUAGE_ICONS: icon_by_extension = icon(LANGUAGE_ICONS[extension], scale_factor) else: if extension == '.ipynb': if is_dark_interface(): icon_by_extension = QIcon( get_image_path('notebook_dark.svg')) else: icon_by_extension = QIcon( get_image_path('notebook_light.svg')) elif mime_type is not None: try: # Fix for issue 5080. Even though # mimetypes.guess_type documentation states that # the return value will be None or a tuple of # the form type/subtype, in the Windows registry, # .sql has a mimetype of text\plain # instead of text/plain therefore mimetypes is # returning it incorrectly. file_type, bin_name = mime_type.split('/') except ValueError: file_type = 'text' if file_type == 'text': icon_by_extension = icon('TextFileIcon', scale_factor) elif file_type == 'audio': icon_by_extension = icon('AudioFileIcon', scale_factor) elif file_type == 'video': icon_by_extension = icon('VideoFileIcon', scale_factor) elif file_type == 'image': icon_by_extension = icon('ImageFileIcon', scale_factor) elif file_type == 'application': if bin_name in application_icons: icon_by_extension = icon( application_icons[bin_name], scale_factor) return icon_by_extension
def __init__(self, parent): super().__init__(parent) newcb = self.create_checkbox introspection_group = QGroupBox(_("Basic features")) goto_definition_box = newcb( _("Enable Go to definition"), 'jedi_definition', tip=_("If enabled, left-clicking on an object name while \n" "pressing the {} key will go to that object's definition\n" "(if resolved).").format(self.CTRL)) follow_imports_box = newcb(_("Follow imports when going to a " "definition"), 'jedi_definition/follow_imports') show_signature_box = newcb(_("Show calltips"), 'jedi_signature_help') enable_hover_hints_box = newcb( _("Enable hover hints"), 'enable_hover_hints', tip=_("If enabled, hovering the mouse pointer over an object\n" "name will display that object's signature and/or\n" "docstring (if present).")) introspection_layout = QVBoxLayout() introspection_layout.addWidget(goto_definition_box) introspection_layout.addWidget(follow_imports_box) introspection_layout.addWidget(show_signature_box) introspection_layout.addWidget(enable_hover_hints_box) introspection_group.setLayout(introspection_layout) goto_definition_box.toggled.connect(follow_imports_box.setEnabled) # Advanced group advanced_group = QGroupBox(_("Advanced")) modules_textedit = self.create_textedit( _("Preload the following modules to make completion faster " "and more accurate:"), 'preload_modules' ) if is_dark_interface(): modules_textedit.textbox.setStyleSheet( "border: 1px solid #32414B;" ) advanced_layout = QVBoxLayout() advanced_layout.addWidget(modules_textedit) advanced_group.setLayout(advanced_layout) layout = QVBoxLayout() layout.addWidget(introspection_group) layout.addWidget(advanced_group) layout.addStretch(1) self.setLayout(layout)
def show_fig_outline_in_viewer(self, state): """Draw a frame around the figure viewer if state is True.""" if state is True: if is_dark_interface(): self.figviewer.figcanvas.setStyleSheet( "FigureCanvas{border: 2px solid %s;}" % qdarkstyle.palette.DarkPalette.COLOR_BACKGROUND_NORMAL) else: self.figviewer.figcanvas.setStyleSheet( "FigureCanvas{border: 2px solid %s;}" % self.figviewer.figcanvas.palette().shadow().color().name()) else: self.figviewer.figcanvas.setStyleSheet( "FigureCanvas{border: 0px;}")
def __init__(self, parent, button_size): super(QToolButton, self).__init__(parent) self.parent = parent # Style self.setMaximumSize(button_size) self.setAutoRaise(True) self.setIcon(ima.icon('drag-horizontal')) if is_dark_interface(): self.setStyleSheet("QToolButton {" "border-radius: 0px;" "border: 0px;" "background-color: #32414B;}") else: self.setStyleSheet("QToolButton {border: 0px;}")
def _calculate_figure_canvas_width(self): """ Calculate the width the thumbnails need to have to fit the scrollarea. """ extra_padding = 10 if sys.platform == 'darwin' else 0 figure_canvas_width = ( self.scrollarea.width() - 2 * self.lineWidth() - self.scrollarea.viewportMargins().left() - self.scrollarea.viewportMargins().right() - extra_padding - self.scrollarea.verticalScrollBar().sizeHint().width()) if is_dark_interface(): # This is required to take into account some hard-coded padding # and margin in qdarkstyle. figure_canvas_width = figure_canvas_width - 6 return figure_canvas_width
def get_kite_icon(scale_factor=0.85): """Return the Kite logo taking into account the theme of the interface.""" icon_path = 'kite_light.svg' if is_dark_interface(): icon_path = 'kite_dark.svg' pixmap = QPixmap(get_image_path(icon_path)) if scale_factor is not None: pixmap_height = 16 * scale_factor pixmap_width = 16 * scale_factor pixmap = pixmap.scaled(pixmap_width, pixmap_height, aspectRatioMode=Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) kite_icon = QIcon(pixmap) return kite_icon
def __init__(self, main=None): """Bind widget to a QMainWindow instance.""" super(PluginWidget, self).__init__(main) assert self.CONF_SECTION is not None self.dockwidget = None self.undocked_window = None # Check compatibility check_compatibility, message = self.check_compatibility() if not check_compatibility: self.show_compatibility_message(message) self.PLUGIN_PATH = os.path.dirname(inspect.getfile(self.__class__)) self.main = main self.default_margins = None self.plugin_actions = None self.ismaximized = False self.isvisible = False # Options button and menu self.options_button = create_toolbutton(self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) # Don't show menu arrow and remove padding if is_dark_interface(): self.options_button.setStyleSheet( ("QToolButton::menu-indicator{image: none;}\n" "QToolButton{padding: 3px;}")) else: self.options_button.setStyleSheet( "QToolButton::menu-indicator{image: none;}") self.options_menu = QMenu(self) # NOTE: Don't use the default option of CONF.get to assign a # None shortcut to plugins that don't have one. That will mess # the creation of our Keyboard Shortcuts prefs page try: self.shortcut = CONF.get('shortcuts', '_/switch to %s' % self.CONF_SECTION) except configparser.NoOptionError: pass # We decided to create our own toggle action instead of using # the one that comes with dockwidget because it's not possible # to raise and focus the plugin with it. self.toggle_view_action = None
def _update_stylesheet(self, widget): """Update the background stylesheet to make it lighter.""" if is_dark_interface(): css = qdarkstyle.load_stylesheet_from_environment() widget.setStyleSheet(css) palette = widget.palette() background = palette.color(palette.Window).lighter(150).name() border = palette.color(palette.Window).lighter(200).name() name = widget.__class__.__name__ widget.setObjectName(name) extra_css = ''' {0}#{0} {{ background-color:{1}; border: 1px solid {2}; }}'''.format(name, background, border) widget.setStyleSheet(css + extra_css)
def __init__(self, parent, button_size): super(QToolButton, self).__init__(parent) self.parent = parent # Style self.setMaximumSize(button_size) self.setAutoRaise(True) self.setIcon(ima.icon('drag-horizontal')) if is_dark_interface(): self.setStyleSheet(("QToolButton {{" "border-radius: 0px;" "border: 0px;" "background-color: {color};}}").format( color=QStylePalette.COLOR_BACKGROUND_3)) else: self.setStyleSheet("QToolButton {border: 0px;}")
def _calculate_figure_canvas_width(self, thumbnail): """ Calculate the witdh the thumbnail's figure canvas need to have for the thumbnail to fit the scrollarea. """ extra_padding = 10 if sys.platform == 'darwin' else 0 figure_canvas_width = (self.scrollarea.width() - 2 * self.lineWidth() - self.scrollarea.viewportMargins().left() - self.scrollarea.viewportMargins().right() - thumbnail.savefig_btn.width() - thumbnail.layout().spacing() - extra_padding) if is_dark_interface(): # This is required to take into account some hard-coded padding # and margin in qdarkstyle. figure_canvas_width = figure_canvas_width - 6 return figure_canvas_width
def highlight_canvas(self, highlight): """ Set a colored frame around the FigureCanvas if highlight is True. """ colorname = self.canvas.palette().highlight().color().name() if highlight: # Highlighted figure is not clear in dark mode with blue color. # See spyder-ide/spyder#10255. if is_dark_interface(): self.canvas.setStyleSheet( "FigureCanvas{border: 2px solid %s;}" % "#148CD2") else: self.canvas.setStyleSheet( "FigureCanvas{border: 2px solid %s;}" % colorname) else: self.canvas.setStyleSheet("FigureCanvas{}")
def page(self, title, contents): """Format an HTML page.""" if is_dark_interface(): css_path = "static/css/dark_pydoc.css" else: css_path = "static/css/light_pydoc.css" css_link = ('<link rel="stylesheet" type="text/css" href="%s">' % css_path) html_page = '''\ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html><head><title>Pydoc: %s</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> %s</head><body>%s<div style="clear:both;padding-top:.7em;">%s</div> </body></html>''' % (title, css_link, html_navbar(), contents) return html_page
def highlight_canvas(self, highlight): """ Set a colored frame around the FigureCanvas if highlight is True. """ if highlight: # Highlighted figure is not clear in dark mode with blue color. # See spyder-ide/spyder#10255. if is_dark_interface(): self.canvas.setStyleSheet( "FigureCanvas{border: 2px solid %s;}" % qdarkstyle.palette.DarkPalette.COLOR_SELECTION_LIGHT) else: self.canvas.setStyleSheet( "FigureCanvas{border: 2px solid %s;}" % self.canvas.palette().highlight().color().name()) else: self.canvas.setStyleSheet("FigureCanvas{}")
def __init__(self, parent, button_size): super(QToolButton, self).__init__(parent) # Style self.setMaximumSize(button_size) self.setAutoRaise(True) self.setCursor(Qt.ArrowCursor) if is_dark_interface(): self.setStyleSheet("QToolButton {" "border-radius: 0px;" "border: 0px;" "image: url(:/qss_icons/rc/close.png);" "background-color: #32414B;}" "QToolButton:hover {" "image: url(:/qss_icons/rc/close-hover.png);}") else: self.setIcon(QApplication.style().standardIcon( QStyle.SP_DockWidgetCloseButton))
def __init__(self, parent=None, spinner=False): """Status bar widget base.""" super().__init__(parent) # Variables self.value = None self._parent = parent # Widget self._icon = self.get_icon() self._pixmap = None self._icon_size = QSize(16, 16) # Should this be adjustable? self.label_icon = QLabel() self.label_value = QLabel() self.spinner = None if spinner: self.spinner = create_waitspinner(size=14, parent=self) # Layout setup layout = QHBoxLayout(self) layout.setSpacing(0) # Reduce space between icon and label layout.addWidget(self.label_icon) layout.addWidget(self.label_value) if spinner: layout.addWidget(self.spinner) self.spinner.hide() if is_dark_interface(): layout.addSpacing(0) else: layout.addSpacing(10) layout.setContentsMargins(0, 0, 0, 0) # Widget setup self.set_icon() # See spyder-ide/spyder#9044. self.text_font = QFont(QFont().defaultFamily(), weight=QFont.Normal) self.label_value.setAlignment(Qt.AlignRight) self.label_value.setFont(self.text_font) # Setup self.set_value('') self.update_tooltip()
def _setup_menu(self, show_callable_attributes=False, show_special_attributes=False): """Sets up the main menu.""" self.tools_layout = QHBoxLayout() callable_attributes = create_toolbutton( self, text=_("Show callable attributes"), icon=ima.icon("class"), toggled=self._toggle_show_callable_attributes_action) callable_attributes.setCheckable(True) callable_attributes.setChecked(show_callable_attributes) self.tools_layout.addWidget(callable_attributes) special_attributes = create_toolbutton( self, text=_("Show __special__ attributes"), icon=ima.icon("private2"), toggled=self._toggle_show_special_attributes_action) special_attributes.setCheckable(True) special_attributes.setChecked(show_special_attributes) self.tools_layout.addWidget(special_attributes) self.tools_layout.addStretch() self.options_button = create_toolbutton(self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) self.show_cols_submenu = QMenu(self) self.options_button.setMenu(self.show_cols_submenu) # Don't show menu arrow and remove padding if is_dark_interface(): self.options_button.setStyleSheet( ("QToolButton::menu-indicator{image: none;}\n" "QToolButton{padding: 3px;}")) else: self.options_button.setStyleSheet( "QToolButton::menu-indicator{image: none;}") self.tools_layout.addWidget(self.options_button)
def create_waitspinner(size=32, n=11, parent=None): """ Create a wait spinner with the specified size built with n circling dots. """ dot_padding = 1 # To calculate the size of the dots, we need to solve the following # system of two equations in two variables. # (1) middle_circumference = pi * (size - dot_size) # (2) middle_circumference = n * (dot_size + dot_padding) dot_size = (pi * size - n * dot_padding) / (n + pi) inner_radius = (size - 2 * dot_size) / 2 spinner = QWaitingSpinner(parent, centerOnParent=False) spinner.setTrailSizeDecreasing(True) spinner.setNumberOfLines(n) spinner.setLineLength(dot_size) spinner.setLineWidth(dot_size) spinner.setInnerRadius(inner_radius) spinner.setColor(Qt.white if is_dark_interface() else Qt.black) return spinner
def __init__(self, parent, testing=False): """Constructor.""" if testing: self.CONF_FILE = False SpyderPluginWidget.__init__(self, parent) self.testing = testing self.fileswitcher_dlg = None self.main = parent self.recent_notebooks = self.get_option('recent_notebooks', default=[]) self.recent_notebook_menu = QMenu(_("Open recent"), self) layout = QVBoxLayout() new_notebook_btn = create_toolbutton(self, icon=ima.icon('options_more'), tip=_('Open a new notebook'), triggered=self.create_new_client) menu_btn = create_toolbutton(self, icon=ima.icon('tooloptions'), tip=_('Options')) self.menu_actions = self.get_plugin_actions() menu_btn.setMenu(self._options_menu) menu_btn.setPopupMode(menu_btn.InstantPopup) corner_widgets = {Qt.TopRightCorner: [new_notebook_btn, menu_btn]} dark_theme = is_dark_interface() self.server_manager = ServerManager(dark_theme) self.tabwidget = NotebookTabWidget( self, self.server_manager, menu=self._options_menu, actions=self.menu_actions, corner_widgets=corner_widgets, dark_theme=dark_theme) self.tabwidget.currentChanged.connect(self.refresh_plugin) layout.addWidget(self.tabwidget) self.setLayout(layout)
def __init__(self, parent=None, inline=True, offset=0, force_float=False): QDialog.__init__(self, parent=parent) self._parent = parent self._text = None self._valid = None self._offset = offset # TODO: add this as an option in the General Preferences? self._force_float = force_float self._help_inline = _(""" <b>Numpy Array/Matrix Helper</b><br> Type an array in Matlab : <code>[1 2;3 4]</code><br> or Spyder simplified syntax : <code>1 2;3 4</code> <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two spaces or two tabs to generate a ';'. """) self._help_table = _(""" <b>Numpy Array/Matrix Helper</b><br> Enter an array in the table. <br> Use Tab to move between cells. <br><br> Hit 'Enter' for array or 'Ctrl+Enter' for matrix. <br><br> <b>Hint:</b><br> Use two tabs at the end of a row to move to the next row. """) # Widgets self._button_warning = QToolButton() self._button_help = HelperToolButton() self._button_help.setIcon(ima.icon('MessageBoxInformation')) if is_dark_interface(): style = """ QToolButton { border: 1px solid grey; padding:0px; border-radius: 2px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #32414B, stop: 1 #16252B); } """ else: style = """ QToolButton { border: 1px solid grey; padding:0px; border-radius: 2px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f6f7fa, stop: 1 #dadbde); } """ self._button_help.setStyleSheet(style) if inline: self._button_help.setToolTip(self._help_inline) self._text = NumpyArrayInline(self) self._widget = self._text else: self._button_help.setToolTip(self._help_table) self._table = NumpyArrayTable(self) self._widget = self._table style = """ QDialog { margin:0px; border: 1px solid grey; padding:0px; border-radius: 2px; }""" self.setStyleSheet(style) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self._button_warning.setStyleSheet(style) # widget setup self.setWindowFlags(Qt.Window | Qt.Dialog | Qt.FramelessWindowHint) self.setModal(True) self.setWindowOpacity(0.90) self._widget.setMinimumWidth(200) # layout self._layout = QHBoxLayout() self._layout.addWidget(self._widget) self._layout.addWidget(self._button_warning, 1, Qt.AlignTop) self._layout.addWidget(self._button_help, 1, Qt.AlignTop) self.setLayout(self._layout) self._widget.setFocus()
# Third party imports from qtpy.QtCore import Qt, Slot from qtpy.QtWidgets import QGroupBox, QInputDialog, QLabel, QVBoxLayout # Local imports from spyder.config.base import _ from spyder.config.gui import is_dark_interface from spyder.api.plugins import SpyderPluginWidget from spyder.api.preferences import PluginConfigPage from spyder.utils import icon_manager as ima from spyder.utils.programs import is_module_installed from spyder.utils.qthelpers import create_action, MENU_SEPARATOR from .widgets.pylintgui import PylintWidget if is_dark_interface(): MAIN_TEXT_COLOR = 'white' MAIN_PREVRATE_COLOR = 'white' else: MAIN_TEXT_COLOR = '#444444' MAIN_PREVRATE_COLOR = '#666666' class PylintConfigPage(PluginConfigPage): def setup_page(self): settings_group = QGroupBox(_("Settings")) save_box = self.create_checkbox(_("Save file before analyzing it"), 'save_before', default=True) hist_group = QGroupBox(_("History")) hist_label1 = QLabel(_("The following option will be applied at next "
def update_qt_style_combobox(self): """Enable/disable the Qt style combobox.""" if is_dark_interface(): self.style_combobox.setEnabled(False) else: self.style_combobox.setEnabled(True)
def setup_page(self): newcb = self.create_checkbox # --- Introspection --- # Basic features group basic_features_group = QGroupBox(_("Basic features")) completion_box = newcb(_("Enable code completion"), 'code_completion') goto_definition_box = newcb( _("Enable Go to definition"), 'jedi_definition', tip=_("If this option is enabled, left-clicking on\n" "an object name while pressing the {} key will go to\n" "that object's definition (if resolved).".format(self.CTRL))) follow_imports_box = newcb(_("Follow imports when going to a " "definition"), 'jedi_definition/follow_imports') show_signature_box = newcb(_("Show calltips"), 'jedi_signature_help') basic_features_layout = QVBoxLayout() basic_features_layout.addWidget(completion_box) basic_features_layout.addWidget(goto_definition_box) basic_features_layout.addWidget(follow_imports_box) basic_features_layout.addWidget(show_signature_box) basic_features_group.setLayout(basic_features_layout) # Advanced group advanced_group = QGroupBox(_("Advanced")) modules_textedit = self.create_textedit( _("Preload the following modules to make completion faster " "and more accurate:"), 'preload_modules' ) if is_dark_interface(): modules_textedit.textbox.setStyleSheet( "border: 1px solid #32414B;" ) advanced_layout = QVBoxLayout() advanced_layout.addWidget(modules_textedit) advanced_group.setLayout(advanced_layout) # --- Linting --- # Linting options linting_label = QLabel(_("Spyder can optionally highlight syntax " "errors and possible problems with your " "code in the editor.")) linting_label.setOpenExternalLinks(True) linting_label.setWordWrap(True) linting_check = self.create_checkbox( _("Enable basic linting"), 'pyflakes') linting_complexity_box = self.create_checkbox( _("Enable complexity linting with " "the Mccabe package"), 'mccabe') # Linting layout linting_layout = QVBoxLayout() linting_layout.addWidget(linting_label) linting_layout.addWidget(linting_check) linting_layout.addWidget(linting_complexity_box) linting_widget = QWidget() linting_widget.setLayout(linting_layout) # --- Code style tab --- # Code style label pep_url = ( '<a href="https://www.python.org/dev/peps/pep-0008">PEP 8</a>') code_style_codes_url = _( "<a href='http://pycodestyle.pycqa.org/en/stable" "/intro.html#error-codes'>pycodestyle error codes</a>") code_style_label = QLabel( _("Spyder can use pycodestyle to analyze your code for " "conformance to the {} convention. You can also " "manually show or hide specific warnings by their " "{}.").format(pep_url, code_style_codes_url)) code_style_label.setOpenExternalLinks(True) code_style_label.setWordWrap(True) # Code style checkbox code_style_check = self.create_checkbox( _("Enable code style linting"), 'pycodestyle') # Code style options self.code_style_filenames_match = self.create_lineedit( _("Only check filenames matching these patterns:"), 'pycodestyle/filename', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Check Python files: *.py")) self.code_style_exclude = self.create_lineedit( _("Exclude files or directories matching these patterns:"), 'pycodestyle/exclude', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Exclude all test files: (?!test_).*\\.py")) code_style_select = self.create_lineedit( _("Show the following errors or warnings:").format( code_style_codes_url), 'pycodestyle/select', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: E113, W391")) code_style_ignore = self.create_lineedit( _("Ignore the following errors or warnings:"), 'pycodestyle/ignore', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: E201, E303")) code_style_max_line_length = self.create_spinbox( _("Maximum allowed line length:"), None, 'pycodestyle/max_line_length', min_=10, max_=500, step=1, tip=_("Default is 79")) # Code style layout code_style_g_layout = QGridLayout() code_style_g_layout.addWidget( self.code_style_filenames_match.label, 1, 0) code_style_g_layout.addWidget( self.code_style_filenames_match.textbox, 1, 1) code_style_g_layout.addWidget(self.code_style_exclude.label, 2, 0) code_style_g_layout.addWidget(self.code_style_exclude.textbox, 2, 1) code_style_g_layout.addWidget(code_style_select.label, 3, 0) code_style_g_layout.addWidget(code_style_select.textbox, 3, 1) code_style_g_layout.addWidget(code_style_ignore.label, 4, 0) code_style_g_layout.addWidget(code_style_ignore.textbox, 4, 1) code_style_g_layout.addWidget(code_style_max_line_length.plabel, 5, 0) code_style_g_layout.addWidget( code_style_max_line_length.spinbox, 5, 1) # Set Code style options enabled/disabled code_style_g_widget = QWidget() code_style_g_widget.setLayout(code_style_g_layout) code_style_g_widget.setEnabled(self.get_option('pycodestyle')) code_style_check.toggled.connect(code_style_g_widget.setEnabled) # Code style layout code_style_layout = QVBoxLayout() code_style_layout.addWidget(code_style_label) code_style_layout.addWidget(code_style_check) code_style_layout.addWidget(code_style_g_widget) code_style_widget = QWidget() code_style_widget.setLayout(code_style_layout) # --- Docstring tab --- # Docstring style label numpy_url = ( "<a href='https://numpydoc.readthedocs.io/en/" "latest/format.html'>Numpy</a>") pep257_url = ( "<a href='https://www.python.org/dev/peps/pep-0257/'>PEP 257</a>") docstring_style_codes = _( "<a href='http://www.pydocstyle.org/en/stable" "/error_codes.html'>page</a>") docstring_style_label = QLabel( _("Here you can decide if you want to perform style analysis on " "your docstrings according to the {} or {} conventions. You can " "also decide if you want to show or ignore specific errors, " "according to the codes found on this {}.").format( numpy_url, pep257_url, docstring_style_codes)) docstring_style_label.setOpenExternalLinks(True) docstring_style_label.setWordWrap(True) # Docstring style checkbox docstring_style_check = self.create_checkbox( _("Enable docstring style linting"), 'pydocstyle') # Docstring style options docstring_style_convention = self.create_combobox( _("Choose the convention used to lint docstrings: "), (("Numpy", 'numpy'), ("PEP 257", 'pep257'), ("Custom", 'custom')), 'pydocstyle/convention') self.docstring_style_select = self.create_lineedit( _("Show the following errors:"), 'pydocstyle/select', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: D413, D414")) self.docstring_style_ignore = self.create_lineedit( _("Ignore the following errors:"), 'pydocstyle/ignore', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: D107, D402")) self.docstring_style_match = self.create_lineedit( _("Only check filenames matching these patterns:"), 'pydocstyle/match', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Skip test files: (?!test_).*\\.py")) self.docstring_style_match_dir = self.create_lineedit( _("Only check in directories matching these patterns:"), 'pydocstyle/match_dir', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Skip dot directories: [^\\.].*")) # Custom option handling docstring_style_convention.combobox.currentTextChanged.connect( self.setup_docstring_style_convention) current_convention = docstring_style_convention.combobox.currentText() self.setup_docstring_style_convention(current_convention) # Docstring style layout docstring_style_g_layout = QGridLayout() docstring_style_g_layout.addWidget( docstring_style_convention.label, 1, 0) docstring_style_g_layout.addWidget( docstring_style_convention.combobox, 1, 1) docstring_style_g_layout.addWidget( self.docstring_style_select.label, 2, 0) docstring_style_g_layout.addWidget( self.docstring_style_select.textbox, 2, 1) docstring_style_g_layout.addWidget( self.docstring_style_ignore.label, 3, 0) docstring_style_g_layout.addWidget( self.docstring_style_ignore.textbox, 3, 1) docstring_style_g_layout.addWidget( self.docstring_style_match.label, 4, 0) docstring_style_g_layout.addWidget( self.docstring_style_match.textbox, 4, 1) docstring_style_g_layout.addWidget( self.docstring_style_match_dir.label, 5, 0) docstring_style_g_layout.addWidget( self.docstring_style_match_dir.textbox, 5, 1) # Set Docstring style options enabled/disabled docstring_style_g_widget = QWidget() docstring_style_g_widget.setLayout(docstring_style_g_layout) docstring_style_g_widget.setEnabled(self.get_option('pydocstyle')) docstring_style_check.toggled.connect( docstring_style_g_widget.setEnabled) # Docstring style layout docstring_style_layout = QVBoxLayout() docstring_style_layout.addWidget(docstring_style_label) docstring_style_layout.addWidget(docstring_style_check) docstring_style_layout.addWidget(docstring_style_g_widget) docstring_style_widget = QWidget() docstring_style_widget.setLayout(docstring_style_layout) # --- Advanced tab --- # Advanced label advanced_label = QLabel( _("Please don't modify these values unless " "you know what you're doing!")) advanced_label.setWordWrap(True) advanced_label.setAlignment(Qt.AlignJustify) # Advanced options advanced_command_launch = self.create_lineedit( _("Command to launch the Python language server: "), 'advanced/command_launch', alignment=Qt.Horizontal, word_wrap=False) advanced_host = self.create_lineedit( _("IP Address and port to bind the server to: "), 'advanced/host', alignment=Qt.Horizontal, word_wrap=False) advanced_port = self.create_spinbox( ":", "", 'advanced/port', min_=1, max_=65535, step=1) # Advanced layout advanced_g_layout = QGridLayout() advanced_g_layout.addWidget(advanced_command_launch.label, 1, 0) advanced_g_layout.addWidget(advanced_command_launch.textbox, 1, 1) advanced_g_layout.addWidget(advanced_host.label, 2, 0) advanced_host_port_g_layout = QGridLayout() advanced_host_port_g_layout.addWidget(advanced_host.textbox, 1, 0) advanced_host_port_g_layout.addWidget(advanced_port.plabel, 1, 1) advanced_host_port_g_layout.addWidget(advanced_port.spinbox, 1, 2) advanced_g_layout.addLayout(advanced_host_port_g_layout, 2, 1) advanced_widget = QWidget() advanced_layout = QVBoxLayout() advanced_layout.addWidget(advanced_label) advanced_layout.addLayout(advanced_g_layout) advanced_widget.setLayout(advanced_layout) # --- Other servers tab --- # Section label servers_label = QLabel( _("Spyder uses the <a href=\"{lsp_url}\">Language Server " "Protocol</a> to provide code completion and linting " "for its Editor. Here, you can setup and configure LSP servers " "for languages other than Python, so Spyder can provide such " "features for those languages as well." ).format(lsp_url=LSP_URL)) servers_label.setOpenExternalLinks(True) servers_label.setWordWrap(True) servers_label.setAlignment(Qt.AlignJustify) # Servers table table_group = QGroupBox(_('Available servers:')) self.table = LSPServerTable(self, text_color=ima.MAIN_FG_COLOR) table_layout = QVBoxLayout() table_layout.addWidget(self.table) table_group.setLayout(table_layout) # Buttons self.reset_btn = QPushButton(_("Reset to default values")) self.new_btn = QPushButton(_("Set up a new server")) self.delete_btn = QPushButton(_("Delete currently selected server")) self.delete_btn.setEnabled(False) # Slots connected to buttons self.new_btn.clicked.connect(self.create_new_server) self.reset_btn.clicked.connect(self.reset_to_default) self.delete_btn.clicked.connect(self.delete_server) # Buttons layout btns = [self.new_btn, self.delete_btn, self.reset_btn] buttons_layout = QGridLayout() for i, btn in enumerate(btns): buttons_layout.addWidget(btn, i, 1) buttons_layout.setColumnStretch(0, 1) buttons_layout.setColumnStretch(1, 2) buttons_layout.setColumnStretch(2, 1) # Combined layout servers_widget = QWidget() servers_layout = QVBoxLayout() servers_layout.addSpacing(-10) servers_layout.addWidget(servers_label) servers_layout.addWidget(table_group) servers_layout.addSpacing(10) servers_layout.addLayout(buttons_layout) servers_widget.setLayout(servers_layout) # --- Tabs organization --- tabs = QTabWidget() tabs.addTab(self.create_tab(basic_features_group, advanced_group), _('Introspection')) tabs.addTab(self.create_tab(linting_widget), _('Linting')) tabs.addTab(self.create_tab(code_style_widget), _('Code style')) tabs.addTab(self.create_tab(docstring_style_widget), _('Docstring style')) tabs.addTab(self.create_tab(advanced_widget), _('Advanced')) tabs.addTab(self.create_tab(servers_widget), _('Other languages')) vlayout = QVBoxLayout() vlayout.addWidget(tabs) self.setLayout(vlayout)