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)
11
Here, 'plugins' are widgets designed specifically for Spyder
12
These plugins inherit the following classes
13
(SpyderPluginMixin & SpyderPluginWidget)
16
# pylint: disable=C0103
17
# pylint: disable=R0903
18
# pylint: disable=R0911
19
# pylint: disable=R0201
21
from spyderlib.qt.QtGui import (QDockWidget, QWidget, QShortcut, QCursor,
22
QKeySequence, QMainWindow, QApplication)
23
from spyderlib.qt.QtCore import SIGNAL, Qt, QObject, Signal
26
from spyderlib.utils.qthelpers import toggle_actions
27
from spyderlib.config import CONF, get_font, set_font, get_icon
28
from spyderlib.userconfig import NoDefault
29
from spyderlib.plugins.configdialog import SpyderConfigPage
32
class PluginConfigPage(SpyderConfigPage):
33
"""Plugin configuration dialog box page widget"""
34
def __init__(self, plugin, parent):
36
self.get_option = plugin.get_option
37
self.set_option = plugin.set_option
38
self.get_name = plugin.get_plugin_title
39
self.get_icon = plugin.get_plugin_icon
40
self.get_font = plugin.get_plugin_font
41
self.set_font = plugin.set_plugin_font
42
self.apply_settings = plugin.apply_plugin_settings
43
SpyderConfigPage.__init__(self, parent)
46
class SpyderPluginMixin(object):
48
Useful methods to bind widgets to the main window
49
See SpyderPluginWidget class for required widget interface
54
plugin.sig_option_changed.emit('show_all', checked)
55
'show_message(QString,int)'
58
CONFIGWIDGET_CLASS = None
59
ALLOWED_AREAS = Qt.AllDockWidgetAreas
60
LOCATION = Qt.LeftDockWidgetArea
61
FEATURES = QDockWidget.DockWidgetClosable | \
62
QDockWidget.DockWidgetFloatable | \
63
QDockWidget.DockWidgetMovable
64
DISABLE_ACTIONS_WHEN_HIDDEN = True
65
sig_option_changed = None
66
def __init__(self, main):
67
"""Bind widget to a QMainWindow instance"""
68
super(SpyderPluginMixin, self).__init__()
69
assert self.CONF_SECTION is not None
71
self.default_margins = None
72
self.plugin_actions = None
73
self.dockwidget = None
74
self.mainwindow = None
75
self.ismaximized = False
76
self.isvisible = False
78
def initialize_plugin(self):
79
"""Initialize plugin: connect signals, setup actions, ..."""
80
self.plugin_actions = self.get_plugin_actions()
81
QObject.connect(self, SIGNAL('show_message(QString,int)'),
83
QObject.connect(self, SIGNAL('update_plugin_title()'),
84
self.__update_plugin_title)
85
if self.sig_option_changed is not None:
86
self.sig_option_changed.connect(self.set_option)
87
self.setWindowTitle(self.get_plugin_title())
89
def update_margins(self):
90
layout = self.layout()
91
if self.default_margins is None:
92
self.default_margins = layout.getContentsMargins()
93
if CONF.get('main', 'use_custom_margin', True):
94
margin = CONF.get('main', 'custom_margin', 0)
95
layout.setContentsMargins(*[margin]*4)
97
layout.setContentsMargins(*self.default_margins)
99
def __update_plugin_title(self):
100
"""Update plugin title, i.e. dockwidget or mainwindow title"""
101
if self.dockwidget is not None:
102
win = self.dockwidget
103
elif self.mainwindow is not None:
104
win = self.mainwindow
107
win.setWindowTitle(self.get_plugin_title())
109
def create_dockwidget(self):
110
"""Add to parent QMainWindow as a dock widget"""
112
# This is not clear yet why the following do not work...
114
## # Using Qt.Window window flags solves Issue #880 (detached dockwidgets
115
## # are not painted after restarting Spyder and restoring their hexstate)
116
## # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked)
117
## # or non-Windows platforms (lot of warnings are printed out)
118
## # (so in those cases, we use the default window flags: Qt.Widget):
119
## flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window
120
dock = QDockWidget(self.get_plugin_title(), self.main)#, flags)
122
dock.setObjectName(self.__class__.__name__+"_dw")
123
dock.setAllowedAreas(self.ALLOWED_AREAS)
124
dock.setFeatures(self.FEATURES)
126
self.update_margins()
127
self.connect(dock, SIGNAL('visibilityChanged(bool)'),
128
self.visibility_changed)
129
self.dockwidget = dock
130
short = self.get_option("shortcut", None)
131
if short is not None:
132
shortcut = QShortcut(QKeySequence(short),
133
self.main, self.switch_to_plugin)
134
self.register_shortcut(shortcut, "_",
135
"Switch to %s" % self.CONF_SECTION,
137
return (dock, self.LOCATION)
139
def create_mainwindow(self):
141
Create a QMainWindow instance containing this plugin
142
Note: this method is currently not used
144
self.mainwindow = mainwindow = QMainWindow()
145
mainwindow.setAttribute(Qt.WA_DeleteOnClose)
146
icon = self.get_widget_icon()
147
if isinstance(icon, basestring):
148
icon = get_icon(icon)
149
mainwindow.setWindowIcon(icon)
150
mainwindow.setWindowTitle(self.get_plugin_title())
151
mainwindow.setCentralWidget(self)
152
self.refresh_plugin()
155
def create_configwidget(self, parent):
156
"""Create configuration dialog box page widget"""
157
if self.CONFIGWIDGET_CLASS is not None:
158
configwidget = self.CONFIGWIDGET_CLASS(self, parent)
159
configwidget.initialize()
162
def apply_plugin_settings(self, options):
163
"""Apply configuration file's plugin settings"""
164
raise NotImplementedError
166
def register_shortcut(self, qaction_or_qshortcut, context, name,
169
Register QAction or QShortcut to Spyder main application,
170
with shortcut (context, name, default)
172
self.main.register_shortcut(qaction_or_qshortcut,
173
context, name, default)
175
def register_widget_shortcuts(self, context, widget):
177
Register widget shortcuts
178
widget interface must have a method called 'get_shortcut_data'
180
for qshortcut, name, default in widget.get_shortcut_data():
181
self.register_shortcut(qshortcut, context, name, default)
183
def switch_to_plugin(self):
185
This method is called when pressing plugin's shortcut key"""
186
if not self.ismaximized:
187
self.dockwidget.show()
188
self.visibility_changed(True)
190
def visibility_changed(self, enable):
191
"""DockWidget visibility has changed"""
193
self.dockwidget.raise_()
194
widget = self.get_focus_widget()
195
if widget is not None:
197
visible = self.dockwidget.isVisible() or self.ismaximized
198
if self.DISABLE_ACTIONS_WHEN_HIDDEN:
199
toggle_actions(self.plugin_actions, visible)
201
self.refresh_plugin() #XXX Is it a good idea?
202
self.isvisible = enable and visible
204
def set_option(self, option, value):
206
Set a plugin option in configuration file
207
Use a SIGNAL to call it, e.g.:
208
plugin.sig_option_changed.emit('show_all', checked)
210
CONF.set(self.CONF_SECTION, str(option), value)
212
def get_option(self, option, default=NoDefault):
213
"""Get a plugin option from configuration file"""
214
return CONF.get(self.CONF_SECTION, option, default)
216
def get_plugin_font(self, option=None):
217
"""Return plugin font option"""
218
return get_font(self.CONF_SECTION, option)
220
def set_plugin_font(self, font, option=None):
221
"""Set plugin font option"""
222
set_font(font, self.CONF_SECTION, option)
224
def show_message(self, message, timeout=0):
225
"""Show message in main window's status bar"""
226
self.main.statusBar().showMessage(message, timeout)
228
def starting_long_process(self, message):
230
Showing message in main window's status bar
231
and changing mouse cursor to Qt.WaitCursor
233
self.show_message(message)
234
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
235
QApplication.processEvents()
237
def ending_long_process(self, message=""):
239
Clearing main window's status bar
240
and restoring mouse cursor
242
QApplication.restoreOverrideCursor()
243
self.show_message(message, timeout=2000)
244
QApplication.processEvents()
246
def set_default_color_scheme(self, name='Spyder'):
247
"""Set default color scheme (only once)"""
248
color_scheme_name = self.get_option('color_scheme_name', None)
249
if color_scheme_name is None:
250
names = CONF.get("color_schemes", "names")
251
if name not in names:
253
self.set_option('color_scheme_name', name)
256
class SpyderPluginWidget(QWidget, SpyderPluginMixin):
258
Spyder base widget class
259
Spyder's widgets either inherit this class or reimplement its interface
261
sig_option_changed = Signal(str, object)
263
def __init__(self, parent):
264
QWidget.__init__(self, parent)
265
SpyderPluginMixin.__init__(self, parent)
267
def get_plugin_title(self):
270
Note: after some thinking, it appears that using a method
271
is more flexible here than using a class attribute
273
raise NotImplementedError
275
def get_plugin_icon(self):
277
Return plugin icon (QIcon instance)
278
Note: this is required for plugins creating a main window
279
(see SpyderPluginMixin.create_mainwindow)
280
and for configuration dialog widgets creation
282
return get_icon('qt.png')
284
def get_focus_widget(self):
286
Return the widget to give focus to when
287
this plugin's dockwidget is raised on top-level
291
def closing_plugin(self, cancelable=False):
293
Perform actions before parent main window is closed
294
Return True or False whether the plugin may be closed immediately or not
295
Note: returned value is ignored if *cancelable* is False
297
raise NotImplementedError
299
def refresh_plugin(self):
301
raise NotImplementedError
303
def get_plugin_actions(self):
305
Return a list of actions related to plugin
306
Note: these actions will be enabled when plugin's dockwidget is visible
307
and they will be disabled when it's hidden
309
raise NotImplementedError
311
def register_plugin(self):
312
"""Register plugin in Spyder's main window"""
313
raise NotImplementedError
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)
11
Here, 'plugins' are widgets designed specifically for Spyder
12
These plugins inherit the following classes
13
(SpyderPluginMixin & SpyderPluginWidget)
16
# pylint: disable=C0103
17
# pylint: disable=R0903
18
# pylint: disable=R0911
19
# pylint: disable=R0201
21
from spyderlib.qt.QtGui import (QDockWidget, QWidget, QShortcut, QCursor,
22
QKeySequence, QMainWindow, QApplication)
23
from spyderlib.qt.QtCore import SIGNAL, Qt, QObject, Signal
26
from spyderlib.utils.qthelpers import toggle_actions
27
from spyderlib.config import CONF
28
from spyderlib.userconfig import NoDefault
29
from spyderlib.guiconfig import get_font, set_font, get_icon
30
from spyderlib.plugins.configdialog import SpyderConfigPage
33
class PluginConfigPage(SpyderConfigPage):
34
"""Plugin configuration dialog box page widget"""
35
def __init__(self, plugin, parent):
37
self.get_option = plugin.get_option
38
self.set_option = plugin.set_option
39
self.get_name = plugin.get_plugin_title
40
self.get_icon = plugin.get_plugin_icon
41
self.get_font = plugin.get_plugin_font
42
self.set_font = plugin.set_plugin_font
43
self.apply_settings = plugin.apply_plugin_settings
44
SpyderConfigPage.__init__(self, parent)
47
class SpyderPluginMixin(object):
49
Useful methods to bind widgets to the main window
50
See SpyderPluginWidget class for required widget interface
55
plugin.sig_option_changed.emit('show_all', checked)
56
'show_message(QString,int)'
59
CONFIGWIDGET_CLASS = None
60
ALLOWED_AREAS = Qt.AllDockWidgetAreas
61
LOCATION = Qt.LeftDockWidgetArea
62
FEATURES = QDockWidget.DockWidgetClosable | \
63
QDockWidget.DockWidgetFloatable | \
64
QDockWidget.DockWidgetMovable
65
DISABLE_ACTIONS_WHEN_HIDDEN = True
66
sig_option_changed = None
67
def __init__(self, main):
68
"""Bind widget to a QMainWindow instance"""
69
super(SpyderPluginMixin, self).__init__()
70
assert self.CONF_SECTION is not None
72
self.default_margins = None
73
self.plugin_actions = None
74
self.dockwidget = None
75
self.mainwindow = None
76
self.ismaximized = False
77
self.isvisible = False
79
def initialize_plugin(self):
80
"""Initialize plugin: connect signals, setup actions, ..."""
81
self.plugin_actions = self.get_plugin_actions()
82
QObject.connect(self, SIGNAL('show_message(QString,int)'),
84
QObject.connect(self, SIGNAL('update_plugin_title()'),
85
self.__update_plugin_title)
86
if self.sig_option_changed is not None:
87
self.sig_option_changed.connect(self.set_option)
88
self.setWindowTitle(self.get_plugin_title())
90
def update_margins(self):
91
layout = self.layout()
92
if self.default_margins is None:
93
self.default_margins = layout.getContentsMargins()
94
if CONF.get('main', 'use_custom_margin', True):
95
margin = CONF.get('main', 'custom_margin', 0)
96
layout.setContentsMargins(*[margin]*4)
98
layout.setContentsMargins(*self.default_margins)
100
def __update_plugin_title(self):
101
"""Update plugin title, i.e. dockwidget or mainwindow title"""
102
if self.dockwidget is not None:
103
win = self.dockwidget
104
elif self.mainwindow is not None:
105
win = self.mainwindow
108
win.setWindowTitle(self.get_plugin_title())
110
def create_dockwidget(self):
111
"""Add to parent QMainWindow as a dock widget"""
113
# This is not clear yet why the following do not work...
115
## # Using Qt.Window window flags solves Issue #880 (detached dockwidgets
116
## # are not painted after restarting Spyder and restoring their hexstate)
117
## # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked)
118
## # or non-Windows platforms (lot of warnings are printed out)
119
## # (so in those cases, we use the default window flags: Qt.Widget):
120
## flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window
121
dock = QDockWidget(self.get_plugin_title(), self.main)#, flags)
123
dock.setObjectName(self.__class__.__name__+"_dw")
124
dock.setAllowedAreas(self.ALLOWED_AREAS)
125
dock.setFeatures(self.FEATURES)
127
self.update_margins()
128
self.connect(dock, SIGNAL('visibilityChanged(bool)'),
129
self.visibility_changed)
130
self.dockwidget = dock
131
short = self.get_option("shortcut", None)
132
if short is not None:
133
shortcut = QShortcut(QKeySequence(short),
134
self.main, self.switch_to_plugin)
135
self.register_shortcut(shortcut, "_",
136
"Switch to %s" % self.CONF_SECTION,
138
return (dock, self.LOCATION)
140
def create_mainwindow(self):
142
Create a QMainWindow instance containing this plugin
143
Note: this method is currently not used
145
self.mainwindow = mainwindow = QMainWindow()
146
mainwindow.setAttribute(Qt.WA_DeleteOnClose)
147
icon = self.get_widget_icon()
148
if isinstance(icon, basestring):
149
icon = get_icon(icon)
150
mainwindow.setWindowIcon(icon)
151
mainwindow.setWindowTitle(self.get_plugin_title())
152
mainwindow.setCentralWidget(self)
153
self.refresh_plugin()
156
def create_configwidget(self, parent):
157
"""Create configuration dialog box page widget"""
158
if self.CONFIGWIDGET_CLASS is not None:
159
configwidget = self.CONFIGWIDGET_CLASS(self, parent)
160
configwidget.initialize()
163
def apply_plugin_settings(self, options):
164
"""Apply configuration file's plugin settings"""
165
raise NotImplementedError
167
def register_shortcut(self, qaction_or_qshortcut, context, name,
170
Register QAction or QShortcut to Spyder main application,
171
with shortcut (context, name, default)
173
self.main.register_shortcut(qaction_or_qshortcut,
174
context, name, default)
176
def register_widget_shortcuts(self, context, widget):
178
Register widget shortcuts
179
widget interface must have a method called 'get_shortcut_data'
181
for qshortcut, name, default in widget.get_shortcut_data():
182
self.register_shortcut(qshortcut, context, name, default)
184
def switch_to_plugin(self):
186
This method is called when pressing plugin's shortcut key"""
187
if not self.ismaximized:
188
self.dockwidget.show()
189
self.visibility_changed(True)
191
def visibility_changed(self, enable):
192
"""DockWidget visibility has changed"""
194
self.dockwidget.raise_()
195
widget = self.get_focus_widget()
196
if widget is not None:
198
visible = self.dockwidget.isVisible() or self.ismaximized
199
if self.DISABLE_ACTIONS_WHEN_HIDDEN:
200
toggle_actions(self.plugin_actions, visible)
202
self.refresh_plugin() #XXX Is it a good idea?
203
self.isvisible = enable and visible
205
def set_option(self, option, value):
207
Set a plugin option in configuration file
208
Use a SIGNAL to call it, e.g.:
209
plugin.sig_option_changed.emit('show_all', checked)
211
CONF.set(self.CONF_SECTION, str(option), value)
213
def get_option(self, option, default=NoDefault):
214
"""Get a plugin option from configuration file"""
215
return CONF.get(self.CONF_SECTION, option, default)
217
def get_plugin_font(self, option=None):
218
"""Return plugin font option"""
219
return get_font(self.CONF_SECTION, option)
221
def set_plugin_font(self, font, option=None):
222
"""Set plugin font option"""
223
set_font(font, self.CONF_SECTION, option)
225
def show_message(self, message, timeout=0):
226
"""Show message in main window's status bar"""
227
self.main.statusBar().showMessage(message, timeout)
229
def starting_long_process(self, message):
231
Showing message in main window's status bar
232
and changing mouse cursor to Qt.WaitCursor
234
self.show_message(message)
235
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
236
QApplication.processEvents()
238
def ending_long_process(self, message=""):
240
Clearing main window's status bar
241
and restoring mouse cursor
243
QApplication.restoreOverrideCursor()
244
self.show_message(message, timeout=2000)
245
QApplication.processEvents()
247
def set_default_color_scheme(self, name='Spyder'):
248
"""Set default color scheme (only once)"""
249
color_scheme_name = self.get_option('color_scheme_name', None)
250
if color_scheme_name is None:
251
names = CONF.get("color_schemes", "names")
252
if name not in names:
254
self.set_option('color_scheme_name', name)
257
class SpyderPluginWidget(QWidget, SpyderPluginMixin):
259
Spyder base widget class
260
Spyder's widgets either inherit this class or reimplement its interface
262
sig_option_changed = Signal(str, object)
264
def __init__(self, parent):
265
QWidget.__init__(self, parent)
266
SpyderPluginMixin.__init__(self, parent)
268
def get_plugin_title(self):
271
Note: after some thinking, it appears that using a method
272
is more flexible here than using a class attribute
274
raise NotImplementedError
276
def get_plugin_icon(self):
278
Return plugin icon (QIcon instance)
279
Note: this is required for plugins creating a main window
280
(see SpyderPluginMixin.create_mainwindow)
281
and for configuration dialog widgets creation
283
return get_icon('qt.png')
285
def get_focus_widget(self):
287
Return the widget to give focus to when
288
this plugin's dockwidget is raised on top-level
292
def closing_plugin(self, cancelable=False):
294
Perform actions before parent main window is closed
295
Return True or False whether the plugin may be closed immediately or not
296
Note: returned value is ignored if *cancelable* is False
298
raise NotImplementedError
300
def refresh_plugin(self):
302
raise NotImplementedError
304
def get_plugin_actions(self):
306
Return a list of actions related to plugin
307
Note: these actions will be enabled when plugin's dockwidget is visible
308
and they will be disabled when it's hidden
310
raise NotImplementedError
312
def register_plugin(self):
313
"""Register plugin in Spyder's main window"""
314
raise NotImplementedError