16
16
import os.path as osp
19
from spyderlib.qt.QtGui import (QMenu, QApplication, QCursor, QToolTip,
20
QKeySequence, QMessageBox, QMouseEvent,
21
QTextCursor, QTextCharFormat, QShortcut)
22
from spyderlib.qt.QtCore import Qt, QCoreApplication, QTimer, SIGNAL, Property
20
from spyderlib.qt.QtGui import (QMenu, QApplication, QToolTip, QKeySequence,
21
QMessageBox, QTextCursor, QTextCharFormat,
23
from spyderlib.qt.QtCore import (Qt, QCoreApplication, QTimer, SIGNAL,
23
25
from spyderlib.qt.compat import getsavefilename
26
from spyderlib.baseconfig import get_conf_path, _, STDERR
27
from spyderlib.config import CONF, get_icon, get_font
28
from spyderlib.baseconfig import get_conf_path, _, STDERR, DEBUG
29
from spyderlib.guiconfig import CONF, get_font
28
30
from spyderlib.utils import encoding
29
from spyderlib.utils.misc import get_error_match
30
from spyderlib.utils.dochelpers import getobj
31
31
from spyderlib.utils.qthelpers import (keybinding, create_action, add_actions,
32
restore_keyevent, get_icon)
33
33
from spyderlib.widgets.sourcecode.base import ConsoleBaseWidget
36
HISTORY_FILENAMES = []
39
class ShellBaseWidget(ConsoleBaseWidget):
34
from spyderlib.widgets.mixins import (InspectObjectMixin, TracebackLinksMixin,
38
class ShellBaseWidget(ConsoleBaseWidget, SaveHistoryMixin):
46
def __init__(self, parent, history_filename, debug=False, profile=False):
43
def __init__(self, parent, history_filename, profile=False):
48
45
parent : specifies the parent widget
50
47
ConsoleBaseWidget.__init__(self, parent)
48
SaveHistoryMixin.__init__(self)
52
50
# Prompt position: tuple (line, index)
53
51
self.current_prompt_pos = None
306
304
# (otherwise, right below, we would remove selection
307
305
# if not on current line)
308
306
ctrl = event.modifiers() & Qt.ControlModifier
309
if event.key() == Qt.Key_C and ctrl:
307
meta = event.modifiers() & Qt.MetaModifier # meta=ctrl in OSX
308
if event.key() == Qt.Key_C and \
309
((Qt.MetaModifier | Qt.ControlModifier) & event.modifiers()):
310
if meta and sys.platform == 'darwin':
373
376
method('word' if ctrl else 'character', direction='right')
375
378
elif (key == Qt.Key_Home) or ((key == Qt.Key_Up) and ctrl):
376
self._key_home(shift)
379
self._key_home(shift, ctrl)
378
381
elif (key == Qt.Key_End) or ((key == Qt.Key_Down) and ctrl):
382
self._key_end(shift, ctrl)
381
384
elif key == Qt.Key_Up:
382
385
if not self.is_cursor_on_last_line():
404
407
# the event queue - i.e. if the busy buffer is ever implemented)
405
408
ConsoleBaseWidget.keyPressEvent(self, event)
407
elif key == Qt.Key_Escape and ctrl and shift:
408
self._key_ctrl_shift_escape()
410
410
elif key == Qt.Key_Escape and shift:
411
self._key_shift_escape()
413
413
elif key == Qt.Key_Escape:
414
414
self._key_escape()
416
elif key == Qt.Key_L and ctrl:
417
self.clear_terminal()
416
419
elif key == Qt.Key_V and ctrl:
461
464
raise NotImplementedError
462
465
def _key_ctrl_space(self):
463
466
raise NotImplementedError
464
def _key_home(self, shift):
467
def _key_home(self, shift, ctrl):
465
468
raise NotImplementedError
466
def _key_end(self, shift):
469
def _key_end(self, shift, ctrl):
467
470
raise NotImplementedError
468
471
def _key_pageup(self):
469
472
raise NotImplementedError
471
474
raise NotImplementedError
472
475
def _key_escape(self):
473
476
raise NotImplementedError
474
def _key_shift_escape(self):
475
raise NotImplementedError
476
def _key_ctrl_shift_escape(self):
477
raise NotImplementedError
478
477
def _key_question(self, text):
479
478
raise NotImplementedError
480
479
def _key_parenleft(self, text):
506
505
encoding.writelines(rawhistory, self.history_filename)
509
def add_to_history(self, command):
510
"""Add command to history"""
511
command = unicode(command)
512
if command in ['', '\n'] or command.startswith('Traceback'):
514
if command.endswith('\n'):
515
command = command[:-1]
517
if len(self.history)>0 and self.history[-1] == command:
519
self.history.append(command)
520
text = os.linesep + command
522
# When the first entry will be written in history file,
523
# the separator will be append first:
524
if self.history_filename not in HISTORY_FILENAMES:
525
HISTORY_FILENAMES.append(self.history_filename)
526
text = self.SEPARATOR + text
528
encoding.write(text, self.history_filename, mode='ab')
529
self.emit(SIGNAL('append_to_history(QString,QString)'),
530
self.history_filename, text)
532
508
def browse_history(self, backward):
533
509
"""Browse history"""
534
510
if self.is_cursor_before('eol') and self.hist_wholeline:
633
609
return ConsoleBaseWidget.focusNextPrevChild(self, next)
635
def mousePressEvent(self, event):
637
Re-implemented to handle the mouse press event.
638
event: the mouse press event (QMouseEvent)
640
if event.button() == Qt.MidButton:
641
text = self.get_selected_text()
642
# Simulating left mouse button:
643
event = QMouseEvent(QMouseEvent.MouseButtonPress, event.pos(),
644
Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
645
ConsoleBaseWidget.mousePressEvent(self, event)
646
if self.new_input_line:
648
self.insert_text(text)
651
ConsoleBaseWidget.mousePressEvent(self, event)
654
612
#------ Drag and drop
682
640
raise NotImplementedError
685
class PythonShellWidget(ShellBaseWidget):
643
class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget,
645
"""Python shell widget"""
646
QT_CLASS = ShellBaseWidget
689
648
INITHISTORY = ['# -*- coding: utf-8 -*-',
690
649
'# *** Spyder Python Console History Log ***',]
691
650
SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime())
693
def __init__(self, parent, history_filename, debug=False, profile=False):
694
ShellBaseWidget.__init__(self, parent, history_filename, debug, profile)
696
self.inspector = None
697
self.inspector_enabled = True
700
self.__cursor_changed = False
652
def __init__(self, parent, history_filename, profile=False):
653
ShellBaseWidget.__init__(self, parent, history_filename, profile)
654
TracebackLinksMixin.__init__(self)
655
InspectObjectMixin.__init__(self)
702
657
# Local shortcuts
703
658
self.inspectsc = QShortcut(QKeySequence("Ctrl+I"), self,
755
710
QApplication.clipboard().setText(text)
759
def mouseReleaseEvent(self, event):
761
ConsoleBaseWidget.mouseReleaseEvent(self, event)
762
text = self.get_line_at(event.pos())
763
if get_error_match(text) and not self.has_selected_text():
764
self.emit(SIGNAL("go_to_error(QString)"), text)
766
def mouseMoveEvent(self, event):
767
"""Show Pointing Hand Cursor on error messages"""
768
text = self.get_line_at(event.pos())
769
if get_error_match(text):
770
if not self.__cursor_changed:
771
QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor))
772
self.__cursor_changed = True
775
if self.__cursor_changed:
776
QApplication.restoreOverrideCursor()
777
self.__cursor_changed = False
778
ConsoleBaseWidget.mouseMoveEvent(self, event)
780
def leaveEvent(self, event):
781
"""If cursor has not been restored yet, do it now"""
782
if self.__cursor_changed:
783
QApplication.restoreOverrideCursor()
784
self.__cursor_changed = False
785
ConsoleBaseWidget.leaveEvent(self, event)
788
713
#------ Key handlers
789
714
def postprocess_keyevent(self, event):
790
715
"""Process keypress event"""
827
752
if not self.is_completion_widget_visible():
828
753
self.show_code_completion(automatic=False)
830
def _key_home(self, shift):
755
def _key_home(self, shift, ctrl):
831
756
"""Action for Home key"""
832
757
if self.is_cursor_on_last_line():
833
self.stdkey_home(shift, self.current_prompt_pos)
758
self.stdkey_home(shift, ctrl, self.current_prompt_pos)
835
def _key_end(self, shift):
760
def _key_end(self, shift, ctrl):
836
761
"""Action for End key"""
837
762
if self.is_cursor_on_last_line():
838
self.stdkey_end(shift)
763
self.stdkey_end(shift, ctrl)
840
765
def _key_pageup(self):
841
766
"""Action for PageUp key"""
911
830
def get_dir(self, objtxt):
912
831
"""Return dir(object)"""
913
832
raise NotImplementedError
914
def get_completion(self, objtxt):
915
"""Return completion list associated to object name"""
917
833
def get_module_completion(self, objtxt):
918
834
"""Return module completion list associated to object name"""
970
886
automatic=automatic)
973
#-- IPython only -------------------------------------------------------
974
# Using IPython code completion feature: __IP.complete
975
elif ' ' in text and not text.endswith(' '):
976
try1 = text.split(' ')[-1]
977
obj_list = self.get_completion(try1)
979
self.show_completion_list(obj_list, completion_text=try1,
982
elif text.startswith('%'):
983
# IPython magic commands
984
obj_list = self.get_completion(text)
986
self.show_completion_list(obj_list, completion_text=text,
988
# There is no point continuing the process when text starts with '%'
990
obj_list = self.get_completion(last_obj)
991
if not text.endswith('.') and last_obj and obj_list:
992
self.show_completion_list(obj_list, completion_text=last_obj,
995
#-----------------------------------------------------------------------
997
889
obj_dir = self.get_dir(last_obj)
998
890
if last_obj and obj_dir and text.endswith('.'):
999
891
self.show_completion_list(obj_dir, automatic=automatic)
1036
928
completion_text=text[q_pos+1:],
1037
929
automatic=automatic)
1040
def show_docstring(self, text, call=False, force=False):
1041
"""Show docstring or arguments"""
1042
text = unicode(text) # Useful only for ExternalShellBase
1044
insp_enabled = self.inspector_enabled or force
1045
if force and self.inspector is not None:
1046
self.inspector.dockwidget.setVisible(True)
1047
self.inspector.dockwidget.raise_()
1048
if insp_enabled and (self.inspector is not None) and \
1049
(self.inspector.dockwidget.isVisible()):
1050
# ObjectInspector widget exists and is visible
1051
self.inspector.set_shell(self)
1052
self.inspector.set_object_text(text, ignore_unknown=True)
1053
self.setFocus() # if inspector was not at top level, raising it to
1054
# top will automatically give it focus because of
1055
# the visibility_changed signal, so we must give
1056
# focus back to shell
1057
if call and self.calltips:
1058
# Display argument list if this is function call
1059
iscallable = self.iscallable(text)
1060
if iscallable is not None:
1062
arglist = self.get_arglist(text)
1063
if isinstance(arglist, bool):
1066
self.show_calltip(_("Arguments"),
1068
elif self.calltips: # inspector is not visible or link is disabled
1069
doc = self.get__doc__(text)
1071
self.show_calltip(_("Documentation"), doc)
1074
#------ Miscellanous
1075
def get_last_obj(self, last=False):
1077
Return the last valid object on the current line
1079
return getobj(self.get_current_line_to_cursor(), last=last)
1081
def set_inspector(self, inspector):
1082
"""Set ObjectInspector DockWidget reference"""
1083
self.inspector = inspector
1084
self.inspector.set_shell(self)
1086
def set_inspector_enabled(self, state):
1087
self.inspector_enabled = state
1089
def inspect_current_object(self):
1091
text1 = self.get_text('sol', 'cursor')
1092
tl1 = re.findall(r'([a-zA-Z_]+[0-9a-zA-Z_\.]*)', text1)
1093
if tl1 and text1.endswith(tl1[-1]):
1095
text2 = self.get_text('cursor', 'eol')
1096
tl2 = re.findall(r'([0-9a-zA-Z_\.]+[0-9a-zA-Z_\.]*)', text2)
1097
if tl2 and text2.startswith(tl2[0]):
1100
self.show_docstring(text, force=True)
1102
932
#------ Drag'n Drop
1103
933
def drop_pathlist(self, pathlist):
1122
952
INITHISTORY = ['%s *** Spyder Terminal History Log ***' % COM, COM,]
1123
953
SEPARATOR = '%s%s ---(%s)---' % (os.linesep*2, COM, time.ctime())
1125
def __init__(self, parent, history_filename, debug=False, profile=False):
1126
ShellBaseWidget.__init__(self, parent, history_filename, debug, profile)
955
def __init__(self, parent, history_filename, profile=False):
956
ShellBaseWidget.__init__(self, parent, history_filename, profile)
1128
958
#------ Key handlers
1129
959
def _key_other(self, text):