4
4
# Licensed under the terms of the MIT License
5
5
# (see spyderlib/__init__.py for details)
9
9
# pylint: disable-msg=C0103
10
10
# pylint: disable-msg=R0903
11
11
# pylint: disable-msg=R0911
12
12
# pylint: disable-msg=R0201
14
from PyQt4.QtGui import QFileDialog, QMessageBox, QFontDialog, QMenu
14
from PyQt4.QtGui import QMessageBox, QFontDialog, QMenu
15
15
from PyQt4.QtCore import SIGNAL
24
24
from spyderlib.config import (CONF, get_conf_path, str2type, get_icon,
25
25
get_font, set_font)
26
from spyderlib.utils.qthelpers import create_action, add_actions, translate
27
from spyderlib.utils.iofuncs import (save_dictionary, load_dictionary,
28
load_array, load_image, load_dicom,
29
load_matlab, save_matlab)
26
from spyderlib.utils import fix_reference_name
27
from spyderlib.utils.qthelpers import create_action, add_actions
28
from spyderlib.utils.iofuncs import iofunctions
30
29
from spyderlib.widgets.dicteditor import DictEditorTableView, globalsfilter
31
from spyderlib.plugins import PluginMixin
30
from spyderlib.plugins import SpyderPluginMixin
34
33
FILTERS = tuple(str2type(CONF.get('workspace', 'filters')))
65
64
DictEditorTableView.__init__(self, parent, None, names=True,
66
65
truncate=truncate, inplace=inplace,
67
66
minmax=minmax, collvalue=collvalue)
68
PluginMixin.__init__(self, parent)
67
SpyderPluginMixin.__init__(self, parent)
71
69
self.load_temp_namespace()
73
71
self.setFont(get_font(self.ID))
76
"""Setup I/O functions and filters"""
78
('.spydata', translate('Workspace', "Spyder data files"),
79
load_dictionary, save_dictionary),
80
('.npy', translate('Workspace', "NumPy arrays"),
82
('.mat', translate('Workspace', "Matlab files"),
83
load_matlab, save_matlab),
84
('.csv', translate('Workspace', "CSV text files"),
85
'import_wizard', None),
86
('.txt', translate('Workspace', "Text files"),
87
'import_wizard', None),
88
('.jpg', translate('Workspace', "JPEG images"),
90
('.png', translate('Workspace', "PNG images"),
92
('.gif', translate('Workspace', "GIF images"),
94
('.tif', translate('Workspace', "TIFF images"),
96
('.dcm', translate('Workspace', "DICOM images"),
104
for ext, name, loadfunc, savefunc in iofuncs:
105
filter_str = unicode(name + " (*%s)" % ext)
106
if loadfunc is not None:
107
load_filters.append(filter_str)
108
load_funcs[ext] = loadfunc
110
if savefunc is not None:
111
save_filters.append(filter_str)
112
save_funcs[ext] = savefunc
113
load_filters.insert(0, unicode(self.tr("Supported files")+" (*"+\
114
" *".join(load_ext)+")"))
115
self.load_filters = "\n".join(load_filters)
116
self.save_filters = "\n".join(save_filters)
117
self.load_funcs = load_funcs
118
self.save_funcs = save_funcs
120
def get_widget_title(self):
73
#------ SpyderPluginWidget API ---------------------------------------------
74
def get_plugin_title(self):
121
75
"""Return widget title"""
122
76
return self.tr('Workspace')
131
def set_interpreter(self, interpreter):
132
"""Bind to interpreter"""
133
self.interpreter = interpreter
134
self.refresh(force=True)
136
def get_namespace(self, itermax=ITERMAX):
137
"""Return filtered namespace"""
138
return wsfilter(self.namespace, itermax=itermax)
140
def __clear_namespace(self):
141
"""Clear namespace"""
142
keys = self.get_namespace().keys()
144
self.namespace.pop(key)
145
self.refresh(force=True)
148
"""Ask to clear workspace"""
149
answer = QMessageBox.question(self, self.tr("Clear workspace"),
150
self.tr("Do you want to clear all data from workspace?"),
151
QMessageBox.Yes | QMessageBox.No)
152
if answer == QMessageBox.Yes:
153
self.__clear_namespace()
155
def refresh(self, force=False):
157
if CONF.get(self.ID, 'autorefresh') or force:
158
self.refresh_editor()
160
def refresh_editor(self):
161
"""Refresh DictEditor"""
162
if self.interpreter is not None:
163
self.namespace = self.interpreter.namespace
164
self.set_filter( wsfilter )
165
self.set_data( self.namespace )
166
self.adjust_columns()
168
def set_actions(self):
85
def get_plugin_actions(self):
169
86
"""Setup actions"""
170
87
import_action = create_action(self, self.tr("Import data..."), None,
171
88
'ws_open.png', self.tr("Import data to workspace"),
233
150
toolbar_actions = (refresh_action, import_action, save_as_action)
234
151
return (menu_actions, toolbar_actions)
236
def change_font1(self):
238
self.__change_font('dicteditor_header')
240
def change_font2(self):
242
self.__change_font('dicteditor')
244
def __change_font(self, section):
245
font, valid = QFontDialog.getFont(get_font(section), self,
246
self.tr("Select a new font"))
248
set_font(font, section)
250
def toggle_autorefresh(self, checked):
251
"""Toggle autorefresh mode"""
252
CONF.set(self.ID, 'autorefresh', checked)
255
def toggle_autosave(self, checked):
256
"""Toggle autosave mode"""
257
CONF.set(self.ID, 'autosave', checked)
259
def closing(self, cancelable=False):
153
def refresh_plugin(self, force=False):
155
if CONF.get(self.ID, 'autorefresh') or force:
156
self.refresh_editor()
158
def closing_plugin(self, cancelable=False):
260
159
"""Perform actions before parent main window is closed"""
261
160
if CONF.get(self.ID, 'autosave'):
262
161
# Saving workspace
278
177
buttons = QMessageBox.Yes | QMessageBox.No
280
179
buttons = buttons | QMessageBox.Cancel
281
answer = QMessageBox.question(self, self.get_widget_title(),
180
answer = QMessageBox.question(self, self.get_plugin_title(),
282
181
self.tr("Workspace is currently keeping reference "
283
182
"to %1 object%2.\n\nDo you want to save %3?") \
284
183
.arg(srefnb).arg(s_or_not).arg(it_or_them), buttons)
291
190
# Removing last saved workspace
292
191
os.remove(self.TEMPFILE_PATH)
194
#------ Public API ---------------------------------------------------------
195
def set_interpreter(self, interpreter):
196
"""Bind to interpreter"""
197
self.interpreter = interpreter
198
self.refresh_plugin(force=True)
200
def get_namespace(self, itermax=ITERMAX):
201
"""Return filtered namespace"""
202
return wsfilter(self.namespace, itermax=itermax)
204
def __clear_namespace(self):
205
"""Clear namespace"""
206
keys = self.get_namespace().keys()
208
self.namespace.pop(key)
209
self.refresh_plugin(force=True)
212
"""Ask to clear workspace"""
213
answer = QMessageBox.question(self, self.tr("Clear workspace"),
214
self.tr("Do you want to clear all data from workspace?"),
215
QMessageBox.Yes | QMessageBox.No)
216
if answer == QMessageBox.Yes:
217
self.__clear_namespace()
219
def refresh_editor(self):
220
"""Refresh DictEditor"""
221
if self.interpreter is not None:
222
self.namespace = self.interpreter.namespace
223
self.set_filter( wsfilter )
224
self.set_data( self.namespace )
225
self.adjust_columns()
227
def change_font1(self):
229
self.__change_font('dicteditor_header')
231
def change_font2(self):
233
self.__change_font('dicteditor')
235
def __change_font(self, section):
236
font, valid = QFontDialog.getFont(get_font(section), self,
237
self.tr("Select a new font"))
239
set_font(font, section)
241
def toggle_autorefresh(self, checked):
242
"""Toggle autorefresh mode"""
243
CONF.set(self.ID, 'autorefresh', checked)
244
self.refresh_plugin()
246
def toggle_autosave(self, checked):
247
"""Toggle autosave mode"""
248
CONF.set(self.ID, 'autosave', checked)
295
250
def load_temp_namespace(self):
296
251
"""Attempt to load last session namespace"""
310
265
if filename is None:
311
266
self.emit(SIGNAL('redirect_stdio(bool)'), False)
312
267
basedir = osp.dirname(self.filename)
313
filename = QFileDialog.getOpenFileName(self,
314
title, basedir, self.load_filters)
268
filename = iofunctions.get_open_filename(self, basedir, title)
315
269
self.emit(SIGNAL('redirect_stdio(bool)'), True)
317
271
filename = unicode(filename)
320
274
self.filename = unicode(filename)
321
ext = osp.splitext(self.filename)[1]
275
ext = osp.splitext(self.filename)[1].lower()
323
if ext not in self.load_funcs:
277
if ext not in iofunctions.load_funcs:
324
278
buttons = QMessageBox.Yes | QMessageBox.Cancel
325
279
answer = QMessageBox.question(self, title,
326
280
self.tr("<b>Unsupported file type '%1'</b><br><br>"
346
300
self.starting_long_process(self.tr("Loading data..."))
347
301
namespace, error_message = load_func(self.filename)
348
self.ending_long_process()
302
if self.namespace is not None:
303
for key in namespace.keys():
304
new_key = fix_reference_name(key,
305
blacklist=self.interpreter.namespace.keys())
307
namespace[new_key] = namespace.pop(key)
308
self.ending_long_process()
349
309
if error_message is None:
350
310
if self.namespace is None:
351
311
self.namespace = namespace
357
317
self.tr("<b>Unable to load '%1'</b>"
358
318
"<br><br>Error message:<br>%2") \
359
319
.arg(self.filename).arg(error_message))
360
self.refresh(force=True)
320
self.refresh_plugin(force=True)
362
322
def save_as(self):
363
323
"""Save current workspace as"""
364
324
self.emit(SIGNAL('redirect_stdio(bool)'), False)
365
filename = QFileDialog.getSaveFileName(self,
366
self.tr("Save workspace"), self.filename,
325
filename = iofunctions.get_save_filename(self, self.filename,
326
self.tr("Save workspace"))
368
327
self.emit(SIGNAL('redirect_stdio(bool)'), True)
370
329
self.filename = unicode(filename)
376
335
"""Save workspace"""
377
336
title = self.tr("Save workspace")
379
ext = osp.splitext(filename)[1]
380
if ext not in self.save_funcs:
381
QMessageBox.critical(self, title,
382
self.tr("<b>Unsupported file type '%1'</b>") \
386
338
self.starting_long_process(self.tr("Saving workspace..."))
387
339
namespace = self.get_namespace(itermax=-1).copy()
388
error_message = self.save_funcs[ext](namespace, filename)
340
error_message = iofunctions.save(namespace, filename)
389
341
self.ending_long_process()
391
343
if error_message is not None:
394
346
"<br><br>Error message:<br>%1") \
395
347
.arg(error_message))
397
self.refresh(force=True)
349
self.refresh_plugin(force=True)
400
352
def toggle_exclude_private(self, checked):
401
353
"""Toggle exclude private references"""
402
354
CONF.set(self.ID, 'exclude_private', checked)
403
self.refresh(force=True)
355
self.refresh_plugin(force=True)
405
357
def toggle_exclude_upper(self, checked):
406
358
"""Toggle exclude upper-case references"""
407
359
CONF.set(self.ID, 'exclude_upper', checked)
408
self.refresh(force=True)
360
self.refresh_plugin(force=True)
410
362
def toggle_exclude_unsupported(self, checked):
411
363
"""Toggle exclude unsupported datatypes"""
412
364
CONF.set(self.ID, 'exclude_unsupported', checked)
413
self.refresh(force=True)
365
self.refresh_plugin(force=True)
416
368
def focusInEvent(self, event):