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