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