Show More
@@ -0,0 +1,179 b'' | |||||
|
1 | """Magic Helper - dockable widget showing magic commands for the MainWindow | |||
|
2 | ||||
|
3 | ||||
|
4 | Authors: | |||
|
5 | ||||
|
6 | * Dimitry Kloper | |||
|
7 | ||||
|
8 | """ | |||
|
9 | ||||
|
10 | #----------------------------------------------------------------------------- | |||
|
11 | # Imports | |||
|
12 | #----------------------------------------------------------------------------- | |||
|
13 | ||||
|
14 | # stdlib imports | |||
|
15 | import json | |||
|
16 | import re | |||
|
17 | import sys | |||
|
18 | ||||
|
19 | # System library imports | |||
|
20 | from IPython.external.qt import QtGui,QtCore | |||
|
21 | ||||
|
22 | from IPython.core.magic import magic_escapes | |||
|
23 | ||||
|
24 | class MagicHelper(QtGui.QDockWidget): | |||
|
25 | ||||
|
26 | pasteRequested = QtCore.pyqtSignal(str, name = 'pasteRequested') | |||
|
27 | runRequested = QtCore.pyqtSignal(str, name = 'runRequested') | |||
|
28 | ||||
|
29 | #--------------------------------------------------------------------------- | |||
|
30 | # 'object' interface | |||
|
31 | #--------------------------------------------------------------------------- | |||
|
32 | ||||
|
33 | def __init__(self, name, parent): | |||
|
34 | ||||
|
35 | super(MagicHelper, self).__init__(name, parent) | |||
|
36 | ||||
|
37 | # this is a hack. The main_window reference will be used for | |||
|
38 | # explicit interface to kernel that must be hidden by signal/slot | |||
|
39 | # mechanism in the future | |||
|
40 | self.main_window = parent | |||
|
41 | ||||
|
42 | self.data = None | |||
|
43 | ||||
|
44 | class MinListWidget(QtGui.QListWidget): | |||
|
45 | def sizeHint(self): | |||
|
46 | s = QtCore.QSize() | |||
|
47 | s.setHeight(super(MinListWidget,self).sizeHint().height()) | |||
|
48 | s.setWidth(self.sizeHintForColumn(0)) | |||
|
49 | return s | |||
|
50 | ||||
|
51 | self.frame = QtGui.QFrame() | |||
|
52 | self.search_label = QtGui.QLabel("Search:") | |||
|
53 | self.search_line = QtGui.QLineEdit() | |||
|
54 | self.search_class = QtGui.QComboBox() | |||
|
55 | self.search_list = MinListWidget() | |||
|
56 | self.paste_button = QtGui.QPushButton("Paste") | |||
|
57 | self.run_button = QtGui.QPushButton("Run") | |||
|
58 | ||||
|
59 | main_layout = QtGui.QVBoxLayout() | |||
|
60 | search_layout = QtGui.QHBoxLayout() | |||
|
61 | search_layout.addWidget(self.search_label) | |||
|
62 | search_layout.addWidget(self.search_line, 10) | |||
|
63 | main_layout.addLayout(search_layout) | |||
|
64 | main_layout.addWidget(self.search_class) | |||
|
65 | main_layout.addWidget(self.search_list, 10) | |||
|
66 | action_layout = QtGui.QHBoxLayout() | |||
|
67 | action_layout.addWidget(self.paste_button) | |||
|
68 | action_layout.addWidget(self.run_button) | |||
|
69 | main_layout.addLayout(action_layout) | |||
|
70 | ||||
|
71 | self.frame.setLayout(main_layout) | |||
|
72 | self.setWidget(self.frame) | |||
|
73 | ||||
|
74 | self.visibilityChanged[bool].connect( self.update_magic_helper ) | |||
|
75 | self.search_class.activated[int].connect( | |||
|
76 | self.class_selected | |||
|
77 | ) | |||
|
78 | self.search_line.textChanged[str].connect( | |||
|
79 | self.search_changed | |||
|
80 | ) | |||
|
81 | self.search_list.itemDoubleClicked[QtGui.QListWidgetItem].connect( | |||
|
82 | self.paste_requested | |||
|
83 | ) | |||
|
84 | self.paste_button.clicked[bool].connect( | |||
|
85 | self.paste_requested | |||
|
86 | ) | |||
|
87 | self.run_button.clicked[bool].connect( | |||
|
88 | self.run_requested | |||
|
89 | ) | |||
|
90 | ||||
|
91 | def update_magic_helper(self, visible): | |||
|
92 | if not visible or self.data != None: | |||
|
93 | return | |||
|
94 | self.data = {} | |||
|
95 | self.search_class.clear() | |||
|
96 | self.search_class.addItem("Populating...") | |||
|
97 | self.main_window.active_frontend._silent_exec_callback( | |||
|
98 | 'get_ipython().magic("lsmagic")', | |||
|
99 | self.populate_magic_helper | |||
|
100 | ) | |||
|
101 | ||||
|
102 | def populate_magic_helper(self, data): | |||
|
103 | if not data: | |||
|
104 | return | |||
|
105 | ||||
|
106 | if data['status'] != 'ok': | |||
|
107 | self.main_window.log.warn( | |||
|
108 | "%%lsmagic user-expression failed: {}".format(data) | |||
|
109 | ) | |||
|
110 | return | |||
|
111 | ||||
|
112 | self.search_class.clear() | |||
|
113 | self.search_list.clear() | |||
|
114 | ||||
|
115 | self.data = json.loads( | |||
|
116 | data['data'].get('application/json', {}) | |||
|
117 | ) | |||
|
118 | ||||
|
119 | self.search_class.addItem('All Magics', 'any') | |||
|
120 | classes = set() | |||
|
121 | ||||
|
122 | for mtype in sorted(self.data): | |||
|
123 | subdict = self.data[mtype] | |||
|
124 | for name in sorted(subdict): | |||
|
125 | classes.add(subdict[name]) | |||
|
126 | ||||
|
127 | for cls in sorted(classes): | |||
|
128 | label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls) | |||
|
129 | self.search_class.addItem(label, cls) | |||
|
130 | ||||
|
131 | self.filter_magic_helper('.', 'any') | |||
|
132 | ||||
|
133 | def class_selected(self, index): | |||
|
134 | item = self.search_class.itemData(index) | |||
|
135 | regex = self.search_line.text() | |||
|
136 | self.filter_magic_helper(regex = regex, cls = item) | |||
|
137 | ||||
|
138 | def search_changed(self, search_string): | |||
|
139 | item = self.search_class.itemData( | |||
|
140 | self.search_class.currentIndex() | |||
|
141 | ) | |||
|
142 | self.filter_magic_helper(regex = search_string, cls = item) | |||
|
143 | ||||
|
144 | def _get_current_search_item(self, item = None): | |||
|
145 | text = None | |||
|
146 | if not isinstance(item, QtGui.QListWidgetItem): | |||
|
147 | item = self.search_list.currentItem() | |||
|
148 | text = item.text() | |||
|
149 | return text | |||
|
150 | ||||
|
151 | def paste_requested(self, item = None): | |||
|
152 | text = self._get_current_search_item(item) | |||
|
153 | if text != None: | |||
|
154 | self.pasteRequested.emit(text) | |||
|
155 | ||||
|
156 | def run_requested(self, item = None): | |||
|
157 | text = self._get_current_search_item(item) | |||
|
158 | if text != None: | |||
|
159 | self.runRequested.emit(text) | |||
|
160 | ||||
|
161 | def filter_magic_helper(self, regex, cls): | |||
|
162 | if regex == "" or regex == None: | |||
|
163 | regex = '.' | |||
|
164 | if cls == None: | |||
|
165 | cls = 'any' | |||
|
166 | ||||
|
167 | self.search_list.clear() | |||
|
168 | for mtype in sorted(self.data): | |||
|
169 | subdict = self.data[mtype] | |||
|
170 | prefix = magic_escapes[mtype] | |||
|
171 | ||||
|
172 | for name in sorted(subdict): | |||
|
173 | mclass = subdict[name] | |||
|
174 | pmagic = prefix + name | |||
|
175 | ||||
|
176 | if (re.match(regex, name) or re.match(regex, pmagic)) and \ | |||
|
177 | (cls == 'any' or cls == mclass): | |||
|
178 | self.search_list.addItem(pmagic) | |||
|
179 |
@@ -699,157 +699,39 b' class MainWindow(QtGui.QMainWindow):' | |||||
699 | self.add_menu_action(self.help_menu, self.onlineHelpAct) |
|
699 | self.add_menu_action(self.help_menu, self.onlineHelpAct) | |
700 |
|
700 | |||
701 | def init_magic_helper(self): |
|
701 | def init_magic_helper(self): | |
702 | self.magic_helper_data = None |
|
702 | from .magic_helper import MagicHelper | |
703 | self.magic_helper = QtGui.QDockWidget("Show Magics", self) |
|
703 | ||
|
704 | self.magic_helper = MagicHelper("Show Magics", self) | |||
|
705 | ||||
704 | self.magic_helper.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea | |
|
706 | self.magic_helper.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea | | |
705 | QtCore.Qt.RightDockWidgetArea) |
|
707 | QtCore.Qt.RightDockWidgetArea) | |
706 | self.magic_helper.setVisible(False) |
|
708 | self.magic_helper.setVisible(False) | |
707 |
|
709 | |||
708 | class MinListWidget(QtGui.QListWidget): |
|
710 | self.magic_helper.pasteRequested[str].connect( | |
709 | def sizeHint(self): |
|
|||
710 | s = QtCore.QSize() |
|
|||
711 | s.setHeight(super(MinListWidget,self).sizeHint().height()) |
|
|||
712 | s.setWidth(self.sizeHintForColumn(0)) |
|
|||
713 | return s |
|
|||
714 |
|
||||
715 | self.magic_helper_frame = QtGui.QFrame() |
|
|||
716 | self.magic_helper_searchl = QtGui.QLabel("Search:") |
|
|||
717 | self.magic_helper_search = QtGui.QLineEdit() |
|
|||
718 | self.magic_helper_class = QtGui.QComboBox() |
|
|||
719 | self.magic_helper_list = MinListWidget() |
|
|||
720 | self.magic_helper_paste = QtGui.QPushButton("Paste") |
|
|||
721 | self.magic_helper_run = QtGui.QPushButton("Run") |
|
|||
722 |
|
||||
723 | main_layout = QtGui.QVBoxLayout() |
|
|||
724 | search_layout = QtGui.QHBoxLayout() |
|
|||
725 | search_layout.addWidget(self.magic_helper_searchl) |
|
|||
726 | search_layout.addWidget(self.magic_helper_search, 10) |
|
|||
727 | main_layout.addLayout(search_layout) |
|
|||
728 | main_layout.addWidget(self.magic_helper_class) |
|
|||
729 | main_layout.addWidget(self.magic_helper_list, 10) |
|
|||
730 | action_layout = QtGui.QHBoxLayout() |
|
|||
731 | action_layout.addWidget(self.magic_helper_paste) |
|
|||
732 | action_layout.addWidget(self.magic_helper_run) |
|
|||
733 | main_layout.addLayout(action_layout) |
|
|||
734 |
|
||||
735 | self.magic_helper_frame.setLayout(main_layout) |
|
|||
736 | self.magic_helper.setWidget(self.magic_helper_frame) |
|
|||
737 |
|
||||
738 | self.magic_helper.visibilityChanged[bool].connect( |
|
|||
739 | self.update_magic_helper |
|
|||
740 | ) |
|
|||
741 | self.magic_helper_class.activated[int].connect( |
|
|||
742 | self.magic_helper_class_selected |
|
|||
743 | ) |
|
|||
744 | self.magic_helper_search.textChanged[str].connect( |
|
|||
745 | self.magic_helper_search_changed |
|
|||
746 | ) |
|
|||
747 | self.magic_helper_list.itemDoubleClicked[QtGui.QListWidgetItem].connect( |
|
|||
748 | self.magic_helper_paste_requested |
|
|||
749 | ) |
|
|||
750 | self.magic_helper_paste.clicked[bool].connect( |
|
|||
751 | self.magic_helper_paste_requested |
|
711 | self.magic_helper_paste_requested | |
752 | ) |
|
712 | ) | |
753 |
self.magic_helper |
|
713 | self.magic_helper.runRequested[str].connect( | |
754 | self.magic_helper_run_requested |
|
714 | self.magic_helper_run_requested | |
755 | ) |
|
715 | ) | |
756 |
|
716 | |||
757 | self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.magic_helper) |
|
717 | self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.magic_helper) | |
758 |
|
718 | |||
759 | def update_magic_helper(self, visible): |
|
|||
760 | if not visible or self.magic_helper_data != None: |
|
|||
761 | return |
|
|||
762 | self.magic_helper_data = {} |
|
|||
763 | self.magic_helper_class.clear() |
|
|||
764 | self.magic_helper_class.addItem("Populating...") |
|
|||
765 | self.active_frontend._silent_exec_callback( |
|
|||
766 | 'get_ipython().magic("lsmagic")', |
|
|||
767 | self.populate_magic_helper |
|
|||
768 | ) |
|
|||
769 |
|
||||
770 | def populate_magic_helper(self, data): |
|
|||
771 | if not data: |
|
|||
772 | return |
|
|||
773 |
|
||||
774 | if data['status'] != 'ok': |
|
|||
775 | self.log.warn("%%lsmagic user-expression failed: {}".format(data)) |
|
|||
776 | return |
|
|||
777 |
|
||||
778 | self.magic_helper_class.clear() |
|
|||
779 | self.magic_helper_list.clear() |
|
|||
780 |
|
||||
781 | self.magic_helper_data = json.loads( |
|
|||
782 | data['data'].get('application/json', {}) |
|
|||
783 | ) |
|
|||
784 |
|
||||
785 | self.magic_helper_class.addItem('All Magics', 'any') |
|
|||
786 | classes = set() |
|
|||
787 |
|
||||
788 | for mtype in sorted(self.magic_helper_data): |
|
|||
789 | subdict = self.magic_helper_data[mtype] |
|
|||
790 | for name in sorted(subdict): |
|
|||
791 | classes.add(subdict[name]) |
|
|||
792 |
|
||||
793 | for cls in sorted(classes): |
|
|||
794 | label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls) |
|
|||
795 | self.magic_helper_class.addItem(label, cls) |
|
|||
796 |
|
||||
797 | self.filter_magic_helper('.', 'any') |
|
|||
798 |
|
||||
799 | def magic_helper_class_selected(self, index): |
|
|||
800 | item = self.magic_helper_class.itemData(index) |
|
|||
801 | regex = self.magic_helper_search.text() |
|
|||
802 | self.filter_magic_helper(regex = regex, cls = item) |
|
|||
803 |
|
||||
804 | def magic_helper_search_changed(self, search_string): |
|
|||
805 | item = self.magic_helper_class.itemData( |
|
|||
806 | self.magic_helper_class.currentIndex() |
|
|||
807 | ) |
|
|||
808 | self.filter_magic_helper(regex = search_string, cls = item) |
|
|||
809 |
|
||||
810 | def _magic_helper_get_current(self, item = None): |
|
|||
811 | text = None |
|
|||
812 | if not isinstance(item, QtGui.QListWidgetItem): |
|
|||
813 | item = self.magic_helper_list.currentItem() |
|
|||
814 | text = item.text() |
|
|||
815 | return text |
|
|||
816 |
|
||||
817 | def _set_active_frontend_focus(self): |
|
719 | def _set_active_frontend_focus(self): | |
818 | # this is a hack, self.active_frontend._control seems to be |
|
720 | # this is a hack, self.active_frontend._control seems to be | |
819 | # a private member. Unfortunately this is the only method |
|
721 | # a private member. Unfortunately this is the only method | |
820 | # to set focus reliably |
|
722 | # to set focus reliably | |
821 | QtCore.QTimer.singleShot(200, self.active_frontend._control.setFocus) |
|
723 | QtCore.QTimer.singleShot(200, self.active_frontend._control.setFocus) | |
822 |
|
724 | |||
823 |
def magic_helper_paste_requested(self, |
|
725 | def magic_helper_paste_requested(self, text = None): | |
824 | text = self._magic_helper_get_current(item) |
|
|||
825 | if text != None: |
|
726 | if text != None: | |
826 | self.active_frontend.input_buffer = text |
|
727 | self.active_frontend.input_buffer = text | |
827 | self._set_active_frontend_focus() |
|
728 | self._set_active_frontend_focus() | |
828 |
|
729 | |||
829 |
def magic_helper_run_requested(self, |
|
730 | def magic_helper_run_requested(self, text = None): | |
830 | text = self._magic_helper_get_current(item) |
|
|||
831 | if text != None: |
|
731 | if text != None: | |
832 | self.active_frontend.execute(text) |
|
732 | self.active_frontend.execute(text) | |
833 | self._set_active_frontend_focus() |
|
733 | self._set_active_frontend_focus() | |
834 |
|
734 | |||
835 | def filter_magic_helper(self, regex, cls): |
|
|||
836 | if regex == "" or regex == None: |
|
|||
837 | regex = '.' |
|
|||
838 | if cls == None: |
|
|||
839 | cls = 'any' |
|
|||
840 |
|
||||
841 | self.magic_helper_list.clear() |
|
|||
842 | for mtype in sorted(self.magic_helper_data): |
|
|||
843 | subdict = self.magic_helper_data[mtype] |
|
|||
844 | prefix = magic_escapes[mtype] |
|
|||
845 |
|
||||
846 | for name in sorted(subdict): |
|
|||
847 | mclass = subdict[name] |
|
|||
848 | pmagic = prefix + name |
|
|||
849 |
|
||||
850 | if (re.match(regex, name) or re.match(regex, pmagic)) and \ |
|
|||
851 | (cls == 'any' or cls == mclass): |
|
|||
852 | self.magic_helper_list.addItem(pmagic) |
|
|||
853 |
|
735 | |||
854 |
|
736 | |||
855 | # minimize/maximize/fullscreen actions: |
|
737 | # minimize/maximize/fullscreen actions: |
General Comments 0
You need to be logged in to leave comments.
Login now