1
# -*- coding: utf-8 -*-
3
# Copyright © 2009-2010 Pierre Raybaut
4
# Licensed under the terms of the MIT License
5
# (see spyderlib/__init__.py for details)
7
"""Configuration dialog / Preferences"""
12
from spyderlib.baseconfig import _
13
from spyderlib.config import (get_icon, CONF, CUSTOM_COLOR_SCHEME_NAME,
14
set_default_color_scheme, COLOR_SCHEME_NAMES)
15
from spyderlib.utils.qthelpers import get_std_icon
16
from spyderlib.userconfig import NoDefault
17
from spyderlib.widgets.colors import ColorLayout
19
from spyderlib.qt.QtGui import (QWidget, QDialog, QListWidget, QListWidgetItem,
20
QVBoxLayout, QStackedWidget, QListView,
21
QHBoxLayout, QDialogButtonBox, QCheckBox,
22
QMessageBox, QLabel, QLineEdit, QSpinBox,
23
QPushButton, QFontComboBox, QGroupBox,
24
QComboBox, QColor, QGridLayout, QTabWidget,
25
QRadioButton, QButtonGroup, QSplitter,
26
QStyleFactory, QScrollArea)
27
from spyderlib.qt.QtCore import Qt, QSize, SIGNAL, SLOT, Slot
28
from spyderlib.qt.compat import (to_qvariant, from_qvariant,
29
getexistingdirectory, getopenfilename)
32
class ConfigPage(QWidget):
33
"""Configuration page base class"""
34
def __init__(self, parent, apply_callback=None):
35
QWidget.__init__(self, parent)
36
self.apply_callback = apply_callback
37
self.is_modified = False
41
Initialize configuration page:
43
* load settings and change widgets accordingly
49
"""Return page name"""
50
raise NotImplementedError
53
"""Return page icon"""
54
raise NotImplementedError
57
"""Setup configuration page widget"""
58
raise NotImplementedError
60
def set_modified(self, state):
61
self.is_modified = state
62
self.emit(SIGNAL("apply_button_enabled(bool)"), state)
65
"""Return True if all widget contents are valid"""
66
raise NotImplementedError
68
def apply_changes(self):
69
"""Apply changes callback"""
72
if self.apply_callback is not None:
74
self.set_modified(False)
76
def load_from_conf(self):
77
"""Load settings from configuration file"""
78
raise NotImplementedError
80
def save_to_conf(self):
81
"""Save settings to configuration file"""
82
raise NotImplementedError
85
class ConfigDialog(QDialog):
86
"""Spyder configuration ('Preferences') dialog box"""
87
def __init__(self, parent=None):
88
QDialog.__init__(self, parent)
90
# Destroying the C++ object right after closing the dialog box,
91
# otherwise it may be garbage-collected in another QThread
92
# (e.g. the editor's analysis thread in Spyder), thus leading to
93
# a segmentation fault on UNIX or an application crash on Windows
94
self.setAttribute(Qt.WA_DeleteOnClose)
96
self.contents_widget = QListWidget()
97
self.contents_widget.setMovement(QListView.Static)
98
self.contents_widget.setSpacing(1)
100
bbox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Apply
101
|QDialogButtonBox.Cancel)
102
self.apply_btn = bbox.button(QDialogButtonBox.Apply)
103
self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()"))
104
self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()"))
105
self.connect(bbox, SIGNAL("clicked(QAbstractButton*)"),
108
self.pages_widget = QStackedWidget()
109
self.connect(self.pages_widget, SIGNAL("currentChanged(int)"),
110
self.current_page_changed)
112
self.connect(self.contents_widget, SIGNAL("currentRowChanged(int)"),
113
self.pages_widget.setCurrentIndex)
114
self.contents_widget.setCurrentRow(0)
116
hsplitter = QSplitter()
117
hsplitter.addWidget(self.contents_widget)
118
hsplitter.addWidget(self.pages_widget)
120
btnlayout = QHBoxLayout()
121
btnlayout.addStretch(1)
122
btnlayout.addWidget(bbox)
124
vlayout = QVBoxLayout()
125
vlayout.addWidget(hsplitter)
126
vlayout.addLayout(btnlayout)
128
self.setLayout(vlayout)
130
self.setWindowTitle(_("Preferences"))
131
self.setWindowIcon(get_icon("configure.png"))
133
def get_current_index(self):
134
"""Return current page index"""
135
return self.contents_widget.currentRow()
137
def set_current_index(self, index):
138
"""Set current page index"""
139
self.contents_widget.setCurrentRow(index)
141
def get_page(self, index=None):
142
"""Return page widget"""
144
widget = self.pages_widget.currentWidget()
146
widget = self.pages_widget.widget(index)
147
return widget.widget()
150
"""Reimplement Qt method"""
151
for index in range(self.pages_widget.count()):
152
configpage = self.get_page(index)
153
if not configpage.is_valid():
155
configpage.apply_changes()
158
def button_clicked(self, button):
159
if button is self.apply_btn:
160
# Apply button was clicked
161
configpage = self.get_page()
162
if not configpage.is_valid():
164
configpage.apply_changes()
166
def current_page_changed(self, index):
167
widget = self.get_page(index)
168
self.apply_btn.setVisible(widget.apply_callback is not None)
169
self.apply_btn.setEnabled(widget.is_modified)
171
def add_page(self, widget):
172
self.connect(self, SIGNAL('check_settings()'), widget.check_settings)
173
self.connect(widget, SIGNAL('show_this_page()'),
174
lambda row=self.contents_widget.count():
175
self.contents_widget.setCurrentRow(row))
176
self.connect(widget, SIGNAL("apply_button_enabled(bool)"),
177
self.apply_btn.setEnabled)
178
scrollarea = QScrollArea(self)
179
scrollarea.setWidgetResizable(True)
180
scrollarea.setWidget(widget)
181
self.pages_widget.addWidget(scrollarea)
182
item = QListWidgetItem(self.contents_widget)
183
item.setIcon(widget.get_icon())
184
item.setText(widget.get_name())
185
item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
186
item.setSizeHint(QSize(0, 25))
188
def check_all_settings(self):
189
"""This method is called to check all configuration page settings
190
after configuration dialog has been shown"""
191
self.emit(SIGNAL('check_settings()'))
194
class SpyderConfigPage(ConfigPage):
195
"""Plugin configuration dialog box page widget"""
196
def __init__(self, parent):
197
ConfigPage.__init__(self, parent,
198
apply_callback=lambda:
199
self.apply_settings(self.changed_options))
201
self.radiobuttons = {}
203
self.validate_data = {}
209
self.changed_options = set()
210
self.default_button_group = None
212
def apply_settings(self, options):
213
raise NotImplementedError
215
def check_settings(self):
216
"""This method is called to check settings after configuration
217
dialog has been shown"""
220
def set_modified(self, state):
221
ConfigPage.set_modified(self, state)
223
self.changed_options = set()
226
"""Return True if all widget contents are valid"""
227
for lineedit in self.lineedits:
228
if lineedit in self.validate_data and lineedit.isEnabled():
229
validator, invalid_msg = self.validate_data[lineedit]
230
text = unicode(lineedit.text())
231
if not validator(text):
232
QMessageBox.critical(self, self.get_name(),
233
"%s:<br><b>%s</b>" % (invalid_msg, text),
238
def load_from_conf(self):
239
"""Load settings from configuration file"""
240
for checkbox, (option, default) in self.checkboxes.items():
241
checkbox.setChecked(self.get_option(option, default))
242
self.connect(checkbox, SIGNAL("clicked(bool)"),
243
lambda _foo, opt=option: self.has_been_modified(opt))
244
for radiobutton, (option, default) in self.radiobuttons.items():
245
radiobutton.setChecked(self.get_option(option, default))
246
self.connect(radiobutton, SIGNAL("toggled(bool)"),
247
lambda _foo, opt=option: self.has_been_modified(opt))
248
for lineedit, (option, default) in self.lineedits.items():
249
lineedit.setText(self.get_option(option, default))
250
self.connect(lineedit, SIGNAL("textChanged(QString)"),
251
lambda _foo, opt=option: self.has_been_modified(opt))
252
for spinbox, (option, default) in self.spinboxes.items():
253
spinbox.setValue(self.get_option(option, default))
254
self.connect(spinbox, SIGNAL('valueChanged(int)'),
255
lambda _foo, opt=option: self.has_been_modified(opt))
256
for combobox, (option, default) in self.comboboxes.items():
257
value = self.get_option(option, default)
258
for index in range(combobox.count()):
259
data = from_qvariant(combobox.itemData(index), unicode)
260
# For PyQt API v2, it is necessary to convert `data` to
261
# unicode in case the original type was not a string, like an
262
# integer for example (see spyderlib.qt.compat.from_qvariant):
263
if unicode(data) == unicode(value):
265
combobox.setCurrentIndex(index)
266
self.connect(combobox, SIGNAL('currentIndexChanged(int)'),
267
lambda _foo, opt=option: self.has_been_modified(opt))
268
for (fontbox, sizebox), option in self.fontboxes.items():
269
font = self.get_font(option)
270
fontbox.setCurrentFont(font)
271
sizebox.setValue(font.pointSize())
273
property = 'plugin_font'
276
self.connect(fontbox, SIGNAL('currentIndexChanged(int)'),
277
lambda _foo, opt=property: self.has_been_modified(opt))
278
self.connect(sizebox, SIGNAL('valueChanged(int)'),
279
lambda _foo, opt=property: self.has_been_modified(opt))
280
for clayout, (option, default) in self.coloredits.items():
281
property = to_qvariant(option)
282
edit = clayout.lineedit
283
btn = clayout.colorbtn
284
edit.setText(self.get_option(option, default))
285
self.connect(btn, SIGNAL('clicked()'),
286
lambda opt=option: self.has_been_modified(opt))
287
self.connect(edit, SIGNAL("textChanged(QString)"),
288
lambda _foo, opt=option: self.has_been_modified(opt))
289
for (clayout, cb_bold, cb_italic
290
), (option, default) in self.scedits.items():
291
edit = clayout.lineedit
292
btn = clayout.colorbtn
293
color, bold, italic = self.get_option(option, default)
295
cb_bold.setChecked(bold)
296
cb_italic.setChecked(italic)
297
self.connect(btn, SIGNAL('clicked()'),
298
lambda opt=option: self.has_been_modified(opt))
299
self.connect(edit, SIGNAL("textChanged(QString)"),
300
lambda _foo, opt=option: self.has_been_modified(opt))
301
self.connect(cb_bold, SIGNAL("clicked(bool)"),
302
lambda _foo, opt=option: self.has_been_modified(opt))
303
self.connect(cb_italic, SIGNAL("clicked(bool)"),
304
lambda _foo, opt=option: self.has_been_modified(opt))
306
def save_to_conf(self):
307
"""Save settings to configuration file"""
308
for checkbox, (option, _default) in self.checkboxes.items():
309
self.set_option(option, checkbox.isChecked())
310
for radiobutton, (option, _default) in self.radiobuttons.items():
311
self.set_option(option, radiobutton.isChecked())
312
for lineedit, (option, _default) in self.lineedits.items():
313
self.set_option(option, unicode(lineedit.text()))
314
for spinbox, (option, _default) in self.spinboxes.items():
315
self.set_option(option, spinbox.value())
316
for combobox, (option, _default) in self.comboboxes.items():
317
data = combobox.itemData(combobox.currentIndex())
318
self.set_option(option, from_qvariant(data, unicode))
319
for (fontbox, sizebox), option in self.fontboxes.items():
320
font = fontbox.currentFont()
321
font.setPointSize(sizebox.value())
322
self.set_font(font, option)
323
for clayout, (option, _default) in self.coloredits.items():
324
self.set_option(option, unicode(clayout.lineedit.text()))
325
for (clayout, cb_bold, cb_italic), (option, _default) in self.scedits.items():
326
color = unicode(clayout.lineedit.text())
327
bold = cb_bold.isChecked()
328
italic = cb_italic.isChecked()
329
self.set_option(option, (color, bold, italic))
332
def has_been_modified(self, option):
333
self.set_modified(True)
334
self.changed_options.add(option)
336
def create_checkbox(self, text, option, default=NoDefault,
337
tip=None, msg_warning=None, msg_info=None,
338
msg_if_enabled=False):
339
checkbox = QCheckBox(text)
341
checkbox.setToolTip(tip)
342
self.checkboxes[checkbox] = (option, default)
343
if msg_warning is not None or msg_info is not None:
344
def show_message(is_checked):
345
if is_checked or not msg_if_enabled:
346
if msg_warning is not None:
347
QMessageBox.warning(self, self.get_name(),
348
msg_warning, QMessageBox.Ok)
349
if msg_info is not None:
350
QMessageBox.information(self, self.get_name(),
351
msg_info, QMessageBox.Ok)
352
self.connect(checkbox, SIGNAL("clicked(bool)"), show_message)
355
def create_radiobutton(self, text, option, default=NoDefault,
356
tip=None, msg_warning=None, msg_info=None,
357
msg_if_enabled=False, button_group=None):
358
radiobutton = QRadioButton(text)
359
if button_group is None:
360
if self.default_button_group is None:
361
self.default_button_group = QButtonGroup(self)
362
button_group = self.default_button_group
363
button_group.addButton(radiobutton)
365
radiobutton.setToolTip(tip)
366
self.radiobuttons[radiobutton] = (option, default)
367
if msg_warning is not None or msg_info is not None:
368
def show_message(is_checked):
369
if is_checked or not msg_if_enabled:
370
if msg_warning is not None:
371
QMessageBox.warning(self, self.get_name(),
372
msg_warning, QMessageBox.Ok)
373
if msg_info is not None:
374
QMessageBox.information(self, self.get_name(),
375
msg_info, QMessageBox.Ok)
376
self.connect(radiobutton, SIGNAL("toggled(bool)"), show_message)
379
def create_lineedit(self, text, option, default=NoDefault,
380
tip=None, alignment=Qt.Vertical):
382
label.setWordWrap(True)
384
layout = QVBoxLayout() if alignment == Qt.Vertical else QHBoxLayout()
385
layout.addWidget(label)
386
layout.addWidget(edit)
387
layout.setContentsMargins(0, 0, 0, 0)
390
self.lineedits[edit] = (option, default)
391
widget = QWidget(self)
392
widget.setLayout(layout)
395
def create_browsedir(self, text, option, default=NoDefault, tip=None):
396
widget = self.create_lineedit(text, option, default,
397
alignment=Qt.Horizontal)
398
for edit in self.lineedits:
399
if widget.isAncestorOf(edit):
401
msg = _("Invalid directory path")
402
self.validate_data[edit] = (osp.isdir, msg)
403
browse_btn = QPushButton(get_std_icon('DirOpenIcon'), "", self)
404
browse_btn.setToolTip(_("Select directory"))
405
self.connect(browse_btn, SIGNAL("clicked()"),
406
lambda: self.select_directory(edit))
407
layout = QHBoxLayout()
408
layout.addWidget(widget)
409
layout.addWidget(browse_btn)
410
layout.setContentsMargins(0, 0, 0, 0)
411
browsedir = QWidget(self)
412
browsedir.setLayout(layout)
415
def select_directory(self, edit):
416
"""Select directory"""
417
basedir = unicode(edit.text())
418
if not osp.isdir(basedir):
419
basedir = os.getcwdu()
420
title = _("Select directory")
421
directory = getexistingdirectory(self, title, basedir)
423
edit.setText(directory)
425
def create_browsefile(self, text, option, default=NoDefault, tip=None,
427
widget = self.create_lineedit(text, option, default,
428
alignment=Qt.Horizontal)
429
for edit in self.lineedits:
430
if widget.isAncestorOf(edit):
432
msg = _("Invalid file path")
433
self.validate_data[edit] = (osp.isfile, msg)
434
browse_btn = QPushButton(get_std_icon('FileIcon'), "", self)
435
browse_btn.setToolTip(_("Select file"))
436
self.connect(browse_btn, SIGNAL("clicked()"),
437
lambda: self.select_file(edit, filters))
438
layout = QHBoxLayout()
439
layout.addWidget(widget)
440
layout.addWidget(browse_btn)
441
layout.setContentsMargins(0, 0, 0, 0)
442
browsedir = QWidget(self)
443
browsedir.setLayout(layout)
446
def select_file(self, edit, filters=None):
448
basedir = osp.dirname(unicode(edit.text()))
449
if not osp.isdir(basedir):
450
basedir = os.getcwdu()
452
filters = _("All files (*)")
453
title = _("Select file")
454
filename, _selfilter = getopenfilename(self, title, basedir, filters)
456
edit.setText(filename)
458
def create_spinbox(self, prefix, suffix, option, default=NoDefault,
459
min_=None, max_=None, step=None, tip=None):
461
plabel = QLabel(prefix)
465
slabel = QLabel(suffix)
470
spinbox.setMinimum(min_)
472
spinbox.setMaximum(max_)
474
spinbox.setSingleStep(step)
476
spinbox.setToolTip(tip)
477
self.spinboxes[spinbox] = (option, default)
478
layout = QHBoxLayout()
479
for subwidget in (plabel, spinbox, slabel):
480
if subwidget is not None:
481
layout.addWidget(subwidget)
483
layout.setContentsMargins(0, 0, 0, 0)
484
widget = QWidget(self)
485
widget.setLayout(layout)
488
def create_coloredit(self, text, option, default=NoDefault, tip=None,
489
without_layout=False):
491
clayout = ColorLayout(QColor(Qt.black), self)
492
clayout.lineedit.setMaximumWidth(80)
494
clayout.setToolTip(tip)
495
self.coloredits[clayout] = (option, default)
497
return label, clayout
498
layout = QHBoxLayout()
499
layout.addWidget(label)
500
layout.addLayout(clayout)
502
layout.setContentsMargins(0, 0, 0, 0)
503
widget = QWidget(self)
504
widget.setLayout(layout)
507
def create_scedit(self, text, option, default=NoDefault, tip=None,
508
without_layout=False):
510
clayout = ColorLayout(QColor(Qt.black), self)
511
clayout.lineedit.setMaximumWidth(80)
513
clayout.setToolTip(tip)
514
cb_bold = QCheckBox()
515
cb_bold.setIcon(get_icon("bold.png"))
516
cb_bold.setToolTip(_("Bold"))
517
cb_italic = QCheckBox()
518
cb_italic.setIcon(get_icon("italic.png"))
519
cb_italic.setToolTip(_("Italic"))
520
self.scedits[(clayout, cb_bold, cb_italic)] = (option, default)
522
return label, clayout, cb_bold, cb_italic
523
layout = QHBoxLayout()
524
layout.addWidget(label)
525
layout.addLayout(clayout)
526
layout.addSpacing(10)
527
layout.addWidget(cb_bold)
528
layout.addWidget(cb_italic)
530
layout.setContentsMargins(0, 0, 0, 0)
531
widget = QWidget(self)
532
widget.setLayout(layout)
535
def create_combobox(self, text, choices, option, default=NoDefault,
537
"""choices: couples (name, key)"""
539
combobox = QComboBox()
541
combobox.setToolTip(tip)
542
for name, key in choices:
543
combobox.addItem(name, to_qvariant(key))
544
self.comboboxes[combobox] = (option, default)
545
layout = QHBoxLayout()
546
for subwidget in (label, combobox):
547
layout.addWidget(subwidget)
549
layout.setContentsMargins(0, 0, 0, 0)
550
widget = QWidget(self)
551
widget.setLayout(layout)
554
def create_fontgroup(self, option=None, text=None,
555
tip=None, fontfilters=None):
556
"""Option=None -> setting plugin font"""
557
fontlabel = QLabel(_("Font: "))
558
fontbox = QFontComboBox()
559
if fontfilters is not None:
560
fontbox.setFontFilters(fontfilters)
561
sizelabel = QLabel(" "+_("Size: "))
563
sizebox.setRange(7, 100)
564
self.fontboxes[(fontbox, sizebox)] = option
565
layout = QHBoxLayout()
566
for subwidget in (fontlabel, fontbox, sizelabel, sizebox):
567
layout.addWidget(subwidget)
570
text = _("Font style")
571
group = QGroupBox(text)
572
group.setLayout(layout)
574
group.setToolTip(tip)
577
def create_button(self, text, callback):
578
btn = QPushButton(text)
579
self.connect(btn, SIGNAL('clicked()'), callback)
580
self.connect(btn, SIGNAL('clicked()'),
581
lambda opt='': self.has_been_modified(opt))
584
def create_tab(self, *widgets):
585
"""Create simple tab widget page: widgets added in a vertical layout"""
587
layout = QVBoxLayout()
589
layout.addWidget(widg)
591
widget.setLayout(layout)
595
class GeneralConfigPage(SpyderConfigPage):
597
def __init__(self, parent, main):
598
SpyderConfigPage.__init__(self, parent)
601
def set_option(self, option, value):
602
CONF.set(self.CONF_SECTION, option, value)
604
def get_option(self, option, default=NoDefault):
605
return CONF.get(self.CONF_SECTION, option, default)
607
def apply_settings(self, options):
608
raise NotImplementedError
611
class MainConfigPage(GeneralConfigPage):
612
CONF_SECTION = "main"
617
return get_icon("genprefs.png")
619
def setup_page(self):
620
interface_group = QGroupBox(_("Interface"))
621
styles = [str(txt) for txt in QStyleFactory.keys()]
622
choices = zip(styles, [style.lower() for style in styles])
623
style_combo = self.create_combobox(_('Qt windows style'), choices,
625
default=self.main.default_style)
626
newcb = self.create_checkbox
627
vertdock_box = newcb(_("Vertical dockwidget title bars"),
628
'vertical_dockwidget_titlebars')
629
verttabs_box = newcb(_("Vertical dockwidget tabs"),
631
animated_box = newcb(_("Animated toolbars and dockwidgets"),
633
margin_box = newcb(_("Custom dockwidget margin:"),
635
margin_spin = self.create_spinbox("", "pixels", 'custom_margin',
637
self.connect(margin_box, SIGNAL("toggled(bool)"),
638
margin_spin.setEnabled)
639
margin_spin.setEnabled(self.get_option('use_custom_margin'))
640
margins_layout = QHBoxLayout()
641
margins_layout.addWidget(margin_box)
642
margins_layout.addWidget(margin_spin)
644
interface_layout = QVBoxLayout()
645
interface_layout.addWidget(style_combo)
646
interface_layout.addWidget(vertdock_box)
647
interface_layout.addWidget(verttabs_box)
648
interface_layout.addWidget(animated_box)
649
interface_layout.addLayout(margins_layout)
650
interface_group.setLayout(interface_layout)
652
vlayout = QVBoxLayout()
653
vlayout.addWidget(interface_group)
654
vlayout.addStretch(1)
655
self.setLayout(vlayout)
657
def apply_settings(self, options):
658
self.main.apply_settings()
661
class ColorSchemeConfigPage(GeneralConfigPage):
662
CONF_SECTION = "color_schemes"
664
return _("Syntax coloring")
667
return get_icon("genprefs.png")
669
def setup_page(self):
671
names = self.get_option("names")
672
names.pop(names.index(CUSTOM_COLOR_SCHEME_NAME))
673
names.insert(0, CUSTOM_COLOR_SCHEME_NAME)
675
"background": _("Background:"),
676
"currentline": _("Current line:"),
677
"occurence": _("Occurence:"),
678
"ctrlclick": _("Link:"),
679
"sideareas": _("Side areas:"),
680
"matched_p": _("Matched parentheses:"),
681
"unmatched_p": _("Unmatched parentheses:"),
682
"normal": _("Normal text:"),
683
"keyword": _("Keyword:"),
684
"builtin": _("Builtin:"),
685
"definition": _("Definition:"),
686
"comment": _("Comment:"),
687
"string": _("String:"),
688
"number": _("Number:"),
689
"instance": _("Instance:"),
691
from spyderlib.widgets.sourcecode import syntaxhighlighters
692
assert all([key in fieldnames
693
for key in syntaxhighlighters.COLOR_SCHEME_KEYS])
694
for tabname in names:
695
cs_group = QGroupBox(_("Color scheme"))
696
cs_layout = QGridLayout()
697
for row, key in enumerate(syntaxhighlighters.COLOR_SCHEME_KEYS):
698
option = "%s/%s" % (tabname, key)
699
value = self.get_option(option)
700
name = fieldnames[key]
701
if isinstance(value, basestring):
702
label, clayout = self.create_coloredit(name, option,
704
label.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
705
cs_layout.addWidget(label, row+1, 0)
706
cs_layout.addLayout(clayout, row+1, 1)
708
label, clayout, cb_bold, cb_italic = self.create_scedit(
709
name, option, without_layout=True)
710
label.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
711
cs_layout.addWidget(label, row+1, 0)
712
cs_layout.addLayout(clayout, row+1, 1)
713
cs_layout.addWidget(cb_bold, row+1, 2)
714
cs_layout.addWidget(cb_italic, row+1, 3)
715
cs_group.setLayout(cs_layout)
716
if tabname in COLOR_SCHEME_NAMES:
717
def_btn = self.create_button(_("Reset to default values"),
718
lambda: self.reset_to_default(tabname))
719
tabs.addTab(self.create_tab(cs_group, def_btn), tabname)
721
tabs.addTab(self.create_tab(cs_group), tabname)
723
vlayout = QVBoxLayout()
724
vlayout.addWidget(tabs)
725
self.setLayout(vlayout)
728
def reset_to_default(self, name):
729
set_default_color_scheme(name, replace=True)
730
self.load_from_conf()
732
def apply_settings(self, options):
733
self.main.editor.apply_plugin_settings(['color_scheme_name'])
734
if self.main.historylog is not None:
735
self.main.historylog.apply_plugin_settings(['color_scheme_name'])
736
if self.main.inspector is not None:
737
self.main.inspector.apply_plugin_settings(['color_scheme_name'])
1
# -*- coding: utf-8 -*-
3
# Copyright © 2009-2010 Pierre Raybaut
4
# Licensed under the terms of the MIT License
5
# (see spyderlib/__init__.py for details)
7
"""Configuration dialog / Preferences"""
12
from spyderlib.baseconfig import _
13
from spyderlib.config import CONF
14
from spyderlib.guiconfig import (get_icon, CUSTOM_COLOR_SCHEME_NAME,
15
set_default_color_scheme, COLOR_SCHEME_NAMES)
16
from spyderlib.utils.qthelpers import get_std_icon
17
from spyderlib.userconfig import NoDefault
18
from spyderlib.widgets.colors import ColorLayout
20
from spyderlib.qt.QtGui import (QWidget, QDialog, QListWidget, QListWidgetItem,
21
QVBoxLayout, QStackedWidget, QListView,
22
QHBoxLayout, QDialogButtonBox, QCheckBox,
23
QMessageBox, QLabel, QLineEdit, QSpinBox,
24
QPushButton, QFontComboBox, QGroupBox,
25
QComboBox, QColor, QGridLayout, QTabWidget,
26
QRadioButton, QButtonGroup, QSplitter,
27
QStyleFactory, QScrollArea)
28
from spyderlib.qt.QtCore import Qt, QSize, SIGNAL, SLOT, Slot
29
from spyderlib.qt.compat import (to_qvariant, from_qvariant,
30
getexistingdirectory, getopenfilename)
33
class SizeMixin(object):
34
"""Mixin to keep widget size accessible
35
even when C++ object has been deleted"""
36
def resizeEvent(self, event):
37
"""Reimplement Qt method"""
38
QDialog.resizeEvent(self, event)
39
self.emit(SIGNAL("size_change(QSize)"), self.size())
42
class ConfigPage(QWidget):
43
"""Configuration page base class"""
44
def __init__(self, parent, apply_callback=None):
45
QWidget.__init__(self, parent)
46
self.apply_callback = apply_callback
47
self.is_modified = False
51
Initialize configuration page:
53
* load settings and change widgets accordingly
59
"""Return page name"""
60
raise NotImplementedError
63
"""Return page icon"""
64
raise NotImplementedError
67
"""Setup configuration page widget"""
68
raise NotImplementedError
70
def set_modified(self, state):
71
self.is_modified = state
72
self.emit(SIGNAL("apply_button_enabled(bool)"), state)
75
"""Return True if all widget contents are valid"""
76
raise NotImplementedError
78
def apply_changes(self):
79
"""Apply changes callback"""
82
if self.apply_callback is not None:
84
self.set_modified(False)
86
def load_from_conf(self):
87
"""Load settings from configuration file"""
88
raise NotImplementedError
90
def save_to_conf(self):
91
"""Save settings to configuration file"""
92
raise NotImplementedError
95
class ConfigDialog(QDialog, SizeMixin):
96
"""Spyder configuration ('Preferences') dialog box"""
97
def __init__(self, parent=None):
98
QDialog.__init__(self, parent)
99
SizeMixin.__init__(self)
101
# Destroying the C++ object right after closing the dialog box,
102
# otherwise it may be garbage-collected in another QThread
103
# (e.g. the editor's analysis thread in Spyder), thus leading to
104
# a segmentation fault on UNIX or an application crash on Windows
105
self.setAttribute(Qt.WA_DeleteOnClose)
107
self.contents_widget = QListWidget()
108
self.contents_widget.setMovement(QListView.Static)
109
self.contents_widget.setSpacing(1)
111
bbox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Apply
112
|QDialogButtonBox.Cancel)
113
self.apply_btn = bbox.button(QDialogButtonBox.Apply)
114
self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()"))
115
self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()"))
116
self.connect(bbox, SIGNAL("clicked(QAbstractButton*)"),
119
self.pages_widget = QStackedWidget()
120
self.connect(self.pages_widget, SIGNAL("currentChanged(int)"),
121
self.current_page_changed)
123
self.connect(self.contents_widget, SIGNAL("currentRowChanged(int)"),
124
self.pages_widget.setCurrentIndex)
125
self.contents_widget.setCurrentRow(0)
127
hsplitter = QSplitter()
128
hsplitter.addWidget(self.contents_widget)
129
hsplitter.addWidget(self.pages_widget)
131
btnlayout = QHBoxLayout()
132
btnlayout.addStretch(1)
133
btnlayout.addWidget(bbox)
135
vlayout = QVBoxLayout()
136
vlayout.addWidget(hsplitter)
137
vlayout.addLayout(btnlayout)
139
self.setLayout(vlayout)
141
self.setWindowTitle(_("Preferences"))
142
self.setWindowIcon(get_icon("configure.png"))
144
def get_current_index(self):
145
"""Return current page index"""
146
return self.contents_widget.currentRow()
148
def set_current_index(self, index):
149
"""Set current page index"""
150
self.contents_widget.setCurrentRow(index)
152
def get_page(self, index=None):
153
"""Return page widget"""
155
widget = self.pages_widget.currentWidget()
157
widget = self.pages_widget.widget(index)
158
return widget.widget()
161
"""Reimplement Qt method"""
162
for index in range(self.pages_widget.count()):
163
configpage = self.get_page(index)
164
if not configpage.is_valid():
166
configpage.apply_changes()
169
def button_clicked(self, button):
170
if button is self.apply_btn:
171
# Apply button was clicked
172
configpage = self.get_page()
173
if not configpage.is_valid():
175
configpage.apply_changes()
177
def current_page_changed(self, index):
178
widget = self.get_page(index)
179
self.apply_btn.setVisible(widget.apply_callback is not None)
180
self.apply_btn.setEnabled(widget.is_modified)
182
def add_page(self, widget):
183
self.connect(self, SIGNAL('check_settings()'), widget.check_settings)
184
self.connect(widget, SIGNAL('show_this_page()'),
185
lambda row=self.contents_widget.count():
186
self.contents_widget.setCurrentRow(row))
187
self.connect(widget, SIGNAL("apply_button_enabled(bool)"),
188
self.apply_btn.setEnabled)
189
scrollarea = QScrollArea(self)
190
scrollarea.setWidgetResizable(True)
191
scrollarea.setWidget(widget)
192
self.pages_widget.addWidget(scrollarea)
193
item = QListWidgetItem(self.contents_widget)
194
item.setIcon(widget.get_icon())
195
item.setText(widget.get_name())
196
item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
197
item.setSizeHint(QSize(0, 25))
199
def check_all_settings(self):
200
"""This method is called to check all configuration page settings
201
after configuration dialog has been shown"""
202
self.emit(SIGNAL('check_settings()'))
205
class SpyderConfigPage(ConfigPage):
206
"""Plugin configuration dialog box page widget"""
207
def __init__(self, parent):
208
ConfigPage.__init__(self, parent,
209
apply_callback=lambda:
210
self.apply_settings(self.changed_options))
212
self.radiobuttons = {}
214
self.validate_data = {}
220
self.changed_options = set()
221
self.default_button_group = None
223
def apply_settings(self, options):
224
raise NotImplementedError
226
def check_settings(self):
227
"""This method is called to check settings after configuration
228
dialog has been shown"""
231
def set_modified(self, state):
232
ConfigPage.set_modified(self, state)
234
self.changed_options = set()
237
"""Return True if all widget contents are valid"""
238
for lineedit in self.lineedits:
239
if lineedit in self.validate_data and lineedit.isEnabled():
240
validator, invalid_msg = self.validate_data[lineedit]
241
text = unicode(lineedit.text())
242
if not validator(text):
243
QMessageBox.critical(self, self.get_name(),
244
"%s:<br><b>%s</b>" % (invalid_msg, text),
249
def load_from_conf(self):
250
"""Load settings from configuration file"""
251
for checkbox, (option, default) in self.checkboxes.items():
252
checkbox.setChecked(self.get_option(option, default))
253
self.connect(checkbox, SIGNAL("clicked(bool)"),
254
lambda _foo, opt=option: self.has_been_modified(opt))
255
for radiobutton, (option, default) in self.radiobuttons.items():
256
radiobutton.setChecked(self.get_option(option, default))
257
self.connect(radiobutton, SIGNAL("toggled(bool)"),
258
lambda _foo, opt=option: self.has_been_modified(opt))
259
for lineedit, (option, default) in self.lineedits.items():
260
lineedit.setText(self.get_option(option, default))
261
self.connect(lineedit, SIGNAL("textChanged(QString)"),
262
lambda _foo, opt=option: self.has_been_modified(opt))
263
for spinbox, (option, default) in self.spinboxes.items():
264
spinbox.setValue(self.get_option(option, default))
265
self.connect(spinbox, SIGNAL('valueChanged(int)'),
266
lambda _foo, opt=option: self.has_been_modified(opt))
267
for combobox, (option, default) in self.comboboxes.items():
268
value = self.get_option(option, default)
269
for index in range(combobox.count()):
270
data = from_qvariant(combobox.itemData(index), unicode)
271
# For PyQt API v2, it is necessary to convert `data` to
272
# unicode in case the original type was not a string, like an
273
# integer for example (see spyderlib.qt.compat.from_qvariant):
274
if unicode(data) == unicode(value):
276
combobox.setCurrentIndex(index)
277
self.connect(combobox, SIGNAL('currentIndexChanged(int)'),
278
lambda _foo, opt=option: self.has_been_modified(opt))
279
for (fontbox, sizebox), option in self.fontboxes.items():
280
font = self.get_font(option)
281
fontbox.setCurrentFont(font)
282
sizebox.setValue(font.pointSize())
284
property = 'plugin_font'
287
self.connect(fontbox, SIGNAL('currentIndexChanged(int)'),
288
lambda _foo, opt=property: self.has_been_modified(opt))
289
self.connect(sizebox, SIGNAL('valueChanged(int)'),
290
lambda _foo, opt=property: self.has_been_modified(opt))
291
for clayout, (option, default) in self.coloredits.items():
292
property = to_qvariant(option)
293
edit = clayout.lineedit
294
btn = clayout.colorbtn
295
edit.setText(self.get_option(option, default))
296
self.connect(btn, SIGNAL('clicked()'),
297
lambda opt=option: self.has_been_modified(opt))
298
self.connect(edit, SIGNAL("textChanged(QString)"),
299
lambda _foo, opt=option: self.has_been_modified(opt))
300
for (clayout, cb_bold, cb_italic
301
), (option, default) in self.scedits.items():
302
edit = clayout.lineedit
303
btn = clayout.colorbtn
304
color, bold, italic = self.get_option(option, default)
306
cb_bold.setChecked(bold)
307
cb_italic.setChecked(italic)
308
self.connect(btn, SIGNAL('clicked()'),
309
lambda opt=option: self.has_been_modified(opt))
310
self.connect(edit, SIGNAL("textChanged(QString)"),
311
lambda _foo, opt=option: self.has_been_modified(opt))
312
self.connect(cb_bold, SIGNAL("clicked(bool)"),
313
lambda _foo, opt=option: self.has_been_modified(opt))
314
self.connect(cb_italic, SIGNAL("clicked(bool)"),
315
lambda _foo, opt=option: self.has_been_modified(opt))
317
def save_to_conf(self):
318
"""Save settings to configuration file"""
319
for checkbox, (option, _default) in self.checkboxes.items():
320
self.set_option(option, checkbox.isChecked())
321
for radiobutton, (option, _default) in self.radiobuttons.items():
322
self.set_option(option, radiobutton.isChecked())
323
for lineedit, (option, _default) in self.lineedits.items():
324
self.set_option(option, unicode(lineedit.text()))
325
for spinbox, (option, _default) in self.spinboxes.items():
326
self.set_option(option, spinbox.value())
327
for combobox, (option, _default) in self.comboboxes.items():
328
data = combobox.itemData(combobox.currentIndex())
329
self.set_option(option, from_qvariant(data, unicode))
330
for (fontbox, sizebox), option in self.fontboxes.items():
331
font = fontbox.currentFont()
332
font.setPointSize(sizebox.value())
333
self.set_font(font, option)
334
for clayout, (option, _default) in self.coloredits.items():
335
self.set_option(option, unicode(clayout.lineedit.text()))
336
for (clayout, cb_bold, cb_italic), (option, _default) in self.scedits.items():
337
color = unicode(clayout.lineedit.text())
338
bold = cb_bold.isChecked()
339
italic = cb_italic.isChecked()
340
self.set_option(option, (color, bold, italic))
343
def has_been_modified(self, option):
344
self.set_modified(True)
345
self.changed_options.add(option)
347
def create_checkbox(self, text, option, default=NoDefault,
348
tip=None, msg_warning=None, msg_info=None,
349
msg_if_enabled=False):
350
checkbox = QCheckBox(text)
352
checkbox.setToolTip(tip)
353
self.checkboxes[checkbox] = (option, default)
354
if msg_warning is not None or msg_info is not None:
355
def show_message(is_checked):
356
if is_checked or not msg_if_enabled:
357
if msg_warning is not None:
358
QMessageBox.warning(self, self.get_name(),
359
msg_warning, QMessageBox.Ok)
360
if msg_info is not None:
361
QMessageBox.information(self, self.get_name(),
362
msg_info, QMessageBox.Ok)
363
self.connect(checkbox, SIGNAL("clicked(bool)"), show_message)
366
def create_radiobutton(self, text, option, default=NoDefault,
367
tip=None, msg_warning=None, msg_info=None,
368
msg_if_enabled=False, button_group=None):
369
radiobutton = QRadioButton(text)
370
if button_group is None:
371
if self.default_button_group is None:
372
self.default_button_group = QButtonGroup(self)
373
button_group = self.default_button_group
374
button_group.addButton(radiobutton)
376
radiobutton.setToolTip(tip)
377
self.radiobuttons[radiobutton] = (option, default)
378
if msg_warning is not None or msg_info is not None:
379
def show_message(is_checked):
380
if is_checked or not msg_if_enabled:
381
if msg_warning is not None:
382
QMessageBox.warning(self, self.get_name(),
383
msg_warning, QMessageBox.Ok)
384
if msg_info is not None:
385
QMessageBox.information(self, self.get_name(),
386
msg_info, QMessageBox.Ok)
387
self.connect(radiobutton, SIGNAL("toggled(bool)"), show_message)
390
def create_lineedit(self, text, option, default=NoDefault,
391
tip=None, alignment=Qt.Vertical):
393
label.setWordWrap(True)
395
layout = QVBoxLayout() if alignment == Qt.Vertical else QHBoxLayout()
396
layout.addWidget(label)
397
layout.addWidget(edit)
398
layout.setContentsMargins(0, 0, 0, 0)
401
self.lineedits[edit] = (option, default)
402
widget = QWidget(self)
403
widget.setLayout(layout)
406
def create_browsedir(self, text, option, default=NoDefault, tip=None):
407
widget = self.create_lineedit(text, option, default,
408
alignment=Qt.Horizontal)
409
for edit in self.lineedits:
410
if widget.isAncestorOf(edit):
412
msg = _("Invalid directory path")
413
self.validate_data[edit] = (osp.isdir, msg)
414
browse_btn = QPushButton(get_std_icon('DirOpenIcon'), "", self)
415
browse_btn.setToolTip(_("Select directory"))
416
self.connect(browse_btn, SIGNAL("clicked()"),
417
lambda: self.select_directory(edit))
418
layout = QHBoxLayout()
419
layout.addWidget(widget)
420
layout.addWidget(browse_btn)
421
layout.setContentsMargins(0, 0, 0, 0)
422
browsedir = QWidget(self)
423
browsedir.setLayout(layout)
426
def select_directory(self, edit):
427
"""Select directory"""
428
basedir = unicode(edit.text())
429
if not osp.isdir(basedir):
430
basedir = os.getcwdu()
431
title = _("Select directory")
432
directory = getexistingdirectory(self, title, basedir)
434
edit.setText(directory)
436
def create_browsefile(self, text, option, default=NoDefault, tip=None,
438
widget = self.create_lineedit(text, option, default,
439
alignment=Qt.Horizontal)
440
for edit in self.lineedits:
441
if widget.isAncestorOf(edit):
443
msg = _("Invalid file path")
444
self.validate_data[edit] = (osp.isfile, msg)
445
browse_btn = QPushButton(get_std_icon('FileIcon'), "", self)
446
browse_btn.setToolTip(_("Select file"))
447
self.connect(browse_btn, SIGNAL("clicked()"),
448
lambda: self.select_file(edit, filters))
449
layout = QHBoxLayout()
450
layout.addWidget(widget)
451
layout.addWidget(browse_btn)
452
layout.setContentsMargins(0, 0, 0, 0)
453
browsedir = QWidget(self)
454
browsedir.setLayout(layout)
457
def select_file(self, edit, filters=None):
459
basedir = osp.dirname(unicode(edit.text()))
460
if not osp.isdir(basedir):
461
basedir = os.getcwdu()
463
filters = _("All files (*)")
464
title = _("Select file")
465
filename, _selfilter = getopenfilename(self, title, basedir, filters)
467
edit.setText(filename)
469
def create_spinbox(self, prefix, suffix, option, default=NoDefault,
470
min_=None, max_=None, step=None, tip=None):
472
plabel = QLabel(prefix)
476
slabel = QLabel(suffix)
481
spinbox.setMinimum(min_)
483
spinbox.setMaximum(max_)
485
spinbox.setSingleStep(step)
487
spinbox.setToolTip(tip)
488
self.spinboxes[spinbox] = (option, default)
489
layout = QHBoxLayout()
490
for subwidget in (plabel, spinbox, slabel):
491
if subwidget is not None:
492
layout.addWidget(subwidget)
494
layout.setContentsMargins(0, 0, 0, 0)
495
widget = QWidget(self)
496
widget.setLayout(layout)
499
def create_coloredit(self, text, option, default=NoDefault, tip=None,
500
without_layout=False):
502
clayout = ColorLayout(QColor(Qt.black), self)
503
clayout.lineedit.setMaximumWidth(80)
505
clayout.setToolTip(tip)
506
self.coloredits[clayout] = (option, default)
508
return label, clayout
509
layout = QHBoxLayout()
510
layout.addWidget(label)
511
layout.addLayout(clayout)
513
layout.setContentsMargins(0, 0, 0, 0)
514
widget = QWidget(self)
515
widget.setLayout(layout)
518
def create_scedit(self, text, option, default=NoDefault, tip=None,
519
without_layout=False):
521
clayout = ColorLayout(QColor(Qt.black), self)
522
clayout.lineedit.setMaximumWidth(80)
524
clayout.setToolTip(tip)
525
cb_bold = QCheckBox()
526
cb_bold.setIcon(get_icon("bold.png"))
527
cb_bold.setToolTip(_("Bold"))
528
cb_italic = QCheckBox()
529
cb_italic.setIcon(get_icon("italic.png"))
530
cb_italic.setToolTip(_("Italic"))
531
self.scedits[(clayout, cb_bold, cb_italic)] = (option, default)
533
return label, clayout, cb_bold, cb_italic
534
layout = QHBoxLayout()
535
layout.addWidget(label)
536
layout.addLayout(clayout)
537
layout.addSpacing(10)
538
layout.addWidget(cb_bold)
539
layout.addWidget(cb_italic)
541
layout.setContentsMargins(0, 0, 0, 0)
542
widget = QWidget(self)
543
widget.setLayout(layout)
546
def create_combobox(self, text, choices, option, default=NoDefault,
548
"""choices: couples (name, key)"""
550
combobox = QComboBox()
552
combobox.setToolTip(tip)
553
for name, key in choices:
554
combobox.addItem(name, to_qvariant(key))
555
self.comboboxes[combobox] = (option, default)
556
layout = QHBoxLayout()
557
for subwidget in (label, combobox):
558
layout.addWidget(subwidget)
560
layout.setContentsMargins(0, 0, 0, 0)
561
widget = QWidget(self)
562
widget.setLayout(layout)
565
def create_fontgroup(self, option=None, text=None,
566
tip=None, fontfilters=None):
567
"""Option=None -> setting plugin font"""
568
fontlabel = QLabel(_("Font: "))
569
fontbox = QFontComboBox()
570
if fontfilters is not None:
571
fontbox.setFontFilters(fontfilters)
572
sizelabel = QLabel(" "+_("Size: "))
574
sizebox.setRange(7, 100)
575
self.fontboxes[(fontbox, sizebox)] = option
576
layout = QHBoxLayout()
577
for subwidget in (fontlabel, fontbox, sizelabel, sizebox):
578
layout.addWidget(subwidget)
581
text = _("Font style")
582
group = QGroupBox(text)
583
group.setLayout(layout)
585
group.setToolTip(tip)
588
def create_button(self, text, callback):
589
btn = QPushButton(text)
590
self.connect(btn, SIGNAL('clicked()'), callback)
591
self.connect(btn, SIGNAL('clicked()'),
592
lambda opt='': self.has_been_modified(opt))
595
def create_tab(self, *widgets):
596
"""Create simple tab widget page: widgets added in a vertical layout"""
598
layout = QVBoxLayout()
600
layout.addWidget(widg)
602
widget.setLayout(layout)
606
class GeneralConfigPage(SpyderConfigPage):
608
def __init__(self, parent, main):
609
SpyderConfigPage.__init__(self, parent)
612
def set_option(self, option, value):
613
CONF.set(self.CONF_SECTION, option, value)
615
def get_option(self, option, default=NoDefault):
616
return CONF.get(self.CONF_SECTION, option, default)
618
def apply_settings(self, options):
619
raise NotImplementedError
622
class MainConfigPage(GeneralConfigPage):
623
CONF_SECTION = "main"
628
return get_icon("genprefs.png")
630
def setup_page(self):
631
interface_group = QGroupBox(_("Interface"))
632
styles = [str(txt) for txt in QStyleFactory.keys()]
633
choices = zip(styles, [style.lower() for style in styles])
634
style_combo = self.create_combobox(_('Qt windows style'), choices,
636
default=self.main.default_style)
637
newcb = self.create_checkbox
638
vertdock_box = newcb(_("Vertical dockwidget title bars"),
639
'vertical_dockwidget_titlebars')
640
verttabs_box = newcb(_("Vertical dockwidget tabs"),
642
animated_box = newcb(_("Animated toolbars and dockwidgets"),
644
margin_box = newcb(_("Custom dockwidget margin:"),
646
margin_spin = self.create_spinbox("", "pixels", 'custom_margin',
648
self.connect(margin_box, SIGNAL("toggled(bool)"),
649
margin_spin.setEnabled)
650
margin_spin.setEnabled(self.get_option('use_custom_margin'))
651
margins_layout = QHBoxLayout()
652
margins_layout.addWidget(margin_box)
653
margins_layout.addWidget(margin_spin)
655
single_instance_box = newcb(_("Use a single instance"),
657
tip=_("Set this to open external<br> "
658
"python, .spy and .mat files in an "
659
"already running instance (Requires "
662
interface_layout = QVBoxLayout()
663
interface_layout.addWidget(style_combo)
664
interface_layout.addWidget(single_instance_box)
665
interface_layout.addWidget(vertdock_box)
666
interface_layout.addWidget(verttabs_box)
667
interface_layout.addWidget(animated_box)
668
interface_layout.addLayout(margins_layout)
669
interface_group.setLayout(interface_layout)
671
sbar_group = QGroupBox(_("Status bar"))
672
memory_box = newcb(_("Show memory usage every"), 'memory_usage/enable',
673
tip=self.main.mem_status.toolTip())
674
memory_spin = self.create_spinbox("", " ms", 'memory_usage/timeout',
675
min_=100, max_=1000000, step=100)
676
self.connect(memory_box, SIGNAL("toggled(bool)"),
677
memory_spin.setEnabled)
678
memory_spin.setEnabled(self.get_option('memory_usage/enable'))
679
memory_layout = QHBoxLayout()
680
memory_layout.addWidget(memory_box)
681
memory_layout.addWidget(memory_spin)
682
memory_layout.setEnabled(self.main.mem_status.is_supported())
683
cpu_box = newcb(_("Show CPU usage every"), 'cpu_usage/enable',
684
tip=self.main.cpu_status.toolTip())
685
cpu_spin = self.create_spinbox("", " ms", 'cpu_usage/timeout',
686
min_=100, max_=1000000, step=100)
687
self.connect(cpu_box, SIGNAL("toggled(bool)"), cpu_spin.setEnabled)
688
cpu_spin.setEnabled(self.get_option('cpu_usage/enable'))
689
cpu_layout = QHBoxLayout()
690
cpu_layout.addWidget(cpu_box)
691
cpu_layout.addWidget(cpu_spin)
692
cpu_layout.setEnabled(self.main.cpu_status.is_supported())
694
sbar_layout = QVBoxLayout()
695
sbar_layout.addLayout(memory_layout)
696
sbar_layout.addLayout(cpu_layout)
697
sbar_group.setLayout(sbar_layout)
699
debug_group = QGroupBox(_("Debugging"))
700
popup_console_box = newcb(_("Pop up internal console when errors "
702
'show_internal_console_if_traceback',
705
debug_layout = QVBoxLayout()
706
debug_layout.addWidget(popup_console_box)
707
debug_group.setLayout(debug_layout)
709
vlayout = QVBoxLayout()
710
vlayout.addWidget(interface_group)
711
vlayout.addWidget(sbar_group)
712
vlayout.addWidget(debug_group)
713
vlayout.addStretch(1)
714
self.setLayout(vlayout)
716
def apply_settings(self, options):
717
self.main.apply_settings()
720
class ColorSchemeConfigPage(GeneralConfigPage):
721
CONF_SECTION = "color_schemes"
723
return _("Syntax coloring")
726
return get_icon("genprefs.png")
728
def setup_page(self):
730
names = self.get_option("names")
731
names.pop(names.index(CUSTOM_COLOR_SCHEME_NAME))
732
names.insert(0, CUSTOM_COLOR_SCHEME_NAME)
734
"background": _("Background:"),
735
"currentline": _("Current line:"),
736
"occurence": _("Occurence:"),
737
"ctrlclick": _("Link:"),
738
"sideareas": _("Side areas:"),
739
"matched_p": _("Matched parentheses:"),
740
"unmatched_p": _("Unmatched parentheses:"),
741
"normal": _("Normal text:"),
742
"keyword": _("Keyword:"),
743
"builtin": _("Builtin:"),
744
"definition": _("Definition:"),
745
"comment": _("Comment:"),
746
"string": _("String:"),
747
"number": _("Number:"),
748
"instance": _("Instance:"),
750
from spyderlib.widgets.sourcecode import syntaxhighlighters
751
assert all([key in fieldnames
752
for key in syntaxhighlighters.COLOR_SCHEME_KEYS])
753
for tabname in names:
754
cs_group = QGroupBox(_("Color scheme"))
755
cs_layout = QGridLayout()
756
for row, key in enumerate(syntaxhighlighters.COLOR_SCHEME_KEYS):
757
option = "%s/%s" % (tabname, key)
758
value = self.get_option(option)
759
name = fieldnames[key]
760
if isinstance(value, basestring):
761
label, clayout = self.create_coloredit(name, option,
763
label.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
764
cs_layout.addWidget(label, row+1, 0)
765
cs_layout.addLayout(clayout, row+1, 1)
767
label, clayout, cb_bold, cb_italic = self.create_scedit(
768
name, option, without_layout=True)
769
label.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
770
cs_layout.addWidget(label, row+1, 0)
771
cs_layout.addLayout(clayout, row+1, 1)
772
cs_layout.addWidget(cb_bold, row+1, 2)
773
cs_layout.addWidget(cb_italic, row+1, 3)
774
cs_group.setLayout(cs_layout)
775
if tabname in COLOR_SCHEME_NAMES:
776
def_btn = self.create_button(_("Reset to default values"),
777
lambda: self.reset_to_default(tabname))
778
tabs.addTab(self.create_tab(cs_group, def_btn), tabname)
780
tabs.addTab(self.create_tab(cs_group), tabname)
782
vlayout = QVBoxLayout()
783
vlayout.addWidget(tabs)
784
self.setLayout(vlayout)
787
def reset_to_default(self, name):
788
set_default_color_scheme(name, replace=True)
789
self.load_from_conf()
791
def apply_settings(self, options):
792
self.main.editor.apply_plugin_settings(['color_scheme_name'])
793
if self.main.historylog is not None:
794
self.main.historylog.apply_plugin_settings(['color_scheme_name'])
795
if self.main.inspector is not None:
796
self.main.inspector.apply_plugin_settings(['color_scheme_name'])