##// END OF EJS Templates
Merge pull request #956 from Carreau/all-magic-menu-live...
Fernando Perez -
r5419:8fa65442 merge
parent child Browse files
Show More
@@ -4,6 +4,7 b' from __future__ import print_function'
4 from collections import namedtuple
4 from collections import namedtuple
5 import sys
5 import sys
6 import time
6 import time
7 import uuid
7
8
8 # System library imports
9 # System library imports
9 from pygments.lexers import PythonLexer
10 from pygments.lexers import PythonLexer
@@ -137,6 +138,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
137 self._input_splitter = self._input_splitter_class(input_mode='cell')
138 self._input_splitter = self._input_splitter_class(input_mode='cell')
138 self._kernel_manager = None
139 self._kernel_manager = None
139 self._request_info = {}
140 self._request_info = {}
141 self._callback_dict = {}
140
142
141 # Configure the ConsoleWidget.
143 # Configure the ConsoleWidget.
142 self.tab_width = 4
144 self.tab_width = 4
@@ -311,6 +313,62 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
311 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
313 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
312 self._complete_with_items(cursor, rep['content']['matches'])
314 self._complete_with_items(cursor, rep['content']['matches'])
313
315
316 def _silent_exec_callback(self, expr, callback):
317 """Silently execute `expr` in the kernel and call `callback` with reply
318
319 the `expr` is evaluated silently in the kernel (without) output in
320 the frontend. Call `callback` with the
321 `repr <http://docs.python.org/library/functions.html#repr> `_ as first argument
322
323 Parameters
324 ----------
325 expr : string
326 valid string to be executed by the kernel.
327 callback : function
328 function accepting one arguement, as a string. The string will be
329 the `repr` of the result of evaluating `expr`
330
331 The `callback` is called with the 'repr()' of the result of `expr` as
332 first argument. To get the object, do 'eval()' onthe passed value.
333
334 See Also
335 --------
336 _handle_exec_callback : private method, deal with calling callback with reply
337
338 """
339
340 # generate uuid, which would be used as a indication of wether or not
341 # the unique request originate from here (can use msg id ?)
342 local_uuid = str(uuid.uuid1())
343 msg_id = self.kernel_manager.shell_channel.execute('',
344 silent=True, user_expressions={ local_uuid:expr })
345 self._callback_dict[local_uuid] = callback
346 self._request_info['execute'] = self._ExecutionRequest(msg_id, 'silent_exec_callback')
347
348 def _handle_exec_callback(self, msg):
349 """Execute `callback` corresonding to `msg` reply, after ``_silent_exec_callback``
350
351 Parameters
352 ----------
353 msg : raw message send by the kernel containing an `user_expressions`
354 and having a 'silent_exec_callback' kind.
355
356 Notes
357 -----
358 This fonction will look for a `callback` associated with the
359 corresponding message id. Association has been made by
360 `_silent_exec_callback`. `callback` is then called with the `repr()`
361 of the value of corresponding `user_expressions` as argument.
362 `callback` is then removed from the known list so that any message
363 coming again with the same id won't trigger it.
364
365 """
366
367 user_exp = msg['content']['user_expressions']
368 for expression in user_exp:
369 if expression in self._callback_dict:
370 self._callback_dict.pop(expression)(user_exp[expression])
371
314 def _handle_execute_reply(self, msg):
372 def _handle_execute_reply(self, msg):
315 """ Handles replies for code execution.
373 """ Handles replies for code execution.
316 """
374 """
@@ -342,6 +400,9 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
342
400
343 self._show_interpreter_prompt_for_reply(msg)
401 self._show_interpreter_prompt_for_reply(msg)
344 self.executed.emit(msg)
402 self.executed.emit(msg)
403 elif info and info.id == msg['parent_header']['msg_id'] and \
404 info.kind == 'silent_exec_callback' and not self._hidden:
405 self._handle_exec_callback(msg)
345 else:
406 else:
346 super(FrontendWidget, self)._handle_execute_reply(msg)
407 super(FrontendWidget, self)._handle_execute_reply(msg)
347
408
@@ -20,6 +20,7 b' Authors:'
20
20
21 # stdlib imports
21 # stdlib imports
22 import sys
22 import sys
23 import re
23 import webbrowser
24 import webbrowser
24 from threading import Thread
25 from threading import Thread
25
26
@@ -544,10 +545,89 b' class MainWindow(QtGui.QMainWindow):'
544
545
545 self.kernel_menu.addSeparator()
546 self.kernel_menu.addSeparator()
546
547
548 def _make_dynamic_magic(self,magic):
549 """Return a function `fun` that will execute `magic` on active frontend.
550
551 Parameters
552 ----------
553 magic : string
554 string that will be executed as is when the returned function is called
555
556 Returns
557 -------
558 fun : function
559 function with no parameters, when called will execute `magic` on the
560 current active frontend at call time
561
562 See Also
563 --------
564 populate_all_magic_menu : generate the "All Magics..." menu
565
566 Notes
567 -----
568 `fun` execute `magic` an active frontend at the moment it is triggerd,
569 not the active frontend at the moment it has been created.
570
571 This function is mostly used to create the "All Magics..." Menu at run time.
572 """
573 # need to level nested function to be sure to past magic
574 # on active frontend **at run time**.
575 def inner_dynamic_magic():
576 self.active_frontend.execute(magic)
577 inner_dynamic_magic.__name__ = "dynamics_magic_s"
578 return inner_dynamic_magic
579
580 def populate_all_magic_menu(self, listofmagic=None):
581 """Clean "All Magics..." menu and repopulate it with `listofmagic`
582
583 Parameters
584 ----------
585 listofmagic : string,
586 repr() of a list of strings, send back by the kernel
587
588 Notes
589 -----
590 `listofmagic`is a repr() of list because it is fed with the result of
591 a 'user_expression'
592 """
593 alm_magic_menu = self.all_magic_menu
594 alm_magic_menu.clear()
595
596 # list of protected magic that don't like to be called without argument
597 # append '?' to the end to print the docstring when called from the menu
598 protected_magic = set(["more","less","load_ext","pycat","loadpy","save"])
599 magics=re.findall('\w+', listofmagic)
600 for magic in magics:
601 if magic in protected_magic:
602 pmagic = '%s%s%s'%('%',magic,'?')
603 else:
604 pmagic = '%s%s'%('%',magic)
605 xaction = QtGui.QAction(pmagic,
606 self,
607 triggered=self._make_dynamic_magic(pmagic)
608 )
609 alm_magic_menu.addAction(xaction)
610
611 def update_all_magic_menu(self):
612 """ Update the list on magic in the "All Magics..." Menu
613
614 Request the kernel with the list of availlable magic and populate the
615 menu with the list received back
616
617 """
618 # first define a callback which will get the list of all magic and put it in the menu.
619 self.active_frontend._silent_exec_callback('get_ipython().lsmagic()', self.populate_all_magic_menu)
620
547 def init_magic_menu(self):
621 def init_magic_menu(self):
548 self.magic_menu = self.menuBar().addMenu("&Magic")
622 self.magic_menu = self.menuBar().addMenu("&Magic")
549 self.all_magic_menu = self.magic_menu.addMenu("&All Magics")
623 self.all_magic_menu = self.magic_menu.addMenu("&All Magics")
550
624
625 # this action should not appear as it will be cleard when menu
626 # will be updated at first kernel response.
627 self.pop = QtGui.QAction("&Update All Magic Menu ",
628 self, triggered=self.update_all_magic_menu)
629 self.add_menu_action(self.all_magic_menu, self.pop)
630
551 self.reset_action = QtGui.QAction("&Reset",
631 self.reset_action = QtGui.QAction("&Reset",
552 self,
632 self,
553 statusTip="Clear all varible from workspace",
633 statusTip="Clear all varible from workspace",
@@ -583,34 +663,7 b' class MainWindow(QtGui.QMainWindow):'
583 statusTip="List interactive variable with detail",
663 statusTip="List interactive variable with detail",
584 triggered=self.whos_magic_active_frontend)
664 triggered=self.whos_magic_active_frontend)
585 self.add_menu_action(self.magic_menu, self.whos_action)
665 self.add_menu_action(self.magic_menu, self.whos_action)
586
666
587 # allmagics submenu:
588
589 #for now this is just a copy and paste, but we should get this dynamically
590 magiclist=["%alias", "%autocall", "%automagic", "%bookmark", "%cd", "%clear",
591 "%colors", "%debug", "%dhist", "%dirs", "%doctest_mode", "%ed", "%edit", "%env", "%gui",
592 "%guiref", "%hist", "%history", "%install_default_config", "%install_profiles",
593 "%less", "%load_ext", "%loadpy", "%logoff", "%logon", "%logstart", "%logstate",
594 "%logstop", "%lsmagic", "%macro", "%magic", "%man", "%more", "%notebook", "%page",
595 "%pastebin", "%pdb", "%pdef", "%pdoc", "%pfile", "%pinfo", "%pinfo2", "%popd", "%pprint",
596 "%precision", "%profile", "%prun", "%psearch", "%psource", "%pushd", "%pwd", "%pycat",
597 "%pylab", "%quickref", "%recall", "%rehashx", "%reload_ext", "%rep", "%rerun",
598 "%reset", "%reset_selective", "%run", "%save", "%sc", "%sx", "%tb", "%time", "%timeit",
599 "%unalias", "%unload_ext", "%who", "%who_ls", "%whos", "%xdel", "%xmode"]
600
601 def make_dynamic_magic(i):
602 def inner_dynamic_magic():
603 self.active_frontend.execute(i)
604 inner_dynamic_magic.__name__ = "dynamics_magic_%s" % i
605 return inner_dynamic_magic
606
607 for magic in magiclist:
608 xaction = QtGui.QAction(magic,
609 self,
610 triggered=make_dynamic_magic(magic)
611 )
612 self.all_magic_menu.addAction(xaction)
613
614 def init_window_menu(self):
667 def init_window_menu(self):
615 self.window_menu = self.menuBar().addMenu("&Window")
668 self.window_menu = self.menuBar().addMenu("&Window")
616 if sys.platform == 'darwin':
669 if sys.platform == 'darwin':
@@ -451,6 +451,10 b' class IPythonQtConsoleApp(BaseIPythonApplication):'
451 self.window.log = self.log
451 self.window.log = self.log
452 self.window.add_tab_with_frontend(self.widget)
452 self.window.add_tab_with_frontend(self.widget)
453 self.window.init_menu_bar()
453 self.window.init_menu_bar()
454
455 # we need to populate the 'Magic Menu' once the kernel has answer at least once
456 self.kernel_manager.shell_channel.first_reply.connect(self.window.pop.trigger)
457
454 self.window.setWindowTitle('Python' if self.pure else 'IPython')
458 self.window.setWindowTitle('Python' if self.pure else 'IPython')
455
459
456 def init_colors(self):
460 def init_colors(self):
General Comments 0
You need to be logged in to leave comments. Login now