##// END OF EJS Templates
Added more documentation for MagicHelper.
Dimitry Kloper -
Show More
@@ -1,163 +1,216 b''
1 """Magic Helper - dockable widget showing magic commands for the MainWindow
1 """MagicHelper - dockable widget showing magic commands for the MainWindow
2
2
3
3
4 Authors:
4 Authors:
5
5
6 * Dimitry Kloper
6 * Dimitry Kloper
7
7
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 # stdlib imports
14 # stdlib imports
15 import json
15 import json
16 import re
16 import re
17 import sys
17 import sys
18
18
19 # System library imports
19 # System library imports
20 from IPython.external.qt import QtGui,QtCore
20 from IPython.external.qt import QtGui,QtCore
21
21
22 from IPython.core.magic import magic_escapes
22 from IPython.core.magic import magic_escapes
23
23
24 class MagicHelper(QtGui.QDockWidget):
24 class MagicHelper(QtGui.QDockWidget):
25 """MagicHelper - dockable widget for convenient search and running of
26 magic command for IPython QtConsole.
27 """
28
29 #---------------------------------------------------------------------------
30 # signals
31 #---------------------------------------------------------------------------
25
32
26 pasteRequested = QtCore.pyqtSignal(str, name = 'pasteRequested')
33 pasteRequested = QtCore.pyqtSignal(str, name = 'pasteRequested')
34 """This signal is emitted when user wants to paste selected magic
35 command into the command line.
36 """
37
27 runRequested = QtCore.pyqtSignal(str, name = 'runRequested')
38 runRequested = QtCore.pyqtSignal(str, name = 'runRequested')
39 """This signal is emitted when user wants to execute selected magic command
40 """
41
28 readyForUpdate = QtCore.pyqtSignal(name = 'readyForUpdate')
42 readyForUpdate = QtCore.pyqtSignal(name = 'readyForUpdate')
43 """This signal is emitted when MagicHelper is ready to be populated.
44 Since kernel querying mechanisms are out of scope of this class,
45 it expects its owner to invoke MagicHelper.populate_magic_helper()
46 as a reaction on this event.
47 """
29
48
30 #---------------------------------------------------------------------------
49 #---------------------------------------------------------------------------
31 # 'object' interface
50 # constructor
32 #---------------------------------------------------------------------------
51 #---------------------------------------------------------------------------
33
52
34 def __init__(self, name, parent):
53 def __init__(self, name, parent):
35
36 super(MagicHelper, self).__init__(name, parent)
54 super(MagicHelper, self).__init__(name, parent)
37
55
38 self.data = None
56 self.data = None
39
57
40 class MinListWidget(QtGui.QListWidget):
58 class MinListWidget(QtGui.QListWidget):
59 """Temp class to overide the default QListWidget size hint
60 in order to make MagicHelper narrow
61 """
41 def sizeHint(self):
62 def sizeHint(self):
42 s = QtCore.QSize()
63 s = QtCore.QSize()
43 s.setHeight(super(MinListWidget,self).sizeHint().height())
64 s.setHeight(super(MinListWidget,self).sizeHint().height())
44 s.setWidth(self.sizeHintForColumn(0))
65 s.setWidth(self.sizeHintForColumn(0))
45 return s
66 return s
46
67
68 # construct content
47 self.frame = QtGui.QFrame()
69 self.frame = QtGui.QFrame()
48 self.search_label = QtGui.QLabel("Search:")
70 self.search_label = QtGui.QLabel("Search:")
49 self.search_line = QtGui.QLineEdit()
71 self.search_line = QtGui.QLineEdit()
50 self.search_class = QtGui.QComboBox()
72 self.search_class = QtGui.QComboBox()
51 self.search_list = MinListWidget()
73 self.search_list = MinListWidget()
52 self.paste_button = QtGui.QPushButton("Paste")
74 self.paste_button = QtGui.QPushButton("Paste")
53 self.run_button = QtGui.QPushButton("Run")
75 self.run_button = QtGui.QPushButton("Run")
54
76
77 # layout all the widgets
55 main_layout = QtGui.QVBoxLayout()
78 main_layout = QtGui.QVBoxLayout()
56 search_layout = QtGui.QHBoxLayout()
79 search_layout = QtGui.QHBoxLayout()
57 search_layout.addWidget(self.search_label)
80 search_layout.addWidget(self.search_label)
58 search_layout.addWidget(self.search_line, 10)
81 search_layout.addWidget(self.search_line, 10)
59 main_layout.addLayout(search_layout)
82 main_layout.addLayout(search_layout)
60 main_layout.addWidget(self.search_class)
83 main_layout.addWidget(self.search_class)
61 main_layout.addWidget(self.search_list, 10)
84 main_layout.addWidget(self.search_list, 10)
62 action_layout = QtGui.QHBoxLayout()
85 action_layout = QtGui.QHBoxLayout()
63 action_layout.addWidget(self.paste_button)
86 action_layout.addWidget(self.paste_button)
64 action_layout.addWidget(self.run_button)
87 action_layout.addWidget(self.run_button)
65 main_layout.addLayout(action_layout)
88 main_layout.addLayout(action_layout)
66
89
67 self.frame.setLayout(main_layout)
90 self.frame.setLayout(main_layout)
68 self.setWidget(self.frame)
91 self.setWidget(self.frame)
69
92
70 self.visibilityChanged[bool].connect( self.update_magic_helper )
93 # connect all the relevant signals to handlers
94 self.visibilityChanged[bool].connect( self._update_magic_helper )
71 self.search_class.activated[int].connect(
95 self.search_class.activated[int].connect(
72 self.class_selected
96 self.class_selected
73 )
97 )
74 self.search_line.textChanged[str].connect(
98 self.search_line.textChanged[str].connect(
75 self.search_changed
99 self.search_changed
76 )
100 )
77 self.search_list.itemDoubleClicked[QtGui.QListWidgetItem].connect(
101 self.search_list.itemDoubleClicked[QtGui.QListWidgetItem].connect(
78 self.paste_requested
102 self.paste_requested
79 )
103 )
80 self.paste_button.clicked[bool].connect(
104 self.paste_button.clicked[bool].connect(
81 self.paste_requested
105 self.paste_requested
82 )
106 )
83 self.run_button.clicked[bool].connect(
107 self.run_button.clicked[bool].connect(
84 self.run_requested
108 self.run_requested
85 )
109 )
86
110
87 def update_magic_helper(self, visible):
111 #---------------------------------------------------------------------------
112 # implementation
113 #---------------------------------------------------------------------------
114
115 def _update_magic_helper(self, visible):
116 """Start update sequence.
117 This method is called when MagicHelper becomes visible. It clears
118 the content and emits readyForUpdate signal. The owner of the
119 instance is expected to invoke populate_magic_helper() when magic
120 info is available.
121 """
88 if not visible or self.data != None:
122 if not visible or self.data != None:
89 return
123 return
90 self.data = {}
124 self.data = {}
91 self.search_class.clear()
125 self.search_class.clear()
92 self.search_class.addItem("Populating...")
126 self.search_class.addItem("Populating...")
127 self.search_list.clear()
93 self.readyForUpdate.emit()
128 self.readyForUpdate.emit()
94
129
95 def populate_magic_helper(self, data):
130 def populate_magic_helper(self, data):
131 """Expects data returned by lsmagics query from kernel.
132 Populates the search_class and search_list with relevant items.
133 """
96 self.search_class.clear()
134 self.search_class.clear()
97 self.search_list.clear()
135 self.search_list.clear()
98
136
99 self.data = json.loads(
137 self.data = json.loads(
100 data['data'].get('application/json', {})
138 data['data'].get('application/json', {})
101 )
139 )
102
140
103 self.search_class.addItem('All Magics', 'any')
141 self.search_class.addItem('All Magics', 'any')
104 classes = set()
142 classes = set()
105
143
106 for mtype in sorted(self.data):
144 for mtype in sorted(self.data):
107 subdict = self.data[mtype]
145 subdict = self.data[mtype]
108 for name in sorted(subdict):
146 for name in sorted(subdict):
109 classes.add(subdict[name])
147 classes.add(subdict[name])
110
148
111 for cls in sorted(classes):
149 for cls in sorted(classes):
112 label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls)
150 label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls)
113 self.search_class.addItem(label, cls)
151 self.search_class.addItem(label, cls)
114
152
115 self.filter_magic_helper('.', 'any')
153 self.filter_magic_helper('.', 'any')
116
154
117 def class_selected(self, index):
155 def class_selected(self, index):
156 """Handle search_class selection changes
157 """
118 item = self.search_class.itemData(index)
158 item = self.search_class.itemData(index)
119 regex = self.search_line.text()
159 regex = self.search_line.text()
120 self.filter_magic_helper(regex = regex, cls = item)
160 self.filter_magic_helper(regex = regex, cls = item)
121
161
122 def search_changed(self, search_string):
162 def search_changed(self, search_string):
163 """Handle search_line text changes.
164 The text is interpreted as a regular expression
165 """
123 item = self.search_class.itemData(
166 item = self.search_class.itemData(
124 self.search_class.currentIndex()
167 self.search_class.currentIndex()
125 )
168 )
126 self.filter_magic_helper(regex = search_string, cls = item)
169 self.filter_magic_helper(regex = search_string, cls = item)
127
170
128 def _get_current_search_item(self, item = None):
171 def _get_current_search_item(self, item = None):
172 """Retrieve magic command currently selected in the search_list
173 """
129 text = None
174 text = None
130 if not isinstance(item, QtGui.QListWidgetItem):
175 if not isinstance(item, QtGui.QListWidgetItem):
131 item = self.search_list.currentItem()
176 item = self.search_list.currentItem()
132 text = item.text()
177 text = item.text()
133 return text
178 return text
134
179
135 def paste_requested(self, item = None):
180 def paste_requested(self, item = None):
181 """Emit pasteRequested signal with currently selected item text
182 """
136 text = self._get_current_search_item(item)
183 text = self._get_current_search_item(item)
137 if text != None:
184 if text != None:
138 self.pasteRequested.emit(text)
185 self.pasteRequested.emit(text)
139
186
140 def run_requested(self, item = None):
187 def run_requested(self, item = None):
188 """Emit runRequested signal with currently selected item text
189 """
141 text = self._get_current_search_item(item)
190 text = self._get_current_search_item(item)
142 if text != None:
191 if text != None:
143 self.runRequested.emit(text)
192 self.runRequested.emit(text)
144
193
145 def filter_magic_helper(self, regex, cls):
194 def filter_magic_helper(self, regex, cls):
195 """Update search_list with magic commands whose text match
196 regex and class match cls.
197 If cls equals 'any' - any class matches.
198 """
146 if regex == "" or regex == None:
199 if regex == "" or regex == None:
147 regex = '.'
200 regex = '.'
148 if cls == None:
201 if cls == None:
149 cls = 'any'
202 cls = 'any'
150
203
151 self.search_list.clear()
204 self.search_list.clear()
152 for mtype in sorted(self.data):
205 for mtype in sorted(self.data):
153 subdict = self.data[mtype]
206 subdict = self.data[mtype]
154 prefix = magic_escapes[mtype]
207 prefix = magic_escapes[mtype]
155
208
156 for name in sorted(subdict):
209 for name in sorted(subdict):
157 mclass = subdict[name]
210 mclass = subdict[name]
158 pmagic = prefix + name
211 pmagic = prefix + name
159
212
160 if (re.match(regex, name) or re.match(regex, pmagic)) and \
213 if (re.match(regex, name) or re.match(regex, pmagic)) and \
161 (cls == 'any' or cls == mclass):
214 (cls == 'any' or cls == mclass):
162 self.search_list.addItem(pmagic)
215 self.search_list.addItem(pmagic)
163
216
General Comments 0
You need to be logged in to leave comments. Login now