# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""
General entry in Preferences.
For historical reasons (dating back to Spyder 2) the main class here is called
`MainConfigPage` and its associated entry in our config system is called
`main`.
"""
import traceback
import sys
from qtpy.compat import from_qvariant
from qtpy.QtCore import Qt
from qtpy.QtWidgets import (QApplication, QButtonGroup, QGridLayout, QGroupBox,
QHBoxLayout, QLabel, QMessageBox, QTabWidget,
QVBoxLayout, QWidget)
from spyder.config.base import (_, DISABLED_LANGUAGES, LANGUAGE_CODES,
running_in_mac_app, save_lang_conf)
from spyder.api.preferences import PluginConfigPage
from spyder.py3compat import to_text_string
from spyder.utils.qthelpers import (register_app_launchservices,
restore_launchservices)
# To open files from Finder directly in Spyder.
if sys.platform == "darwin":
import applaunchservices as als
HDPI_QT_PAGE = "https://doc.qt.io/qt-5/highdpi.html"
class ApplicationConfigPage(PluginConfigPage):
APPLY_CONF_PAGE_SETTINGS = True
def setup_page(self):
newcb = self.create_checkbox
# --- Advanced
# Remove disabled languages
language_codes = LANGUAGE_CODES.copy()
for lang in DISABLED_LANGUAGES:
language_codes.pop(lang)
languages = language_codes.items()
language_choices = sorted([(val, key) for key, val in languages])
language_combo = self.create_combobox(_('Language:'),
language_choices,
'interface_language',
restart=True)
opengl_options = ['Automatic', 'Desktop', 'Software', 'GLES']
opengl_choices = list(zip(opengl_options,
[c.lower() for c in opengl_options]))
opengl_combo = self.create_combobox(_('Rendering engine:'),
opengl_choices,
'opengl',
restart=True)
single_instance_box = newcb(_("Use a single instance"),
'single_instance',
tip=_("Set this to open external
"
"Python files in an already running "
"instance (Requires a restart)"))
prompt_box = newcb(_("Prompt when exiting"), 'prompt_on_exit')
popup_console_box = newcb(_("Show internal Spyder errors to report "
"them to Github"), 'show_internal_errors')
check_updates = newcb(_("Check for updates on startup"),
'check_updates_on_startup')
# Decide if it's possible to activate or not single instance mode
if running_in_mac_app():
self.set_option("single_instance", True)
single_instance_box.setEnabled(False)
comboboxes_advanced_layout = QHBoxLayout()
cbs_adv_grid = QGridLayout()
cbs_adv_grid.addWidget(language_combo.label, 0, 0)
cbs_adv_grid.addWidget(language_combo.combobox, 0, 1)
cbs_adv_grid.addWidget(opengl_combo.label, 1, 0)
cbs_adv_grid.addWidget(opengl_combo.combobox, 1, 1)
comboboxes_advanced_layout.addLayout(cbs_adv_grid)
comboboxes_advanced_layout.addStretch(1)
advanced_layout = QVBoxLayout()
advanced_layout.addLayout(comboboxes_advanced_layout)
advanced_layout.addWidget(single_instance_box)
advanced_layout.addWidget(prompt_box)
advanced_layout.addWidget(popup_console_box)
advanced_layout.addWidget(check_updates)
advanced_widget = QWidget()
advanced_widget.setLayout(advanced_layout)
# --- Panes
interface_group = QGroupBox(_("Panes"))
verttabs_box = newcb(_("Vertical tabs in panes"),
'vertical_tabs')
margin_box = newcb(_("Custom margin for panes:"),
'use_custom_margin')
margin_spin = self.create_spinbox("", _("pixels"), 'custom_margin',
default=0, min_=0, max_=30)
margin_box.toggled.connect(margin_spin.spinbox.setEnabled)
margin_box.toggled.connect(margin_spin.slabel.setEnabled)
margin_spin.spinbox.setEnabled(self.get_option('use_custom_margin'))
margin_spin.slabel.setEnabled(self.get_option('use_custom_margin'))
cursor_box = newcb(_("Cursor blinking:"),
'use_custom_cursor_blinking')
cursor_spin = self.create_spinbox(
"", _("ms"),
'custom_cursor_blinking',
default=QApplication.cursorFlashTime(),
min_=0, max_=5000, step=100)
cursor_box.toggled.connect(cursor_spin.spinbox.setEnabled)
cursor_box.toggled.connect(cursor_spin.slabel.setEnabled)
cursor_spin.spinbox.setEnabled(
self.get_option('use_custom_cursor_blinking'))
cursor_spin.slabel.setEnabled(
self.get_option('use_custom_cursor_blinking'))
margins_cursor_layout = QGridLayout()
margins_cursor_layout.addWidget(margin_box, 0, 0)
margins_cursor_layout.addWidget(margin_spin.spinbox, 0, 1)
margins_cursor_layout.addWidget(margin_spin.slabel, 0, 2)
margins_cursor_layout.addWidget(cursor_box, 1, 0)
margins_cursor_layout.addWidget(cursor_spin.spinbox, 1, 1)
margins_cursor_layout.addWidget(cursor_spin.slabel, 1, 2)
margins_cursor_layout.setColumnStretch(2, 100)
# Layout interface
interface_layout = QVBoxLayout()
interface_layout.addWidget(verttabs_box)
interface_layout.addLayout(margins_cursor_layout)
interface_group.setLayout(interface_layout)
if sys.platform == "darwin":
def set_open_file(state):
if state:
register_app_launchservices()
else:
restore_launchservices()
macOS_group = QGroupBox(_("macOS integration"))
mac_open_file_box = newcb(
_("Open files from Finder with Spyder"),
'mac_open_file',
tip=_("Register Spyder with the Launch Services"))
mac_open_file_box.toggled.connect(set_open_file)
macOS_layout = QVBoxLayout()
macOS_layout.addWidget(mac_open_file_box)
if als.get_bundle_identifier() is None:
# Disable setting
mac_open_file_box.setDisabled(True)
macOS_layout.addWidget(QLabel(
_('Launch Spyder with python.app
to enable'
' Apple event integrations.')))
macOS_group.setLayout(macOS_layout)
# --- Screen resolution Group (hidpi)
screen_resolution_group = QGroupBox(_("Screen resolution"))
screen_resolution_bg = QButtonGroup(screen_resolution_group)
screen_resolution_label = QLabel(_("Configuration for high DPI "
"screens
"
"Please see "
"{0}<> "
"for more information about "
"these options (in "
"English).").format(HDPI_QT_PAGE))
screen_resolution_label.setWordWrap(True)
screen_resolution_label.setOpenExternalLinks(True)
normal_radio = self.create_radiobutton(
_("Normal"),
'normal_screen_resolution',
button_group=screen_resolution_bg)
auto_scale_radio = self.create_radiobutton(
_("Enable auto high DPI scaling"),
'high_dpi_scaling',
button_group=screen_resolution_bg,
tip=_("Set this for high DPI displays"),
restart=True)
custom_scaling_radio = self.create_radiobutton(
_("Set a custom high DPI scaling"),
'high_dpi_custom_scale_factor',
button_group=screen_resolution_bg,
tip=_("Set this for high DPI displays when "
"auto scaling does not work"),
restart=True)
self.custom_scaling_edit = self.create_lineedit(
"",
'high_dpi_custom_scale_factors',
tip=_("Enter values for different screens "
"separated by semicolons ';'.\n"
"Float values are supported"),
alignment=Qt.Horizontal,
regex=r"[0-9]+(?:\.[0-9]*)(;[0-9]+(?:\.[0-9]*))*",
restart=True)
normal_radio.toggled.connect(self.custom_scaling_edit.setDisabled)
auto_scale_radio.toggled.connect(self.custom_scaling_edit.setDisabled)
custom_scaling_radio.toggled.connect(
self.custom_scaling_edit.setEnabled)
# Layout Screen resolution
screen_resolution_layout = QVBoxLayout()
screen_resolution_layout.addWidget(screen_resolution_label)
screen_resolution_inner_layout = QGridLayout()
screen_resolution_inner_layout.addWidget(normal_radio, 0, 0)
screen_resolution_inner_layout.addWidget(auto_scale_radio, 1, 0)
screen_resolution_inner_layout.addWidget(custom_scaling_radio, 2, 0)
screen_resolution_inner_layout.addWidget(
self.custom_scaling_edit, 2, 1)
screen_resolution_layout.addLayout(screen_resolution_inner_layout)
screen_resolution_group.setLayout(screen_resolution_layout)
if sys.platform == "darwin":
interface_tab = self.create_tab(screen_resolution_group,
interface_group, macOS_group)
else:
interface_tab = self.create_tab(screen_resolution_group,
interface_group)
self.tabs = QTabWidget()
self.tabs.addTab(interface_tab, _("Interface"))
self.tabs.addTab(self.create_tab(advanced_widget),
_("Advanced settings"))
vlayout = QVBoxLayout()
vlayout.addWidget(self.tabs)
self.setLayout(vlayout)
def apply_settings(self, options):
if 'high_dpi_custom_scale_factors' in options:
scale_factors = self.get_option(
'high_dpi_custom_scale_factors').split(';')
change_min_scale_factor = False
for idx, scale_factor in enumerate(scale_factors[:]):
scale_factor = float(scale_factor)
if scale_factor < 1.0:
change_min_scale_factor = True
scale_factors[idx] = "1.0"
if change_min_scale_factor:
scale_factors_text = ";".join(scale_factors)
QMessageBox.critical(
self, _("Error"),
_("We're sorry but setting a scale factor bellow 1.0 "
"isn't possible. Any scale factor bellow 1.0 will "
"be set to 1.0"),
QMessageBox.Ok)
self.custom_scaling_edit.textbox.setText(scale_factors_text)
self.set_option(
'high_dpi_custom_scale_factors', scale_factors_text)
self.changed_options.add('high_dpi_custom_scale_factors')
self.plugin.apply_settings()
def _save_lang(self):
"""
Get selected language setting and save to language configuration file.
"""
for combobox, (sec, opt, _default) in list(self.comboboxes.items()):
if opt == 'interface_language':
data = combobox.itemData(combobox.currentIndex())
value = from_qvariant(data, to_text_string)
break
try:
save_lang_conf(value)
self.set_option('interface_language', value)
except Exception:
QMessageBox.critical(
self, _("Error"),
_("We're sorry but the following error occurred while trying "
"to set your selected language:
"
"{}").format(traceback.format_exc()),
QMessageBox.Ok)
return