##// END OF EJS Templates
Updated file headers according to IPython Copyright Policy
Dimitry Kloper -
Show More
@@ -1,216 +1,218 b''
1 """MagicHelper - 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
8 """
7 """
9
8
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
11
10 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
11 # Imports
13 # Imports
12 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
13
15
14 # stdlib imports
16 # stdlib imports
15 import json
17 import json
16 import re
18 import re
17 import sys
19 import sys
18
20
19 # System library imports
21 # System library imports
20 from IPython.external.qt import QtGui,QtCore
22 from IPython.external.qt import QtGui,QtCore
21
23
22 from IPython.core.magic import magic_escapes
24 from IPython.core.magic import magic_escapes
23
25
24 class MagicHelper(QtGui.QDockWidget):
26 class MagicHelper(QtGui.QDockWidget):
25 """MagicHelper - dockable widget for convenient search and running of
27 """MagicHelper - dockable widget for convenient search and running of
26 magic command for IPython QtConsole.
28 magic command for IPython QtConsole.
27 """
29 """
28
30
29 #---------------------------------------------------------------------------
31 #---------------------------------------------------------------------------
30 # signals
32 # signals
31 #---------------------------------------------------------------------------
33 #---------------------------------------------------------------------------
32
34
33 pasteRequested = QtCore.pyqtSignal(str, name = 'pasteRequested')
35 pasteRequested = QtCore.pyqtSignal(str, name = 'pasteRequested')
34 """This signal is emitted when user wants to paste selected magic
36 """This signal is emitted when user wants to paste selected magic
35 command into the command line.
37 command into the command line.
36 """
38 """
37
39
38 runRequested = QtCore.pyqtSignal(str, name = 'runRequested')
40 runRequested = QtCore.pyqtSignal(str, name = 'runRequested')
39 """This signal is emitted when user wants to execute selected magic command
41 """This signal is emitted when user wants to execute selected magic command
40 """
42 """
41
43
42 readyForUpdate = QtCore.pyqtSignal(name = 'readyForUpdate')
44 readyForUpdate = QtCore.pyqtSignal(name = 'readyForUpdate')
43 """This signal is emitted when MagicHelper is ready to be populated.
45 """This signal is emitted when MagicHelper is ready to be populated.
44 Since kernel querying mechanisms are out of scope of this class,
46 Since kernel querying mechanisms are out of scope of this class,
45 it expects its owner to invoke MagicHelper.populate_magic_helper()
47 it expects its owner to invoke MagicHelper.populate_magic_helper()
46 as a reaction on this event.
48 as a reaction on this event.
47 """
49 """
48
50
49 #---------------------------------------------------------------------------
51 #---------------------------------------------------------------------------
50 # constructor
52 # constructor
51 #---------------------------------------------------------------------------
53 #---------------------------------------------------------------------------
52
54
53 def __init__(self, name, parent):
55 def __init__(self, name, parent):
54 super(MagicHelper, self).__init__(name, parent)
56 super(MagicHelper, self).__init__(name, parent)
55
57
56 self.data = None
58 self.data = None
57
59
58 class MinListWidget(QtGui.QListWidget):
60 class MinListWidget(QtGui.QListWidget):
59 """Temp class to overide the default QListWidget size hint
61 """Temp class to overide the default QListWidget size hint
60 in order to make MagicHelper narrow
62 in order to make MagicHelper narrow
61 """
63 """
62 def sizeHint(self):
64 def sizeHint(self):
63 s = QtCore.QSize()
65 s = QtCore.QSize()
64 s.setHeight(super(MinListWidget,self).sizeHint().height())
66 s.setHeight(super(MinListWidget,self).sizeHint().height())
65 s.setWidth(self.sizeHintForColumn(0))
67 s.setWidth(self.sizeHintForColumn(0))
66 return s
68 return s
67
69
68 # construct content
70 # construct content
69 self.frame = QtGui.QFrame()
71 self.frame = QtGui.QFrame()
70 self.search_label = QtGui.QLabel("Search:")
72 self.search_label = QtGui.QLabel("Search:")
71 self.search_line = QtGui.QLineEdit()
73 self.search_line = QtGui.QLineEdit()
72 self.search_class = QtGui.QComboBox()
74 self.search_class = QtGui.QComboBox()
73 self.search_list = MinListWidget()
75 self.search_list = MinListWidget()
74 self.paste_button = QtGui.QPushButton("Paste")
76 self.paste_button = QtGui.QPushButton("Paste")
75 self.run_button = QtGui.QPushButton("Run")
77 self.run_button = QtGui.QPushButton("Run")
76
78
77 # layout all the widgets
79 # layout all the widgets
78 main_layout = QtGui.QVBoxLayout()
80 main_layout = QtGui.QVBoxLayout()
79 search_layout = QtGui.QHBoxLayout()
81 search_layout = QtGui.QHBoxLayout()
80 search_layout.addWidget(self.search_label)
82 search_layout.addWidget(self.search_label)
81 search_layout.addWidget(self.search_line, 10)
83 search_layout.addWidget(self.search_line, 10)
82 main_layout.addLayout(search_layout)
84 main_layout.addLayout(search_layout)
83 main_layout.addWidget(self.search_class)
85 main_layout.addWidget(self.search_class)
84 main_layout.addWidget(self.search_list, 10)
86 main_layout.addWidget(self.search_list, 10)
85 action_layout = QtGui.QHBoxLayout()
87 action_layout = QtGui.QHBoxLayout()
86 action_layout.addWidget(self.paste_button)
88 action_layout.addWidget(self.paste_button)
87 action_layout.addWidget(self.run_button)
89 action_layout.addWidget(self.run_button)
88 main_layout.addLayout(action_layout)
90 main_layout.addLayout(action_layout)
89
91
90 self.frame.setLayout(main_layout)
92 self.frame.setLayout(main_layout)
91 self.setWidget(self.frame)
93 self.setWidget(self.frame)
92
94
93 # connect all the relevant signals to handlers
95 # connect all the relevant signals to handlers
94 self.visibilityChanged[bool].connect( self._update_magic_helper )
96 self.visibilityChanged[bool].connect( self._update_magic_helper )
95 self.search_class.activated[int].connect(
97 self.search_class.activated[int].connect(
96 self.class_selected
98 self.class_selected
97 )
99 )
98 self.search_line.textChanged[str].connect(
100 self.search_line.textChanged[str].connect(
99 self.search_changed
101 self.search_changed
100 )
102 )
101 self.search_list.itemDoubleClicked[QtGui.QListWidgetItem].connect(
103 self.search_list.itemDoubleClicked[QtGui.QListWidgetItem].connect(
102 self.paste_requested
104 self.paste_requested
103 )
105 )
104 self.paste_button.clicked[bool].connect(
106 self.paste_button.clicked[bool].connect(
105 self.paste_requested
107 self.paste_requested
106 )
108 )
107 self.run_button.clicked[bool].connect(
109 self.run_button.clicked[bool].connect(
108 self.run_requested
110 self.run_requested
109 )
111 )
110
112
111 #---------------------------------------------------------------------------
113 #---------------------------------------------------------------------------
112 # implementation
114 # implementation
113 #---------------------------------------------------------------------------
115 #---------------------------------------------------------------------------
114
116
115 def _update_magic_helper(self, visible):
117 def _update_magic_helper(self, visible):
116 """Start update sequence.
118 """Start update sequence.
117 This method is called when MagicHelper becomes visible. It clears
119 This method is called when MagicHelper becomes visible. It clears
118 the content and emits readyForUpdate signal. The owner of the
120 the content and emits readyForUpdate signal. The owner of the
119 instance is expected to invoke populate_magic_helper() when magic
121 instance is expected to invoke populate_magic_helper() when magic
120 info is available.
122 info is available.
121 """
123 """
122 if not visible or self.data != None:
124 if not visible or self.data != None:
123 return
125 return
124 self.data = {}
126 self.data = {}
125 self.search_class.clear()
127 self.search_class.clear()
126 self.search_class.addItem("Populating...")
128 self.search_class.addItem("Populating...")
127 self.search_list.clear()
129 self.search_list.clear()
128 self.readyForUpdate.emit()
130 self.readyForUpdate.emit()
129
131
130 def populate_magic_helper(self, data):
132 def populate_magic_helper(self, data):
131 """Expects data returned by lsmagics query from kernel.
133 """Expects data returned by lsmagics query from kernel.
132 Populates the search_class and search_list with relevant items.
134 Populates the search_class and search_list with relevant items.
133 """
135 """
134 self.search_class.clear()
136 self.search_class.clear()
135 self.search_list.clear()
137 self.search_list.clear()
136
138
137 self.data = json.loads(
139 self.data = json.loads(
138 data['data'].get('application/json', {})
140 data['data'].get('application/json', {})
139 )
141 )
140
142
141 self.search_class.addItem('All Magics', 'any')
143 self.search_class.addItem('All Magics', 'any')
142 classes = set()
144 classes = set()
143
145
144 for mtype in sorted(self.data):
146 for mtype in sorted(self.data):
145 subdict = self.data[mtype]
147 subdict = self.data[mtype]
146 for name in sorted(subdict):
148 for name in sorted(subdict):
147 classes.add(subdict[name])
149 classes.add(subdict[name])
148
150
149 for cls in sorted(classes):
151 for cls in sorted(classes):
150 label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls)
152 label = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>", cls)
151 self.search_class.addItem(label, cls)
153 self.search_class.addItem(label, cls)
152
154
153 self.filter_magic_helper('.', 'any')
155 self.filter_magic_helper('.', 'any')
154
156
155 def class_selected(self, index):
157 def class_selected(self, index):
156 """Handle search_class selection changes
158 """Handle search_class selection changes
157 """
159 """
158 item = self.search_class.itemData(index)
160 item = self.search_class.itemData(index)
159 regex = self.search_line.text()
161 regex = self.search_line.text()
160 self.filter_magic_helper(regex = regex, cls = item)
162 self.filter_magic_helper(regex = regex, cls = item)
161
163
162 def search_changed(self, search_string):
164 def search_changed(self, search_string):
163 """Handle search_line text changes.
165 """Handle search_line text changes.
164 The text is interpreted as a regular expression
166 The text is interpreted as a regular expression
165 """
167 """
166 item = self.search_class.itemData(
168 item = self.search_class.itemData(
167 self.search_class.currentIndex()
169 self.search_class.currentIndex()
168 )
170 )
169 self.filter_magic_helper(regex = search_string, cls = item)
171 self.filter_magic_helper(regex = search_string, cls = item)
170
172
171 def _get_current_search_item(self, item = None):
173 def _get_current_search_item(self, item = None):
172 """Retrieve magic command currently selected in the search_list
174 """Retrieve magic command currently selected in the search_list
173 """
175 """
174 text = None
176 text = None
175 if not isinstance(item, QtGui.QListWidgetItem):
177 if not isinstance(item, QtGui.QListWidgetItem):
176 item = self.search_list.currentItem()
178 item = self.search_list.currentItem()
177 text = item.text()
179 text = item.text()
178 return text
180 return text
179
181
180 def paste_requested(self, item = None):
182 def paste_requested(self, item = None):
181 """Emit pasteRequested signal with currently selected item text
183 """Emit pasteRequested signal with currently selected item text
182 """
184 """
183 text = self._get_current_search_item(item)
185 text = self._get_current_search_item(item)
184 if text != None:
186 if text != None:
185 self.pasteRequested.emit(text)
187 self.pasteRequested.emit(text)
186
188
187 def run_requested(self, item = None):
189 def run_requested(self, item = None):
188 """Emit runRequested signal with currently selected item text
190 """Emit runRequested signal with currently selected item text
189 """
191 """
190 text = self._get_current_search_item(item)
192 text = self._get_current_search_item(item)
191 if text != None:
193 if text != None:
192 self.runRequested.emit(text)
194 self.runRequested.emit(text)
193
195
194 def filter_magic_helper(self, regex, cls):
196 def filter_magic_helper(self, regex, cls):
195 """Update search_list with magic commands whose text match
197 """Update search_list with magic commands whose text match
196 regex and class match cls.
198 regex and class match cls.
197 If cls equals 'any' - any class matches.
199 If cls equals 'any' - any class matches.
198 """
200 """
199 if regex == "" or regex == None:
201 if regex == "" or regex == None:
200 regex = '.'
202 regex = '.'
201 if cls == None:
203 if cls == None:
202 cls = 'any'
204 cls = 'any'
203
205
204 self.search_list.clear()
206 self.search_list.clear()
205 for mtype in sorted(self.data):
207 for mtype in sorted(self.data):
206 subdict = self.data[mtype]
208 subdict = self.data[mtype]
207 prefix = magic_escapes[mtype]
209 prefix = magic_escapes[mtype]
208
210
209 for name in sorted(subdict):
211 for name in sorted(subdict):
210 mclass = subdict[name]
212 mclass = subdict[name]
211 pmagic = prefix + name
213 pmagic = prefix + name
212
214
213 if (re.match(regex, name) or re.match(regex, pmagic)) and \
215 if (re.match(regex, name) or re.match(regex, pmagic)) and \
214 (cls == 'any' or cls == mclass):
216 (cls == 'any' or cls == mclass):
215 self.search_list.addItem(pmagic)
217 self.search_list.addItem(pmagic)
216
218
@@ -1,934 +1,938 b''
1 """The Qt MainWindow for the QtConsole
1 """The Qt MainWindow for the QtConsole
2
2
3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
4 common actions.
4 common actions.
5
5
6 Authors:
6 Authors:
7
7
8 * Evan Patterson
8 * Evan Patterson
9 * Min RK
9 * Min RK
10 * Erik Tollerud
10 * Erik Tollerud
11 * Fernando Perez
11 * Fernando Perez
12 * Bussonnier Matthias
12 * Bussonnier Matthias
13 * Thomas Kluyver
13 * Thomas Kluyver
14 * Paul Ivanov
14 * Paul Ivanov
15
15
16 """
16 """
17
17
18 # Copyright (c) IPython Development Team.
19 # Distributed under the terms of the Modified BSD License.
20
21
18 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
19 # Imports
23 # Imports
20 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
21
25
22 # stdlib imports
26 # stdlib imports
23 import json
27 import json
24 import re
28 import re
25 import sys
29 import sys
26 import webbrowser
30 import webbrowser
27 from threading import Thread
31 from threading import Thread
28
32
29 # System library imports
33 # System library imports
30 from IPython.external.qt import QtGui,QtCore
34 from IPython.external.qt import QtGui,QtCore
31
35
32 from IPython.core.magic import magic_escapes
36 from IPython.core.magic import magic_escapes
33
37
34 def background(f):
38 def background(f):
35 """call a function in a simple thread, to prevent blocking"""
39 """call a function in a simple thread, to prevent blocking"""
36 t = Thread(target=f)
40 t = Thread(target=f)
37 t.start()
41 t.start()
38 return t
42 return t
39
43
40 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
41 # Classes
45 # Classes
42 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
43
47
44 class MainWindow(QtGui.QMainWindow):
48 class MainWindow(QtGui.QMainWindow):
45
49
46 #---------------------------------------------------------------------------
50 #---------------------------------------------------------------------------
47 # 'object' interface
51 # 'object' interface
48 #---------------------------------------------------------------------------
52 #---------------------------------------------------------------------------
49
53
50 def __init__(self, app,
54 def __init__(self, app,
51 confirm_exit=True,
55 confirm_exit=True,
52 new_frontend_factory=None, slave_frontend_factory=None,
56 new_frontend_factory=None, slave_frontend_factory=None,
53 ):
57 ):
54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
58 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
55
59
56 Parameters
60 Parameters
57 ----------
61 ----------
58
62
59 app : reference to QApplication parent
63 app : reference to QApplication parent
60 confirm_exit : bool, optional
64 confirm_exit : bool, optional
61 Whether we should prompt on close of tabs
65 Whether we should prompt on close of tabs
62 new_frontend_factory : callable
66 new_frontend_factory : callable
63 A callable that returns a new IPythonWidget instance, attached to
67 A callable that returns a new IPythonWidget instance, attached to
64 its own running kernel.
68 its own running kernel.
65 slave_frontend_factory : callable
69 slave_frontend_factory : callable
66 A callable that takes an existing IPythonWidget, and returns a new
70 A callable that takes an existing IPythonWidget, and returns a new
67 IPythonWidget instance, attached to the same kernel.
71 IPythonWidget instance, attached to the same kernel.
68 """
72 """
69
73
70 super(MainWindow, self).__init__()
74 super(MainWindow, self).__init__()
71 self._kernel_counter = 0
75 self._kernel_counter = 0
72 self._app = app
76 self._app = app
73 self.confirm_exit = confirm_exit
77 self.confirm_exit = confirm_exit
74 self.new_frontend_factory = new_frontend_factory
78 self.new_frontend_factory = new_frontend_factory
75 self.slave_frontend_factory = slave_frontend_factory
79 self.slave_frontend_factory = slave_frontend_factory
76
80
77 self.tab_widget = QtGui.QTabWidget(self)
81 self.tab_widget = QtGui.QTabWidget(self)
78 self.tab_widget.setDocumentMode(True)
82 self.tab_widget.setDocumentMode(True)
79 self.tab_widget.setTabsClosable(True)
83 self.tab_widget.setTabsClosable(True)
80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
84 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
81
85
82 self.setCentralWidget(self.tab_widget)
86 self.setCentralWidget(self.tab_widget)
83 # hide tab bar at first, since we have no tabs:
87 # hide tab bar at first, since we have no tabs:
84 self.tab_widget.tabBar().setVisible(False)
88 self.tab_widget.tabBar().setVisible(False)
85 # prevent focus in tab bar
89 # prevent focus in tab bar
86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
90 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
87
91
88 def update_tab_bar_visibility(self):
92 def update_tab_bar_visibility(self):
89 """ update visibility of the tabBar depending of the number of tab
93 """ update visibility of the tabBar depending of the number of tab
90
94
91 0 or 1 tab, tabBar hidden
95 0 or 1 tab, tabBar hidden
92 2+ tabs, tabBar visible
96 2+ tabs, tabBar visible
93
97
94 send a self.close if number of tab ==0
98 send a self.close if number of tab ==0
95
99
96 need to be called explicitly, or be connected to tabInserted/tabRemoved
100 need to be called explicitly, or be connected to tabInserted/tabRemoved
97 """
101 """
98 if self.tab_widget.count() <= 1:
102 if self.tab_widget.count() <= 1:
99 self.tab_widget.tabBar().setVisible(False)
103 self.tab_widget.tabBar().setVisible(False)
100 else:
104 else:
101 self.tab_widget.tabBar().setVisible(True)
105 self.tab_widget.tabBar().setVisible(True)
102 if self.tab_widget.count()==0 :
106 if self.tab_widget.count()==0 :
103 self.close()
107 self.close()
104
108
105 @property
109 @property
106 def next_kernel_id(self):
110 def next_kernel_id(self):
107 """constantly increasing counter for kernel IDs"""
111 """constantly increasing counter for kernel IDs"""
108 c = self._kernel_counter
112 c = self._kernel_counter
109 self._kernel_counter += 1
113 self._kernel_counter += 1
110 return c
114 return c
111
115
112 @property
116 @property
113 def active_frontend(self):
117 def active_frontend(self):
114 return self.tab_widget.currentWidget()
118 return self.tab_widget.currentWidget()
115
119
116 def create_tab_with_new_frontend(self):
120 def create_tab_with_new_frontend(self):
117 """create a new frontend and attach it to a new tab"""
121 """create a new frontend and attach it to a new tab"""
118 widget = self.new_frontend_factory()
122 widget = self.new_frontend_factory()
119 self.add_tab_with_frontend(widget)
123 self.add_tab_with_frontend(widget)
120
124
121 def create_tab_with_current_kernel(self):
125 def create_tab_with_current_kernel(self):
122 """create a new frontend attached to the same kernel as the current tab"""
126 """create a new frontend attached to the same kernel as the current tab"""
123 current_widget = self.tab_widget.currentWidget()
127 current_widget = self.tab_widget.currentWidget()
124 current_widget_index = self.tab_widget.indexOf(current_widget)
128 current_widget_index = self.tab_widget.indexOf(current_widget)
125 current_widget_name = self.tab_widget.tabText(current_widget_index)
129 current_widget_name = self.tab_widget.tabText(current_widget_index)
126 widget = self.slave_frontend_factory(current_widget)
130 widget = self.slave_frontend_factory(current_widget)
127 if 'slave' in current_widget_name:
131 if 'slave' in current_widget_name:
128 # don't keep stacking slaves
132 # don't keep stacking slaves
129 name = current_widget_name
133 name = current_widget_name
130 else:
134 else:
131 name = '(%s) slave' % current_widget_name
135 name = '(%s) slave' % current_widget_name
132 self.add_tab_with_frontend(widget,name=name)
136 self.add_tab_with_frontend(widget,name=name)
133
137
134 def close_tab(self,current_tab):
138 def close_tab(self,current_tab):
135 """ Called when you need to try to close a tab.
139 """ Called when you need to try to close a tab.
136
140
137 It takes the number of the tab to be closed as argument, or a reference
141 It takes the number of the tab to be closed as argument, or a reference
138 to the widget inside this tab
142 to the widget inside this tab
139 """
143 """
140
144
141 # let's be sure "tab" and "closing widget" are respectively the index
145 # let's be sure "tab" and "closing widget" are respectively the index
142 # of the tab to close and a reference to the frontend to close
146 # of the tab to close and a reference to the frontend to close
143 if type(current_tab) is not int :
147 if type(current_tab) is not int :
144 current_tab = self.tab_widget.indexOf(current_tab)
148 current_tab = self.tab_widget.indexOf(current_tab)
145 closing_widget=self.tab_widget.widget(current_tab)
149 closing_widget=self.tab_widget.widget(current_tab)
146
150
147
151
148 # when trying to be closed, widget might re-send a request to be
152 # when trying to be closed, widget might re-send a request to be
149 # closed again, but will be deleted when event will be processed. So
153 # closed again, but will be deleted when event will be processed. So
150 # need to check that widget still exists and skip if not. One example
154 # need to check that widget still exists and skip if not. One example
151 # of this is when 'exit' is sent in a slave tab. 'exit' will be
155 # of this is when 'exit' is sent in a slave tab. 'exit' will be
152 # re-sent by this function on the master widget, which ask all slave
156 # re-sent by this function on the master widget, which ask all slave
153 # widgets to exit
157 # widgets to exit
154 if closing_widget==None:
158 if closing_widget==None:
155 return
159 return
156
160
157 #get a list of all slave widgets on the same kernel.
161 #get a list of all slave widgets on the same kernel.
158 slave_tabs = self.find_slave_widgets(closing_widget)
162 slave_tabs = self.find_slave_widgets(closing_widget)
159
163
160 keepkernel = None #Use the prompt by default
164 keepkernel = None #Use the prompt by default
161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
165 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
162 keepkernel = closing_widget._keep_kernel_on_exit
166 keepkernel = closing_widget._keep_kernel_on_exit
163 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
167 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
164 # we set local slave tabs._hidden to True to avoid prompting for kernel
168 # we set local slave tabs._hidden to True to avoid prompting for kernel
165 # restart when they get the signal. and then "forward" the 'exit'
169 # restart when they get the signal. and then "forward" the 'exit'
166 # to the main window
170 # to the main window
167 if keepkernel is not None:
171 if keepkernel is not None:
168 for tab in slave_tabs:
172 for tab in slave_tabs:
169 tab._hidden = True
173 tab._hidden = True
170 if closing_widget in slave_tabs:
174 if closing_widget in slave_tabs:
171 try :
175 try :
172 self.find_master_tab(closing_widget).execute('exit')
176 self.find_master_tab(closing_widget).execute('exit')
173 except AttributeError:
177 except AttributeError:
174 self.log.info("Master already closed or not local, closing only current tab")
178 self.log.info("Master already closed or not local, closing only current tab")
175 self.tab_widget.removeTab(current_tab)
179 self.tab_widget.removeTab(current_tab)
176 self.update_tab_bar_visibility()
180 self.update_tab_bar_visibility()
177 return
181 return
178
182
179 kernel_client = closing_widget.kernel_client
183 kernel_client = closing_widget.kernel_client
180 kernel_manager = closing_widget.kernel_manager
184 kernel_manager = closing_widget.kernel_manager
181
185
182 if keepkernel is None and not closing_widget._confirm_exit:
186 if keepkernel is None and not closing_widget._confirm_exit:
183 # don't prompt, just terminate the kernel if we own it
187 # don't prompt, just terminate the kernel if we own it
184 # or leave it alone if we don't
188 # or leave it alone if we don't
185 keepkernel = closing_widget._existing
189 keepkernel = closing_widget._existing
186 if keepkernel is None: #show prompt
190 if keepkernel is None: #show prompt
187 if kernel_client and kernel_client.channels_running:
191 if kernel_client and kernel_client.channels_running:
188 title = self.window().windowTitle()
192 title = self.window().windowTitle()
189 cancel = QtGui.QMessageBox.Cancel
193 cancel = QtGui.QMessageBox.Cancel
190 okay = QtGui.QMessageBox.Ok
194 okay = QtGui.QMessageBox.Ok
191 if closing_widget._may_close:
195 if closing_widget._may_close:
192 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
196 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
193 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
197 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
194 justthis = QtGui.QPushButton("&No, just this Tab", self)
198 justthis = QtGui.QPushButton("&No, just this Tab", self)
195 justthis.setShortcut('N')
199 justthis.setShortcut('N')
196 closeall = QtGui.QPushButton("&Yes, close all", self)
200 closeall = QtGui.QPushButton("&Yes, close all", self)
197 closeall.setShortcut('Y')
201 closeall.setShortcut('Y')
198 # allow ctrl-d ctrl-d exit, like in terminal
202 # allow ctrl-d ctrl-d exit, like in terminal
199 closeall.setShortcut('Ctrl+D')
203 closeall.setShortcut('Ctrl+D')
200 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
204 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
201 title, msg)
205 title, msg)
202 box.setInformativeText(info)
206 box.setInformativeText(info)
203 box.addButton(cancel)
207 box.addButton(cancel)
204 box.addButton(justthis, QtGui.QMessageBox.NoRole)
208 box.addButton(justthis, QtGui.QMessageBox.NoRole)
205 box.addButton(closeall, QtGui.QMessageBox.YesRole)
209 box.addButton(closeall, QtGui.QMessageBox.YesRole)
206 box.setDefaultButton(closeall)
210 box.setDefaultButton(closeall)
207 box.setEscapeButton(cancel)
211 box.setEscapeButton(cancel)
208 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
212 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
209 box.setIconPixmap(pixmap)
213 box.setIconPixmap(pixmap)
210 reply = box.exec_()
214 reply = box.exec_()
211 if reply == 1: # close All
215 if reply == 1: # close All
212 for slave in slave_tabs:
216 for slave in slave_tabs:
213 background(slave.kernel_client.stop_channels)
217 background(slave.kernel_client.stop_channels)
214 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
218 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
215 closing_widget.execute("exit")
219 closing_widget.execute("exit")
216 self.tab_widget.removeTab(current_tab)
220 self.tab_widget.removeTab(current_tab)
217 background(kernel_client.stop_channels)
221 background(kernel_client.stop_channels)
218 elif reply == 0: # close Console
222 elif reply == 0: # close Console
219 if not closing_widget._existing:
223 if not closing_widget._existing:
220 # Have kernel: don't quit, just close the tab
224 # Have kernel: don't quit, just close the tab
221 closing_widget.execute("exit True")
225 closing_widget.execute("exit True")
222 self.tab_widget.removeTab(current_tab)
226 self.tab_widget.removeTab(current_tab)
223 background(kernel_client.stop_channels)
227 background(kernel_client.stop_channels)
224 else:
228 else:
225 reply = QtGui.QMessageBox.question(self, title,
229 reply = QtGui.QMessageBox.question(self, title,
226 "Are you sure you want to close this Console?"+
230 "Are you sure you want to close this Console?"+
227 "\nThe Kernel and other Consoles will remain active.",
231 "\nThe Kernel and other Consoles will remain active.",
228 okay|cancel,
232 okay|cancel,
229 defaultButton=okay
233 defaultButton=okay
230 )
234 )
231 if reply == okay:
235 if reply == okay:
232 self.tab_widget.removeTab(current_tab)
236 self.tab_widget.removeTab(current_tab)
233 elif keepkernel: #close console but leave kernel running (no prompt)
237 elif keepkernel: #close console but leave kernel running (no prompt)
234 self.tab_widget.removeTab(current_tab)
238 self.tab_widget.removeTab(current_tab)
235 background(kernel_client.stop_channels)
239 background(kernel_client.stop_channels)
236 else: #close console and kernel (no prompt)
240 else: #close console and kernel (no prompt)
237 self.tab_widget.removeTab(current_tab)
241 self.tab_widget.removeTab(current_tab)
238 if kernel_client and kernel_client.channels_running:
242 if kernel_client and kernel_client.channels_running:
239 for slave in slave_tabs:
243 for slave in slave_tabs:
240 background(slave.kernel_client.stop_channels)
244 background(slave.kernel_client.stop_channels)
241 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
245 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
242 if kernel_manager:
246 if kernel_manager:
243 kernel_manager.shutdown_kernel()
247 kernel_manager.shutdown_kernel()
244 background(kernel_client.stop_channels)
248 background(kernel_client.stop_channels)
245
249
246 self.update_tab_bar_visibility()
250 self.update_tab_bar_visibility()
247
251
248 def add_tab_with_frontend(self,frontend,name=None):
252 def add_tab_with_frontend(self,frontend,name=None):
249 """ insert a tab with a given frontend in the tab bar, and give it a name
253 """ insert a tab with a given frontend in the tab bar, and give it a name
250
254
251 """
255 """
252 if not name:
256 if not name:
253 name = 'kernel %i' % self.next_kernel_id
257 name = 'kernel %i' % self.next_kernel_id
254 self.tab_widget.addTab(frontend,name)
258 self.tab_widget.addTab(frontend,name)
255 self.update_tab_bar_visibility()
259 self.update_tab_bar_visibility()
256 self.make_frontend_visible(frontend)
260 self.make_frontend_visible(frontend)
257 frontend.exit_requested.connect(self.close_tab)
261 frontend.exit_requested.connect(self.close_tab)
258
262
259 def next_tab(self):
263 def next_tab(self):
260 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
264 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
261
265
262 def prev_tab(self):
266 def prev_tab(self):
263 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
267 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
264
268
265 def make_frontend_visible(self,frontend):
269 def make_frontend_visible(self,frontend):
266 widget_index=self.tab_widget.indexOf(frontend)
270 widget_index=self.tab_widget.indexOf(frontend)
267 if widget_index > 0 :
271 if widget_index > 0 :
268 self.tab_widget.setCurrentIndex(widget_index)
272 self.tab_widget.setCurrentIndex(widget_index)
269
273
270 def find_master_tab(self,tab,as_list=False):
274 def find_master_tab(self,tab,as_list=False):
271 """
275 """
272 Try to return the frontend that owns the kernel attached to the given widget/tab.
276 Try to return the frontend that owns the kernel attached to the given widget/tab.
273
277
274 Only finds frontend owned by the current application. Selection
278 Only finds frontend owned by the current application. Selection
275 based on port of the kernel might be inaccurate if several kernel
279 based on port of the kernel might be inaccurate if several kernel
276 on different ip use same port number.
280 on different ip use same port number.
277
281
278 This function does the conversion tabNumber/widget if needed.
282 This function does the conversion tabNumber/widget if needed.
279 Might return None if no master widget (non local kernel)
283 Might return None if no master widget (non local kernel)
280 Will crash IPython if more than 1 masterWidget
284 Will crash IPython if more than 1 masterWidget
281
285
282 When asList set to True, always return a list of widget(s) owning
286 When asList set to True, always return a list of widget(s) owning
283 the kernel. The list might be empty or containing several Widget.
287 the kernel. The list might be empty or containing several Widget.
284 """
288 """
285
289
286 #convert from/to int/richIpythonWidget if needed
290 #convert from/to int/richIpythonWidget if needed
287 if isinstance(tab, int):
291 if isinstance(tab, int):
288 tab = self.tab_widget.widget(tab)
292 tab = self.tab_widget.widget(tab)
289 km=tab.kernel_client
293 km=tab.kernel_client
290
294
291 #build list of all widgets
295 #build list of all widgets
292 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
296 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
293
297
294 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
298 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
295 # And should have a _may_close attribute
299 # And should have a _may_close attribute
296 filtered_widget_list = [ widget for widget in widget_list if
300 filtered_widget_list = [ widget for widget in widget_list if
297 widget.kernel_client.connection_file == km.connection_file and
301 widget.kernel_client.connection_file == km.connection_file and
298 hasattr(widget,'_may_close') ]
302 hasattr(widget,'_may_close') ]
299 # the master widget is the one that may close the kernel
303 # the master widget is the one that may close the kernel
300 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
304 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
301 if as_list:
305 if as_list:
302 return master_widget
306 return master_widget
303 assert(len(master_widget)<=1 )
307 assert(len(master_widget)<=1 )
304 if len(master_widget)==0:
308 if len(master_widget)==0:
305 return None
309 return None
306
310
307 return master_widget[0]
311 return master_widget[0]
308
312
309 def find_slave_widgets(self,tab):
313 def find_slave_widgets(self,tab):
310 """return all the frontends that do not own the kernel attached to the given widget/tab.
314 """return all the frontends that do not own the kernel attached to the given widget/tab.
311
315
312 Only find frontends owned by the current application. Selection
316 Only find frontends owned by the current application. Selection
313 based on connection file of the kernel.
317 based on connection file of the kernel.
314
318
315 This function does the conversion tabNumber/widget if needed.
319 This function does the conversion tabNumber/widget if needed.
316 """
320 """
317 #convert from/to int/richIpythonWidget if needed
321 #convert from/to int/richIpythonWidget if needed
318 if isinstance(tab, int):
322 if isinstance(tab, int):
319 tab = self.tab_widget.widget(tab)
323 tab = self.tab_widget.widget(tab)
320 km=tab.kernel_client
324 km=tab.kernel_client
321
325
322 #build list of all widgets
326 #build list of all widgets
323 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
327 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
324
328
325 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
329 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
326 filtered_widget_list = ( widget for widget in widget_list if
330 filtered_widget_list = ( widget for widget in widget_list if
327 widget.kernel_client.connection_file == km.connection_file)
331 widget.kernel_client.connection_file == km.connection_file)
328 # Get a list of all widget owning the same kernel and removed it from
332 # Get a list of all widget owning the same kernel and removed it from
329 # the previous cadidate. (better using sets ?)
333 # the previous cadidate. (better using sets ?)
330 master_widget_list = self.find_master_tab(tab, as_list=True)
334 master_widget_list = self.find_master_tab(tab, as_list=True)
331 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
335 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
332
336
333 return slave_list
337 return slave_list
334
338
335 # Populate the menu bar with common actions and shortcuts
339 # Populate the menu bar with common actions and shortcuts
336 def add_menu_action(self, menu, action, defer_shortcut=False):
340 def add_menu_action(self, menu, action, defer_shortcut=False):
337 """Add action to menu as well as self
341 """Add action to menu as well as self
338
342
339 So that when the menu bar is invisible, its actions are still available.
343 So that when the menu bar is invisible, its actions are still available.
340
344
341 If defer_shortcut is True, set the shortcut context to widget-only,
345 If defer_shortcut is True, set the shortcut context to widget-only,
342 where it will avoid conflict with shortcuts already bound to the
346 where it will avoid conflict with shortcuts already bound to the
343 widgets themselves.
347 widgets themselves.
344 """
348 """
345 menu.addAction(action)
349 menu.addAction(action)
346 self.addAction(action)
350 self.addAction(action)
347
351
348 if defer_shortcut:
352 if defer_shortcut:
349 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
353 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
350
354
351 def init_menu_bar(self):
355 def init_menu_bar(self):
352 #create menu in the order they should appear in the menu bar
356 #create menu in the order they should appear in the menu bar
353 self.init_file_menu()
357 self.init_file_menu()
354 self.init_edit_menu()
358 self.init_edit_menu()
355 self.init_view_menu()
359 self.init_view_menu()
356 self.init_kernel_menu()
360 self.init_kernel_menu()
357 self.init_magic_menu()
361 self.init_magic_menu()
358 self.init_window_menu()
362 self.init_window_menu()
359 self.init_help_menu()
363 self.init_help_menu()
360
364
361 def init_file_menu(self):
365 def init_file_menu(self):
362 self.file_menu = self.menuBar().addMenu("&File")
366 self.file_menu = self.menuBar().addMenu("&File")
363
367
364 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
368 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
365 self,
369 self,
366 shortcut="Ctrl+T",
370 shortcut="Ctrl+T",
367 triggered=self.create_tab_with_new_frontend)
371 triggered=self.create_tab_with_new_frontend)
368 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
372 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
369
373
370 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
374 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
371 self,
375 self,
372 shortcut="Ctrl+Shift+T",
376 shortcut="Ctrl+Shift+T",
373 triggered=self.create_tab_with_current_kernel)
377 triggered=self.create_tab_with_current_kernel)
374 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
378 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
375
379
376 self.file_menu.addSeparator()
380 self.file_menu.addSeparator()
377
381
378 self.close_action=QtGui.QAction("&Close Tab",
382 self.close_action=QtGui.QAction("&Close Tab",
379 self,
383 self,
380 shortcut=QtGui.QKeySequence.Close,
384 shortcut=QtGui.QKeySequence.Close,
381 triggered=self.close_active_frontend
385 triggered=self.close_active_frontend
382 )
386 )
383 self.add_menu_action(self.file_menu, self.close_action)
387 self.add_menu_action(self.file_menu, self.close_action)
384
388
385 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
389 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
386 self,
390 self,
387 shortcut=QtGui.QKeySequence.Save,
391 shortcut=QtGui.QKeySequence.Save,
388 triggered=self.export_action_active_frontend
392 triggered=self.export_action_active_frontend
389 )
393 )
390 self.add_menu_action(self.file_menu, self.export_action, True)
394 self.add_menu_action(self.file_menu, self.export_action, True)
391
395
392 self.file_menu.addSeparator()
396 self.file_menu.addSeparator()
393
397
394 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
398 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
395 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
399 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
396 # Only override the default if there is a collision.
400 # Only override the default if there is a collision.
397 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
401 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
398 printkey = "Ctrl+Shift+P"
402 printkey = "Ctrl+Shift+P"
399 self.print_action = QtGui.QAction("&Print",
403 self.print_action = QtGui.QAction("&Print",
400 self,
404 self,
401 shortcut=printkey,
405 shortcut=printkey,
402 triggered=self.print_action_active_frontend)
406 triggered=self.print_action_active_frontend)
403 self.add_menu_action(self.file_menu, self.print_action, True)
407 self.add_menu_action(self.file_menu, self.print_action, True)
404
408
405 if sys.platform != 'darwin':
409 if sys.platform != 'darwin':
406 # OSX always has Quit in the Application menu, only add it
410 # OSX always has Quit in the Application menu, only add it
407 # to the File menu elsewhere.
411 # to the File menu elsewhere.
408
412
409 self.file_menu.addSeparator()
413 self.file_menu.addSeparator()
410
414
411 self.quit_action = QtGui.QAction("&Quit",
415 self.quit_action = QtGui.QAction("&Quit",
412 self,
416 self,
413 shortcut=QtGui.QKeySequence.Quit,
417 shortcut=QtGui.QKeySequence.Quit,
414 triggered=self.close,
418 triggered=self.close,
415 )
419 )
416 self.add_menu_action(self.file_menu, self.quit_action)
420 self.add_menu_action(self.file_menu, self.quit_action)
417
421
418
422
419 def init_edit_menu(self):
423 def init_edit_menu(self):
420 self.edit_menu = self.menuBar().addMenu("&Edit")
424 self.edit_menu = self.menuBar().addMenu("&Edit")
421
425
422 self.undo_action = QtGui.QAction("&Undo",
426 self.undo_action = QtGui.QAction("&Undo",
423 self,
427 self,
424 shortcut=QtGui.QKeySequence.Undo,
428 shortcut=QtGui.QKeySequence.Undo,
425 statusTip="Undo last action if possible",
429 statusTip="Undo last action if possible",
426 triggered=self.undo_active_frontend
430 triggered=self.undo_active_frontend
427 )
431 )
428 self.add_menu_action(self.edit_menu, self.undo_action)
432 self.add_menu_action(self.edit_menu, self.undo_action)
429
433
430 self.redo_action = QtGui.QAction("&Redo",
434 self.redo_action = QtGui.QAction("&Redo",
431 self,
435 self,
432 shortcut=QtGui.QKeySequence.Redo,
436 shortcut=QtGui.QKeySequence.Redo,
433 statusTip="Redo last action if possible",
437 statusTip="Redo last action if possible",
434 triggered=self.redo_active_frontend)
438 triggered=self.redo_active_frontend)
435 self.add_menu_action(self.edit_menu, self.redo_action)
439 self.add_menu_action(self.edit_menu, self.redo_action)
436
440
437 self.edit_menu.addSeparator()
441 self.edit_menu.addSeparator()
438
442
439 self.cut_action = QtGui.QAction("&Cut",
443 self.cut_action = QtGui.QAction("&Cut",
440 self,
444 self,
441 shortcut=QtGui.QKeySequence.Cut,
445 shortcut=QtGui.QKeySequence.Cut,
442 triggered=self.cut_active_frontend
446 triggered=self.cut_active_frontend
443 )
447 )
444 self.add_menu_action(self.edit_menu, self.cut_action, True)
448 self.add_menu_action(self.edit_menu, self.cut_action, True)
445
449
446 self.copy_action = QtGui.QAction("&Copy",
450 self.copy_action = QtGui.QAction("&Copy",
447 self,
451 self,
448 shortcut=QtGui.QKeySequence.Copy,
452 shortcut=QtGui.QKeySequence.Copy,
449 triggered=self.copy_active_frontend
453 triggered=self.copy_active_frontend
450 )
454 )
451 self.add_menu_action(self.edit_menu, self.copy_action, True)
455 self.add_menu_action(self.edit_menu, self.copy_action, True)
452
456
453 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
457 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
454 self,
458 self,
455 shortcut="Ctrl+Shift+C",
459 shortcut="Ctrl+Shift+C",
456 triggered=self.copy_raw_active_frontend
460 triggered=self.copy_raw_active_frontend
457 )
461 )
458 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
462 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
459
463
460 self.paste_action = QtGui.QAction("&Paste",
464 self.paste_action = QtGui.QAction("&Paste",
461 self,
465 self,
462 shortcut=QtGui.QKeySequence.Paste,
466 shortcut=QtGui.QKeySequence.Paste,
463 triggered=self.paste_active_frontend
467 triggered=self.paste_active_frontend
464 )
468 )
465 self.add_menu_action(self.edit_menu, self.paste_action, True)
469 self.add_menu_action(self.edit_menu, self.paste_action, True)
466
470
467 self.edit_menu.addSeparator()
471 self.edit_menu.addSeparator()
468
472
469 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
473 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
470 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
474 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
471 # Only override the default if there is a collision.
475 # Only override the default if there is a collision.
472 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
476 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
473 selectall = "Ctrl+Shift+A"
477 selectall = "Ctrl+Shift+A"
474 self.select_all_action = QtGui.QAction("Select &All",
478 self.select_all_action = QtGui.QAction("Select &All",
475 self,
479 self,
476 shortcut=selectall,
480 shortcut=selectall,
477 triggered=self.select_all_active_frontend
481 triggered=self.select_all_active_frontend
478 )
482 )
479 self.add_menu_action(self.edit_menu, self.select_all_action, True)
483 self.add_menu_action(self.edit_menu, self.select_all_action, True)
480
484
481
485
482 def init_view_menu(self):
486 def init_view_menu(self):
483 self.view_menu = self.menuBar().addMenu("&View")
487 self.view_menu = self.menuBar().addMenu("&View")
484
488
485 if sys.platform != 'darwin':
489 if sys.platform != 'darwin':
486 # disable on OSX, where there is always a menu bar
490 # disable on OSX, where there is always a menu bar
487 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
491 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
488 self,
492 self,
489 shortcut="Ctrl+Shift+M",
493 shortcut="Ctrl+Shift+M",
490 statusTip="Toggle visibility of menubar",
494 statusTip="Toggle visibility of menubar",
491 triggered=self.toggle_menu_bar)
495 triggered=self.toggle_menu_bar)
492 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
496 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
493
497
494 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
498 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
495 self.full_screen_act = QtGui.QAction("&Full Screen",
499 self.full_screen_act = QtGui.QAction("&Full Screen",
496 self,
500 self,
497 shortcut=fs_key,
501 shortcut=fs_key,
498 statusTip="Toggle between Fullscreen and Normal Size",
502 statusTip="Toggle between Fullscreen and Normal Size",
499 triggered=self.toggleFullScreen)
503 triggered=self.toggleFullScreen)
500 self.add_menu_action(self.view_menu, self.full_screen_act)
504 self.add_menu_action(self.view_menu, self.full_screen_act)
501
505
502 self.view_menu.addSeparator()
506 self.view_menu.addSeparator()
503
507
504 self.increase_font_size = QtGui.QAction("Zoom &In",
508 self.increase_font_size = QtGui.QAction("Zoom &In",
505 self,
509 self,
506 shortcut=QtGui.QKeySequence.ZoomIn,
510 shortcut=QtGui.QKeySequence.ZoomIn,
507 triggered=self.increase_font_size_active_frontend
511 triggered=self.increase_font_size_active_frontend
508 )
512 )
509 self.add_menu_action(self.view_menu, self.increase_font_size, True)
513 self.add_menu_action(self.view_menu, self.increase_font_size, True)
510
514
511 self.decrease_font_size = QtGui.QAction("Zoom &Out",
515 self.decrease_font_size = QtGui.QAction("Zoom &Out",
512 self,
516 self,
513 shortcut=QtGui.QKeySequence.ZoomOut,
517 shortcut=QtGui.QKeySequence.ZoomOut,
514 triggered=self.decrease_font_size_active_frontend
518 triggered=self.decrease_font_size_active_frontend
515 )
519 )
516 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
520 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
517
521
518 self.reset_font_size = QtGui.QAction("Zoom &Reset",
522 self.reset_font_size = QtGui.QAction("Zoom &Reset",
519 self,
523 self,
520 shortcut="Ctrl+0",
524 shortcut="Ctrl+0",
521 triggered=self.reset_font_size_active_frontend
525 triggered=self.reset_font_size_active_frontend
522 )
526 )
523 self.add_menu_action(self.view_menu, self.reset_font_size, True)
527 self.add_menu_action(self.view_menu, self.reset_font_size, True)
524
528
525 self.view_menu.addSeparator()
529 self.view_menu.addSeparator()
526
530
527 self.clear_action = QtGui.QAction("&Clear Screen",
531 self.clear_action = QtGui.QAction("&Clear Screen",
528 self,
532 self,
529 shortcut='Ctrl+L',
533 shortcut='Ctrl+L',
530 statusTip="Clear the console",
534 statusTip="Clear the console",
531 triggered=self.clear_magic_active_frontend)
535 triggered=self.clear_magic_active_frontend)
532 self.add_menu_action(self.view_menu, self.clear_action)
536 self.add_menu_action(self.view_menu, self.clear_action)
533
537
534 self.pager_menu = self.view_menu.addMenu("&Pager")
538 self.pager_menu = self.view_menu.addMenu("&Pager")
535
539
536 hsplit_action = QtGui.QAction(".. &Horizontal Split",
540 hsplit_action = QtGui.QAction(".. &Horizontal Split",
537 self,
541 self,
538 triggered=lambda: self.set_paging_active_frontend('hsplit'))
542 triggered=lambda: self.set_paging_active_frontend('hsplit'))
539
543
540 vsplit_action = QtGui.QAction(" : &Vertical Split",
544 vsplit_action = QtGui.QAction(" : &Vertical Split",
541 self,
545 self,
542 triggered=lambda: self.set_paging_active_frontend('vsplit'))
546 triggered=lambda: self.set_paging_active_frontend('vsplit'))
543
547
544 inside_action = QtGui.QAction(" &Inside Pager",
548 inside_action = QtGui.QAction(" &Inside Pager",
545 self,
549 self,
546 triggered=lambda: self.set_paging_active_frontend('inside'))
550 triggered=lambda: self.set_paging_active_frontend('inside'))
547
551
548 self.pager_menu.addAction(hsplit_action)
552 self.pager_menu.addAction(hsplit_action)
549 self.pager_menu.addAction(vsplit_action)
553 self.pager_menu.addAction(vsplit_action)
550 self.pager_menu.addAction(inside_action)
554 self.pager_menu.addAction(inside_action)
551
555
552 def init_kernel_menu(self):
556 def init_kernel_menu(self):
553 self.kernel_menu = self.menuBar().addMenu("&Kernel")
557 self.kernel_menu = self.menuBar().addMenu("&Kernel")
554 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
558 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
555 # keep the signal shortcuts to ctrl, rather than
559 # keep the signal shortcuts to ctrl, rather than
556 # platform-default like we do elsewhere.
560 # platform-default like we do elsewhere.
557
561
558 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
562 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
559
563
560 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
564 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
561 self,
565 self,
562 triggered=self.interrupt_kernel_active_frontend,
566 triggered=self.interrupt_kernel_active_frontend,
563 shortcut=ctrl+"+C",
567 shortcut=ctrl+"+C",
564 )
568 )
565 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
569 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
566
570
567 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
571 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
568 self,
572 self,
569 triggered=self.restart_kernel_active_frontend,
573 triggered=self.restart_kernel_active_frontend,
570 shortcut=ctrl+"+.",
574 shortcut=ctrl+"+.",
571 )
575 )
572 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
576 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
573
577
574 self.kernel_menu.addSeparator()
578 self.kernel_menu.addSeparator()
575
579
576 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
580 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
577 self,
581 self,
578 checkable=True,
582 checkable=True,
579 checked=self.active_frontend.confirm_restart,
583 checked=self.active_frontend.confirm_restart,
580 triggered=self.toggle_confirm_restart_active_frontend
584 triggered=self.toggle_confirm_restart_active_frontend
581 )
585 )
582
586
583 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
587 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
584 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
588 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
585
589
586 def init_magic_menu(self):
590 def init_magic_menu(self):
587 self.magic_menu = self.menuBar().addMenu("&Magic")
591 self.magic_menu = self.menuBar().addMenu("&Magic")
588
592
589 self.add_menu_action(self.magic_menu,
593 self.add_menu_action(self.magic_menu,
590 self.magic_helper.toggleViewAction())
594 self.magic_helper.toggleViewAction())
591
595
592 self.magic_menu_separator = self.magic_menu.addSeparator()
596 self.magic_menu_separator = self.magic_menu.addSeparator()
593
597
594 self.reset_action = QtGui.QAction("&Reset",
598 self.reset_action = QtGui.QAction("&Reset",
595 self,
599 self,
596 statusTip="Clear all variables from workspace",
600 statusTip="Clear all variables from workspace",
597 triggered=self.reset_magic_active_frontend)
601 triggered=self.reset_magic_active_frontend)
598 self.add_menu_action(self.magic_menu, self.reset_action)
602 self.add_menu_action(self.magic_menu, self.reset_action)
599
603
600 self.history_action = QtGui.QAction("&History",
604 self.history_action = QtGui.QAction("&History",
601 self,
605 self,
602 statusTip="show command history",
606 statusTip="show command history",
603 triggered=self.history_magic_active_frontend)
607 triggered=self.history_magic_active_frontend)
604 self.add_menu_action(self.magic_menu, self.history_action)
608 self.add_menu_action(self.magic_menu, self.history_action)
605
609
606 self.save_action = QtGui.QAction("E&xport History ",
610 self.save_action = QtGui.QAction("E&xport History ",
607 self,
611 self,
608 statusTip="Export History as Python File",
612 statusTip="Export History as Python File",
609 triggered=self.save_magic_active_frontend)
613 triggered=self.save_magic_active_frontend)
610 self.add_menu_action(self.magic_menu, self.save_action)
614 self.add_menu_action(self.magic_menu, self.save_action)
611
615
612 self.who_action = QtGui.QAction("&Who",
616 self.who_action = QtGui.QAction("&Who",
613 self,
617 self,
614 statusTip="List interactive variables",
618 statusTip="List interactive variables",
615 triggered=self.who_magic_active_frontend)
619 triggered=self.who_magic_active_frontend)
616 self.add_menu_action(self.magic_menu, self.who_action)
620 self.add_menu_action(self.magic_menu, self.who_action)
617
621
618 self.who_ls_action = QtGui.QAction("Wh&o ls",
622 self.who_ls_action = QtGui.QAction("Wh&o ls",
619 self,
623 self,
620 statusTip="Return a list of interactive variables",
624 statusTip="Return a list of interactive variables",
621 triggered=self.who_ls_magic_active_frontend)
625 triggered=self.who_ls_magic_active_frontend)
622 self.add_menu_action(self.magic_menu, self.who_ls_action)
626 self.add_menu_action(self.magic_menu, self.who_ls_action)
623
627
624 self.whos_action = QtGui.QAction("Who&s",
628 self.whos_action = QtGui.QAction("Who&s",
625 self,
629 self,
626 statusTip="List interactive variables with details",
630 statusTip="List interactive variables with details",
627 triggered=self.whos_magic_active_frontend)
631 triggered=self.whos_magic_active_frontend)
628 self.add_menu_action(self.magic_menu, self.whos_action)
632 self.add_menu_action(self.magic_menu, self.whos_action)
629
633
630 def init_window_menu(self):
634 def init_window_menu(self):
631 self.window_menu = self.menuBar().addMenu("&Window")
635 self.window_menu = self.menuBar().addMenu("&Window")
632 if sys.platform == 'darwin':
636 if sys.platform == 'darwin':
633 # add min/maximize actions to OSX, which lacks default bindings.
637 # add min/maximize actions to OSX, which lacks default bindings.
634 self.minimizeAct = QtGui.QAction("Mini&mize",
638 self.minimizeAct = QtGui.QAction("Mini&mize",
635 self,
639 self,
636 shortcut="Ctrl+m",
640 shortcut="Ctrl+m",
637 statusTip="Minimize the window/Restore Normal Size",
641 statusTip="Minimize the window/Restore Normal Size",
638 triggered=self.toggleMinimized)
642 triggered=self.toggleMinimized)
639 # maximize is called 'Zoom' on OSX for some reason
643 # maximize is called 'Zoom' on OSX for some reason
640 self.maximizeAct = QtGui.QAction("&Zoom",
644 self.maximizeAct = QtGui.QAction("&Zoom",
641 self,
645 self,
642 shortcut="Ctrl+Shift+M",
646 shortcut="Ctrl+Shift+M",
643 statusTip="Maximize the window/Restore Normal Size",
647 statusTip="Maximize the window/Restore Normal Size",
644 triggered=self.toggleMaximized)
648 triggered=self.toggleMaximized)
645
649
646 self.add_menu_action(self.window_menu, self.minimizeAct)
650 self.add_menu_action(self.window_menu, self.minimizeAct)
647 self.add_menu_action(self.window_menu, self.maximizeAct)
651 self.add_menu_action(self.window_menu, self.maximizeAct)
648 self.window_menu.addSeparator()
652 self.window_menu.addSeparator()
649
653
650 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
654 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
651 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
655 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
652 self,
656 self,
653 shortcut=prev_key,
657 shortcut=prev_key,
654 statusTip="Select previous tab",
658 statusTip="Select previous tab",
655 triggered=self.prev_tab)
659 triggered=self.prev_tab)
656 self.add_menu_action(self.window_menu, self.prev_tab_act)
660 self.add_menu_action(self.window_menu, self.prev_tab_act)
657
661
658 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
662 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
659 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
663 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
660 self,
664 self,
661 shortcut=next_key,
665 shortcut=next_key,
662 statusTip="Select next tab",
666 statusTip="Select next tab",
663 triggered=self.next_tab)
667 triggered=self.next_tab)
664 self.add_menu_action(self.window_menu, self.next_tab_act)
668 self.add_menu_action(self.window_menu, self.next_tab_act)
665
669
666 def init_help_menu(self):
670 def init_help_menu(self):
667 # please keep the Help menu in Mac Os even if empty. It will
671 # please keep the Help menu in Mac Os even if empty. It will
668 # automatically contain a search field to search inside menus and
672 # automatically contain a search field to search inside menus and
669 # please keep it spelled in English, as long as Qt Doesn't support
673 # please keep it spelled in English, as long as Qt Doesn't support
670 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
674 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
671 # this search field functionality
675 # this search field functionality
672
676
673 self.help_menu = self.menuBar().addMenu("&Help")
677 self.help_menu = self.menuBar().addMenu("&Help")
674
678
675
679
676 # Help Menu
680 # Help Menu
677
681
678 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
682 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
679 self,
683 self,
680 triggered=self.intro_active_frontend
684 triggered=self.intro_active_frontend
681 )
685 )
682 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
686 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
683
687
684 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
688 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
685 self,
689 self,
686 triggered=self.quickref_active_frontend
690 triggered=self.quickref_active_frontend
687 )
691 )
688 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
692 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
689
693
690 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
694 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
691 self,
695 self,
692 triggered=self.guiref_active_frontend
696 triggered=self.guiref_active_frontend
693 )
697 )
694 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
698 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
695
699
696 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
700 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
697 self,
701 self,
698 triggered=self._open_online_help)
702 triggered=self._open_online_help)
699 self.add_menu_action(self.help_menu, self.onlineHelpAct)
703 self.add_menu_action(self.help_menu, self.onlineHelpAct)
700
704
701 def init_magic_helper(self):
705 def init_magic_helper(self):
702 from .magic_helper import MagicHelper
706 from .magic_helper import MagicHelper
703
707
704 self.magic_helper = MagicHelper("Show Magics", self)
708 self.magic_helper = MagicHelper("Show Magics", self)
705
709
706 self.magic_helper.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea |
710 self.magic_helper.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea |
707 QtCore.Qt.RightDockWidgetArea)
711 QtCore.Qt.RightDockWidgetArea)
708 self.magic_helper.setVisible(False)
712 self.magic_helper.setVisible(False)
709
713
710 self.magic_helper.pasteRequested[str].connect(
714 self.magic_helper.pasteRequested[str].connect(
711 self.magic_helper_paste_requested
715 self.magic_helper_paste_requested
712 )
716 )
713 self.magic_helper.runRequested[str].connect(
717 self.magic_helper.runRequested[str].connect(
714 self.magic_helper_run_requested
718 self.magic_helper_run_requested
715 )
719 )
716 self.magic_helper.readyForUpdate.connect(
720 self.magic_helper.readyForUpdate.connect(
717 self.magic_helper_update_requested
721 self.magic_helper_update_requested
718 )
722 )
719
723
720 self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.magic_helper)
724 self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.magic_helper)
721
725
722 def _set_active_frontend_focus(self):
726 def _set_active_frontend_focus(self):
723 # this is a hack, self.active_frontend._control seems to be
727 # this is a hack, self.active_frontend._control seems to be
724 # a private member. Unfortunately this is the only method
728 # a private member. Unfortunately this is the only method
725 # to set focus reliably
729 # to set focus reliably
726 QtCore.QTimer.singleShot(200, self.active_frontend._control.setFocus)
730 QtCore.QTimer.singleShot(200, self.active_frontend._control.setFocus)
727
731
728 def magic_helper_paste_requested(self, text = None):
732 def magic_helper_paste_requested(self, text = None):
729 if text != None:
733 if text != None:
730 self.active_frontend.input_buffer = text
734 self.active_frontend.input_buffer = text
731 self._set_active_frontend_focus()
735 self._set_active_frontend_focus()
732
736
733 def magic_helper_run_requested(self, text = None):
737 def magic_helper_run_requested(self, text = None):
734 if text != None:
738 if text != None:
735 self.active_frontend.execute(text)
739 self.active_frontend.execute(text)
736 self._set_active_frontend_focus()
740 self._set_active_frontend_focus()
737
741
738 def magic_helper_update_requested(self):
742 def magic_helper_update_requested(self):
739 def _handle_data(data):
743 def _handle_data(data):
740 if not data:
744 if not data:
741 return
745 return
742
746
743 if data['status'] != 'ok':
747 if data['status'] != 'ok':
744 self.log.warn(
748 self.log.warn(
745 "%%lsmagic user-expression failed: {}".format(data)
749 "%%lsmagic user-expression failed: {}".format(data)
746 )
750 )
747 return
751 return
748 self.magic_helper.populate_magic_helper(data)
752 self.magic_helper.populate_magic_helper(data)
749
753
750 self.active_frontend._silent_exec_callback(
754 self.active_frontend._silent_exec_callback(
751 'get_ipython().magic("lsmagic")',
755 'get_ipython().magic("lsmagic")',
752 _handle_data
756 _handle_data
753 )
757 )
754
758
755 # minimize/maximize/fullscreen actions:
759 # minimize/maximize/fullscreen actions:
756
760
757 def toggle_menu_bar(self):
761 def toggle_menu_bar(self):
758 menu_bar = self.menuBar()
762 menu_bar = self.menuBar()
759 if menu_bar.isVisible():
763 if menu_bar.isVisible():
760 menu_bar.setVisible(False)
764 menu_bar.setVisible(False)
761 else:
765 else:
762 menu_bar.setVisible(True)
766 menu_bar.setVisible(True)
763
767
764 def toggleMinimized(self):
768 def toggleMinimized(self):
765 if not self.isMinimized():
769 if not self.isMinimized():
766 self.showMinimized()
770 self.showMinimized()
767 else:
771 else:
768 self.showNormal()
772 self.showNormal()
769
773
770 def _open_online_help(self):
774 def _open_online_help(self):
771 filename="http://ipython.org/ipython-doc/stable/index.html"
775 filename="http://ipython.org/ipython-doc/stable/index.html"
772 webbrowser.open(filename, new=1, autoraise=True)
776 webbrowser.open(filename, new=1, autoraise=True)
773
777
774 def toggleMaximized(self):
778 def toggleMaximized(self):
775 if not self.isMaximized():
779 if not self.isMaximized():
776 self.showMaximized()
780 self.showMaximized()
777 else:
781 else:
778 self.showNormal()
782 self.showNormal()
779
783
780 # Min/Max imizing while in full screen give a bug
784 # Min/Max imizing while in full screen give a bug
781 # when going out of full screen, at least on OSX
785 # when going out of full screen, at least on OSX
782 def toggleFullScreen(self):
786 def toggleFullScreen(self):
783 if not self.isFullScreen():
787 if not self.isFullScreen():
784 self.showFullScreen()
788 self.showFullScreen()
785 if sys.platform == 'darwin':
789 if sys.platform == 'darwin':
786 self.maximizeAct.setEnabled(False)
790 self.maximizeAct.setEnabled(False)
787 self.minimizeAct.setEnabled(False)
791 self.minimizeAct.setEnabled(False)
788 else:
792 else:
789 self.showNormal()
793 self.showNormal()
790 if sys.platform == 'darwin':
794 if sys.platform == 'darwin':
791 self.maximizeAct.setEnabled(True)
795 self.maximizeAct.setEnabled(True)
792 self.minimizeAct.setEnabled(True)
796 self.minimizeAct.setEnabled(True)
793
797
794 def set_paging_active_frontend(self, paging):
798 def set_paging_active_frontend(self, paging):
795 self.active_frontend._set_paging(paging)
799 self.active_frontend._set_paging(paging)
796
800
797 def close_active_frontend(self):
801 def close_active_frontend(self):
798 self.close_tab(self.active_frontend)
802 self.close_tab(self.active_frontend)
799
803
800 def restart_kernel_active_frontend(self):
804 def restart_kernel_active_frontend(self):
801 self.active_frontend.request_restart_kernel()
805 self.active_frontend.request_restart_kernel()
802
806
803 def interrupt_kernel_active_frontend(self):
807 def interrupt_kernel_active_frontend(self):
804 self.active_frontend.request_interrupt_kernel()
808 self.active_frontend.request_interrupt_kernel()
805
809
806 def toggle_confirm_restart_active_frontend(self):
810 def toggle_confirm_restart_active_frontend(self):
807 widget = self.active_frontend
811 widget = self.active_frontend
808 widget.confirm_restart = not widget.confirm_restart
812 widget.confirm_restart = not widget.confirm_restart
809 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
813 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
810
814
811 def update_restart_checkbox(self):
815 def update_restart_checkbox(self):
812 if self.active_frontend is None:
816 if self.active_frontend is None:
813 return
817 return
814 widget = self.active_frontend
818 widget = self.active_frontend
815 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
819 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
816
820
817 def cut_active_frontend(self):
821 def cut_active_frontend(self):
818 widget = self.active_frontend
822 widget = self.active_frontend
819 if widget.can_cut():
823 if widget.can_cut():
820 widget.cut()
824 widget.cut()
821
825
822 def copy_active_frontend(self):
826 def copy_active_frontend(self):
823 widget = self.active_frontend
827 widget = self.active_frontend
824 widget.copy()
828 widget.copy()
825
829
826 def copy_raw_active_frontend(self):
830 def copy_raw_active_frontend(self):
827 self.active_frontend._copy_raw_action.trigger()
831 self.active_frontend._copy_raw_action.trigger()
828
832
829 def paste_active_frontend(self):
833 def paste_active_frontend(self):
830 widget = self.active_frontend
834 widget = self.active_frontend
831 if widget.can_paste():
835 if widget.can_paste():
832 widget.paste()
836 widget.paste()
833
837
834 def undo_active_frontend(self):
838 def undo_active_frontend(self):
835 self.active_frontend.undo()
839 self.active_frontend.undo()
836
840
837 def redo_active_frontend(self):
841 def redo_active_frontend(self):
838 self.active_frontend.redo()
842 self.active_frontend.redo()
839
843
840 def reset_magic_active_frontend(self):
844 def reset_magic_active_frontend(self):
841 self.active_frontend.execute("%reset")
845 self.active_frontend.execute("%reset")
842
846
843 def history_magic_active_frontend(self):
847 def history_magic_active_frontend(self):
844 self.active_frontend.execute("%history")
848 self.active_frontend.execute("%history")
845
849
846 def save_magic_active_frontend(self):
850 def save_magic_active_frontend(self):
847 self.active_frontend.save_magic()
851 self.active_frontend.save_magic()
848
852
849 def clear_magic_active_frontend(self):
853 def clear_magic_active_frontend(self):
850 self.active_frontend.execute("%clear")
854 self.active_frontend.execute("%clear")
851
855
852 def who_magic_active_frontend(self):
856 def who_magic_active_frontend(self):
853 self.active_frontend.execute("%who")
857 self.active_frontend.execute("%who")
854
858
855 def who_ls_magic_active_frontend(self):
859 def who_ls_magic_active_frontend(self):
856 self.active_frontend.execute("%who_ls")
860 self.active_frontend.execute("%who_ls")
857
861
858 def whos_magic_active_frontend(self):
862 def whos_magic_active_frontend(self):
859 self.active_frontend.execute("%whos")
863 self.active_frontend.execute("%whos")
860
864
861 def print_action_active_frontend(self):
865 def print_action_active_frontend(self):
862 self.active_frontend.print_action.trigger()
866 self.active_frontend.print_action.trigger()
863
867
864 def export_action_active_frontend(self):
868 def export_action_active_frontend(self):
865 self.active_frontend.export_action.trigger()
869 self.active_frontend.export_action.trigger()
866
870
867 def select_all_active_frontend(self):
871 def select_all_active_frontend(self):
868 self.active_frontend.select_all_action.trigger()
872 self.active_frontend.select_all_action.trigger()
869
873
870 def increase_font_size_active_frontend(self):
874 def increase_font_size_active_frontend(self):
871 self.active_frontend.increase_font_size.trigger()
875 self.active_frontend.increase_font_size.trigger()
872
876
873 def decrease_font_size_active_frontend(self):
877 def decrease_font_size_active_frontend(self):
874 self.active_frontend.decrease_font_size.trigger()
878 self.active_frontend.decrease_font_size.trigger()
875
879
876 def reset_font_size_active_frontend(self):
880 def reset_font_size_active_frontend(self):
877 self.active_frontend.reset_font_size.trigger()
881 self.active_frontend.reset_font_size.trigger()
878
882
879 def guiref_active_frontend(self):
883 def guiref_active_frontend(self):
880 self.active_frontend.execute("%guiref")
884 self.active_frontend.execute("%guiref")
881
885
882 def intro_active_frontend(self):
886 def intro_active_frontend(self):
883 self.active_frontend.execute("?")
887 self.active_frontend.execute("?")
884
888
885 def quickref_active_frontend(self):
889 def quickref_active_frontend(self):
886 self.active_frontend.execute("%quickref")
890 self.active_frontend.execute("%quickref")
887 #---------------------------------------------------------------------------
891 #---------------------------------------------------------------------------
888 # QWidget interface
892 # QWidget interface
889 #---------------------------------------------------------------------------
893 #---------------------------------------------------------------------------
890
894
891 def closeEvent(self, event):
895 def closeEvent(self, event):
892 """ Forward the close event to every tabs contained by the windows
896 """ Forward the close event to every tabs contained by the windows
893 """
897 """
894 if self.tab_widget.count() == 0:
898 if self.tab_widget.count() == 0:
895 # no tabs, just close
899 # no tabs, just close
896 event.accept()
900 event.accept()
897 return
901 return
898 # Do Not loop on the widget count as it change while closing
902 # Do Not loop on the widget count as it change while closing
899 title = self.window().windowTitle()
903 title = self.window().windowTitle()
900 cancel = QtGui.QMessageBox.Cancel
904 cancel = QtGui.QMessageBox.Cancel
901 okay = QtGui.QMessageBox.Ok
905 okay = QtGui.QMessageBox.Ok
902
906
903 if self.confirm_exit:
907 if self.confirm_exit:
904 if self.tab_widget.count() > 1:
908 if self.tab_widget.count() > 1:
905 msg = "Close all tabs, stop all kernels, and Quit?"
909 msg = "Close all tabs, stop all kernels, and Quit?"
906 else:
910 else:
907 msg = "Close console, stop kernel, and Quit?"
911 msg = "Close console, stop kernel, and Quit?"
908 info = "Kernels not started here (e.g. notebooks) will be left alone."
912 info = "Kernels not started here (e.g. notebooks) will be left alone."
909 closeall = QtGui.QPushButton("&Quit", self)
913 closeall = QtGui.QPushButton("&Quit", self)
910 closeall.setShortcut('Q')
914 closeall.setShortcut('Q')
911 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
915 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
912 title, msg)
916 title, msg)
913 box.setInformativeText(info)
917 box.setInformativeText(info)
914 box.addButton(cancel)
918 box.addButton(cancel)
915 box.addButton(closeall, QtGui.QMessageBox.YesRole)
919 box.addButton(closeall, QtGui.QMessageBox.YesRole)
916 box.setDefaultButton(closeall)
920 box.setDefaultButton(closeall)
917 box.setEscapeButton(cancel)
921 box.setEscapeButton(cancel)
918 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
922 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
919 box.setIconPixmap(pixmap)
923 box.setIconPixmap(pixmap)
920 reply = box.exec_()
924 reply = box.exec_()
921 else:
925 else:
922 reply = okay
926 reply = okay
923
927
924 if reply == cancel:
928 if reply == cancel:
925 event.ignore()
929 event.ignore()
926 return
930 return
927 if reply == okay:
931 if reply == okay:
928 while self.tab_widget.count() >= 1:
932 while self.tab_widget.count() >= 1:
929 # prevent further confirmations:
933 # prevent further confirmations:
930 widget = self.active_frontend
934 widget = self.active_frontend
931 widget._confirm_exit = False
935 widget._confirm_exit = False
932 self.close_tab(widget)
936 self.close_tab(widget)
933 event.accept()
937 event.accept()
934
938
General Comments 0
You need to be logged in to leave comments. Login now