##// END OF EJS Templates
Fix menubar action after @Carreau's suggestion.
Fernando Perez -
Show More
@@ -1,1187 +1,1187 b''
1 1 """ A minimal application using the Qt console-style IPython frontend.
2 2
3 3 This is not a complete console app, as subprocess will not be able to receive
4 4 input, there is no real readline support, among other limitations.
5 5
6 6 Authors:
7 7
8 8 * Evan Patterson
9 9 * Min RK
10 10 * Erik Tollerud
11 11 * Fernando Perez
12 12
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 # stdlib imports
20 20 import json
21 21 import os
22 22 import signal
23 23 import sys
24 24 import webbrowser
25 25 import uuid
26 26 from getpass import getpass
27 27
28 28 # System library imports
29 29 from IPython.external.qt import QtGui,QtCore
30 30 from pygments.styles import get_all_styles
31 31
32 32 # Local imports
33 33 from IPython.config.application import boolean_flag
34 34 from IPython.core.application import BaseIPythonApplication
35 35 from IPython.core.profiledir import ProfileDir
36 36 from IPython.lib.kernel import tunnel_to_kernel, find_connection_file
37 37 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
38 38 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
39 39 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
40 40 from IPython.frontend.qt.console import styles
41 41 from IPython.frontend.qt.kernelmanager import QtKernelManager
42 42 from IPython.utils.path import filefind
43 43 from IPython.utils.py3compat import str_to_bytes
44 44 from IPython.utils.traitlets import (
45 45 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
46 46 )
47 47 from IPython.zmq.ipkernel import (
48 48 flags as ipkernel_flags,
49 49 aliases as ipkernel_aliases,
50 50 IPKernelApp
51 51 )
52 52 from IPython.zmq.session import Session, default_secure
53 53 from IPython.zmq.zmqshell import ZMQInteractiveShell
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Network Constants
57 57 #-----------------------------------------------------------------------------
58 58
59 59 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
60 60
61 61 #-----------------------------------------------------------------------------
62 62 # Globals
63 63 #-----------------------------------------------------------------------------
64 64
65 65 _examples = """
66 66 ipython qtconsole # start the qtconsole
67 67 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
68 68 """
69 69
70 70 #-----------------------------------------------------------------------------
71 71 # Classes
72 72 #-----------------------------------------------------------------------------
73 73
74 74 class MainWindow(QtGui.QMainWindow):
75 75
76 76 #---------------------------------------------------------------------------
77 77 # 'object' interface
78 78 #---------------------------------------------------------------------------
79 79
80 80 def __init__(self, app, frontend, existing=False, may_close=True,
81 81 confirm_exit=True):
82 82 """ Create a MainWindow for the specified FrontendWidget.
83 83
84 84 The app is passed as an argument to allow for different
85 85 closing behavior depending on whether we are the Kernel's parent.
86 86
87 87 If existing is True, then this Console does not own the Kernel.
88 88
89 89 If may_close is True, then this Console is permitted to close the kernel
90 90 """
91 91
92 92 super(MainWindow, self).__init__()
93 93 self._app = app
94 94
95 95 self.tab_widget = QtGui.QTabWidget(self)
96 96 self.tab_widget.setDocumentMode(True)
97 97 self.tab_widget.setTabsClosable(True)
98 98 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
99 99
100 100 self.setCentralWidget(self.tab_widget)
101 101 self.update_tab_bar_visibility()
102 102
103 103 def update_tab_bar_visibility(self):
104 104 """ update visibility of the tabBar depending of the number of tab
105 105
106 106 0 or 1 tab, tabBar hidden
107 107 2+ tabs, tabBar visible
108 108
109 109 send a self.close if number of tab ==0
110 110
111 111 need to be called explicitely, or be connected to tabInserted/tabRemoved
112 112 """
113 113 if self.tab_widget.count() <= 1:
114 114 self.tab_widget.tabBar().setVisible(False)
115 115 else:
116 116 self.tab_widget.tabBar().setVisible(True)
117 117 if self.tab_widget.count()==0 :
118 118 self.close()
119 119
120 120 @property
121 121 def active_frontend(self):
122 122 return self.tab_widget.currentWidget()
123 123
124 124 def close_tab(self,current_tab):
125 125 """ Called when you need to try to close a tab.
126 126
127 127 It takes the number of the tab to be closed as argument, or a referece
128 128 to the wiget insite this tab
129 129 """
130 130
131 131 # let's be sure "tab" and "closing widget are respectivey the index of the tab to close
132 132 # and a reference to the trontend to close
133 133 if type(current_tab) is not int :
134 134 current_tab = self.tab_widget.indexOf(current_tab)
135 135 closing_widget=self.tab_widget.widget(current_tab)
136 136
137 137
138 138 # when trying to be closed, widget might re-send a request to be closed again, but will
139 139 # be deleted when event will be processed. So need to check that widget still exist and
140 140 # skip if not. One example of this is when 'exit' is send in a slave tab. 'exit' will be
141 141 # re-send by this fonction on the master widget, which ask all slaves widget to exit
142 142 if closing_widget==None:
143 143 return
144 144
145 145 #get a list of all wwidget not owning the kernel.
146 146 slave_tabs=self.find_slaves_tabs(closing_widget)
147 147
148 148 keepkernel = None #Use the prompt by default
149 149 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
150 150 keepkernel = closing_widget._keep_kernel_on_exit
151 151 # If signal sent by exist magic (_keep_kernel_on_exit, exist and not None)
152 152 # we set local slave tabs._hidden to True to avoit prompting for kernel
153 153 # restart when they litt get the signal. and the "forward" the 'exit'
154 154 # to the main win
155 155 if keepkernel is not None:
156 156 for tab in slave_tabs:
157 157 tab._hidden = True
158 158 if closing_widget in slave_tabs :
159 159 try :
160 160 self.find_master_tab(closing_widget).execute('exit')
161 161 except AttributeError:
162 162 self.log.info("Master already closed or not local, closing only current tab")
163 163 self.tab_widget.removeTab(current_tab)
164 164 return
165 165
166 166 kernel_manager = closing_widget.kernel_manager
167 167
168 168 if keepkernel is None and not closing_widget._confirm_exit:
169 169 # don't prompt, just terminate the kernel if we own it
170 170 # or leave it alone if we don't
171 171 keepkernel = not closing_widget._existing
172 172
173 173 if keepkernel is None: #show prompt
174 174 if kernel_manager and kernel_manager.channels_running:
175 175 title = self.window().windowTitle()
176 176 cancel = QtGui.QMessageBox.Cancel
177 177 okay = QtGui.QMessageBox.Ok
178 178 if closing_widget._may_close:
179 179 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
180 180 info = "Would you like to quit the Kernel and all attached Consoles as well?"
181 181 justthis = QtGui.QPushButton("&No, just this Console", self)
182 182 justthis.setShortcut('N')
183 183 closeall = QtGui.QPushButton("&Yes, quit everything", self)
184 184 closeall.setShortcut('Y')
185 185 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
186 186 title, msg)
187 187 box.setInformativeText(info)
188 188 box.addButton(cancel)
189 189 box.addButton(justthis, QtGui.QMessageBox.NoRole)
190 190 box.addButton(closeall, QtGui.QMessageBox.YesRole)
191 191 box.setDefaultButton(closeall)
192 192 box.setEscapeButton(cancel)
193 193 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
194 194 box.setIconPixmap(pixmap)
195 195 reply = box.exec_()
196 196 if reply == 1: # close All
197 197 for slave in slave_tabs:
198 198 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
199 199 closing_widget.execute("exit")
200 200 self.tab_widget.removeTab(current_tab)
201 201 elif reply == 0: # close Console
202 202 if not closing_widget._existing:
203 203 # Have kernel: don't quit, just close the window
204 204 self._app.setQuitOnLastWindowClosed(False)
205 205 closing_widget.execute("exit True")
206 206 else:
207 207 reply = QtGui.QMessageBox.question(self, title,
208 208 "Are you sure you want to close this Console?"+
209 209 "\nThe Kernel and other Consoles will remain active.",
210 210 okay|cancel,
211 211 defaultButton=okay
212 212 )
213 213 if reply == okay:
214 214 self.tab_widget.removeTab(current_tab)
215 215 elif keepkernel: #close console but leave kernel running (no prompt)
216 216 if kernel_manager and kernel_manager.channels_running:
217 217 if not closing_widget._existing:
218 218 # I have the kernel: don't quit, just close the window
219 219 self.tab_widget.removeTab(current_tab)
220 220 else: #close console and kernel (no prompt)
221 221 if kernel_manager and kernel_manager.channels_running:
222 222 for slave in slave_tabs:
223 223 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
224 224 self.tab_widget.removeTab(current_tab)
225 225 kernel_manager.shutdown_kernel()
226 226 self.update_tab_bar_visibility()
227 227
228 228 def add_tab_with_frontend(self,frontend,name=None):
229 229 """ insert a tab with a given frontend in the tab bar, and give it a name
230 230
231 231 """
232 232 if not name:
233 233 name=str('kernel '+str(self.tab_widget.count()))
234 234 self.tab_widget.addTab(frontend,name)
235 235 self.update_tab_bar_visibility()
236 236 self.make_frontend_visible(frontend)
237 237 frontend.exit_requested.connect(self.close_tab)
238 238
239 239 def next_tab(self):
240 240 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
241 241
242 242 def prev_tab(self):
243 243 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
244 244
245 245 def make_frontend_visible(self,frontend):
246 246 widget_index=self.tab_widget.indexOf(frontend)
247 247 if widget_index > 0 :
248 248 self.tab_widget.setCurrentIndex(widget_index)
249 249
250 250 def find_master_tab(self,tab,as_list=False):
251 251 """
252 252 Try to return the frontend that own the kernel attached to the given widget/tab.
253 253
254 254 Only find frontend owed by the current application. Selection
255 255 based on port of the kernel, might be inacurate if several kernel
256 256 on different ip use same port number.
257 257
258 258 This fonction does the conversion tabNumber/widget if needed.
259 259 Might return None if no master widget (non local kernel)
260 260 Will crash IPython if more than 1 masterWidget
261 261
262 262 When asList set to True, always return a list of widget(s) owning
263 263 the kernel. The list might be empty or containing several Widget.
264 264 """
265 265
266 266 #convert from/to int/richIpythonWidget if needed
267 267 if type(tab) == int:
268 268 tab = self.tab_widget.widget(tab)
269 269 km=tab.kernel_manager;
270 270
271 271 #build list of all widgets
272 272 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
273 273
274 274 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
275 275 # And should have a _may_close attribute
276 276 filtred_widget_list = [ widget for widget in widget_list if
277 277 widget.kernel_manager.connection_file == km.connection_file and
278 278 hasattr(widget,'_may_close') ]
279 279 # the master widget is the one that may close the kernel
280 280 master_widget= [ widget for widget in filtred_widget_list if widget._may_close]
281 281 if as_list:
282 282 return master_widget
283 283 assert(len(master_widget)<=1 )
284 284 if len(master_widget)==0:
285 285 return None
286 286
287 287 return master_widget[0]
288 288
289 289 def find_slaves_tabs(self,tab):
290 290 """
291 291 Try to return all the frontend that do not own the kernel attached to the given widget/tab.
292 292
293 293 Only find frontend owed by the current application. Selection
294 294 based on port of the kernel, might be innacurate if several kernel
295 295 on different ip use same port number.
296 296
297 297 This fonction does the conversion tabNumber/widget if needed.
298 298 """
299 299 #convert from/to int/richIpythonWidget if needed
300 300 if type(tab) == int:
301 301 tab = self.tab_widget.widget(tab)
302 302 km=tab.kernel_manager;
303 303
304 304 #build list of all widgets
305 305 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
306 306
307 307 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
308 308 filtered_widget_list = ( widget for widget in widget_list if
309 309 widget.kernel_manager.connection_file == km.connection_file)
310 310 # Get a list of all widget owning the same kernel and removed it from
311 311 # the previous cadidate. (better using sets ?)
312 312 master_widget_list = self.find_master_tab(tab,as_list=True)
313 313 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
314 314
315 315 return slave_list
316 316
317 317 # MenuBar is always present on Mac Os, so let's populate it with possible
318 318 # action, don't do it on other platform as some user might not want the
319 319 # menu bar, or give them an option to remove it
320 320 def init_menu_bar(self):
321 321 #create menu in the order they should appear in the menu bar
322 322 self.file_menu = self.menuBar().addMenu("&File")
323 323 self.edit_menu = self.menuBar().addMenu("&Edit")
324 324 self.kernel_menu = self.menuBar().addMenu("&Kernel")
325 325 self.window_menu = self.menuBar().addMenu("&Window")
326 326 self.magic_menu = self.menuBar().addMenu("&Magic")
327 327 self.all_magic_menu = self.magic_menu.addMenu("&All Magic")
328 328
329 329 # please keep the Help menu in Mac Os even if empty. It will
330 330 # automatically contain a search field to search inside menus and
331 331 # please keep it spelled in English, as long as Qt Doesn't support
332 332 # a QAction.MenuRole like HelpMenuRole otherwise it will loose
333 333 # this search field fonctionnality
334 334
335 335 self.help_menu = self.menuBar().addMenu("&Help")
336 336
337 337 self.print_action = QtGui.QAction("&Print",
338 338 self,
339 339 shortcut="Ctrl+P",
340 340 triggered=self.print_action_active_frontend)
341 341 self.file_menu.addAction(self.print_action)
342 342
343 343 self.export_action=QtGui.QAction("E&xport",
344 344 self,
345 345 shortcut="Ctrl+S",
346 346 triggered=self.export_action_active_frontend
347 347 )
348 348 self.file_menu.addAction(self.export_action)
349 349
350 350 self.select_all_action = QtGui.QAction("Select &All",
351 351 self,
352 352 shortcut="Ctrl+A",
353 353 triggered=self.select_all_active_frontend
354 354 )
355 355 self.file_menu.addAction(self.select_all_action)
356 356
357 357 self.paste_action = QtGui.QAction("&Paste",
358 358 self,
359 359 shortcut=QtGui.QKeySequence.Paste,
360 360 triggered=self.paste_active_frontend
361 361 )
362 362 self.edit_menu.addAction(self.paste_action)
363 363
364 364 self.copy_action = QtGui.QAction("&Copy",
365 365 self,
366 366 shortcut=QtGui.QKeySequence.Copy,
367 367 triggered=self.copy_active_frontend
368 368 )
369 369 self.edit_menu.addAction(self.copy_action)
370 370
371 371 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
372 372 self,
373 373 shortcut="Ctrl+Shift+C",
374 374 triggered=self.copy_raw_active_frontend
375 375 )
376 376 self.edit_menu.addAction(self.copy_raw_action)
377 377
378 378 self.cut_action = QtGui.QAction("&Cut",
379 379 self,
380 380 shortcut=QtGui.QKeySequence.Cut,
381 381 triggered=self.cut_active_frontend
382 382 )
383 383 self.edit_menu.addAction(self.cut_action)
384 384
385 385 self.edit_menu.addSeparator()
386 386
387 387 self.undo_action = QtGui.QAction("&Undo",
388 388 self,
389 389 shortcut="Ctrl+Z",
390 390 statusTip="Undo last action if possible",
391 391 triggered=self.undo_active_frontend
392 392 )
393 393 self.edit_menu.addAction(self.undo_action)
394 394
395 395 self.redo_action = QtGui.QAction("&Redo",
396 396 self,
397 397 shortcut="Ctrl+Shift+Z",
398 398 statusTip="Redo last action if possible",
399 399 triggered=self.redo_active_frontend)
400 400 self.edit_menu.addAction(self.redo_action)
401 401
402 402 self.window_menu.addSeparator()
403 403
404 404 self.increase_font_size = QtGui.QAction("&Increase Font Size",
405 405 self,
406 406 shortcut="Ctrl++",
407 407 triggered=self.increase_font_size_active_frontend
408 408 )
409 409 self.window_menu.addAction(self.increase_font_size)
410 410
411 411 self.decrease_font_size = QtGui.QAction("&Decrease Font Size",
412 412 self,
413 413 shortcut="Ctrl+-",
414 414 triggered=self.decrease_font_size_active_frontend
415 415 )
416 416 self.window_menu.addAction(self.decrease_font_size)
417 417
418 418 self.reset_font_size = QtGui.QAction("&Reset Font Size",
419 419 self,
420 420 shortcut="Ctrl+0",
421 421 triggered=self.reset_font_size_active_frontend
422 422 )
423 423 self.window_menu.addAction(self.reset_font_size)
424 424
425 425 self.window_menu.addSeparator()
426 426
427 427 self.reset_action = QtGui.QAction("&Reset",
428 428 self,
429 429 statusTip="Clear all varible from workspace",
430 430 triggered=self.reset_magic_active_frontend)
431 431 self.magic_menu.addAction(self.reset_action)
432 432
433 433 self.history_action = QtGui.QAction("&History",
434 434 self,
435 435 statusTip="show command history",
436 436 triggered=self.history_magic_active_frontend)
437 437 self.magic_menu.addAction(self.history_action)
438 438
439 439 self.save_action = QtGui.QAction("E&xport History ",
440 440 self,
441 441 statusTip="Export History as Python File",
442 442 triggered=self.save_magic_active_frontend)
443 443 self.magic_menu.addAction(self.save_action)
444 444
445 445 self.clear_action = QtGui.QAction("&Clear Screen",
446 446 self,
447 447 shortcut='Ctrl+L',
448 448 statusTip="Clear the console",
449 449 triggered=self.clear_magic_active_frontend)
450 450 self.window_menu.addAction(self.clear_action)
451 451
452 452 self.who_action = QtGui.QAction("&Who",
453 453 self,
454 454 statusTip="List interactive variable",
455 455 triggered=self.who_magic_active_frontend)
456 456 self.magic_menu.addAction(self.who_action)
457 457
458 458 self.who_ls_action = QtGui.QAction("Wh&o ls",
459 459 self,
460 460 statusTip="Return a list of interactive variable",
461 461 triggered=self.who_ls_magic_active_frontend)
462 462 self.magic_menu.addAction(self.who_ls_action)
463 463
464 464 self.whos_action = QtGui.QAction("Who&s",
465 465 self,
466 466 statusTip="List interactive variable with detail",
467 467 triggered=self.whos_magic_active_frontend)
468 468 self.magic_menu.addAction(self.whos_action)
469 469
470 470 self.intro_active_frontend_action = QtGui.QAction("Intro",
471 471 self,
472 472 triggered=self.intro_active_frontend
473 473 )
474 474 self.help_menu.addAction(self.intro_active_frontend_action)
475 475
476 476 self.guiref_active_frontend_action = QtGui.QAction("Gui references",
477 477 self,
478 478 triggered=self.guiref_active_frontend
479 479 )
480 480 self.help_menu.addAction(self.guiref_active_frontend_action)
481 481
482 482 self.quickref_active_frontend_action = QtGui.QAction("Quick references",
483 483 self,
484 484 triggered=self.quickref_active_frontend
485 485 )
486 486 self.help_menu.addAction(self.quickref_active_frontend_action)
487 487
488 488 self.interrupt_kernel_action = QtGui.QAction("Interrupt current Kernel",
489 489 self,
490 490 triggered=self.interrupt_kernel_active_frontend
491 491 )
492 492 self.kernel_menu.addAction(self.interrupt_kernel_action)
493 493
494 494 self.restart_kernel_action = QtGui.QAction("Restart current Kernel",
495 495 self,
496 496 triggered=self.restart_kernel_active_frontend
497 497 )
498 498 self.kernel_menu.addAction(self.restart_kernel_action)
499 499 self.kernel_menu.addSeparator()
500 500
501 501 #for now this is just a copy and paste, but we should get this dynamically
502 502 magiclist=["%alias", "%autocall", "%automagic", "%bookmark", "%cd", "%clear",
503 503 "%colors", "%debug", "%dhist", "%dirs", "%doctest_mode", "%ed", "%edit", "%env", "%gui",
504 504 "%guiref", "%hist", "%history", "%install_default_config", "%install_profiles",
505 505 "%less", "%load_ext", "%loadpy", "%logoff", "%logon", "%logstart", "%logstate",
506 506 "%logstop", "%lsmagic", "%macro", "%magic", "%man", "%more", "%notebook", "%page",
507 507 "%pastebin", "%pdb", "%pdef", "%pdoc", "%pfile", "%pinfo", "%pinfo2", "%popd", "%pprint",
508 508 "%precision", "%profile", "%prun", "%psearch", "%psource", "%pushd", "%pwd", "%pycat",
509 509 "%pylab", "%quickref", "%recall", "%rehashx", "%reload_ext", "%rep", "%rerun",
510 510 "%reset", "%reset_selective", "%run", "%save", "%sc", "%sx", "%tb", "%time", "%timeit",
511 511 "%unalias", "%unload_ext", "%who", "%who_ls", "%whos", "%xdel", "%xmode"]
512 512
513 513 def make_dynamic_magic(i):
514 514 def inner_dynamic_magic():
515 515 self.active_frontend.execute(i)
516 516 inner_dynamic_magic.__name__ = "dynamics_magic_%s" % i
517 517 return inner_dynamic_magic
518 518
519 519 for magic in magiclist:
520 520 xaction = QtGui.QAction(magic,
521 521 self,
522 522 triggered=make_dynamic_magic(magic)
523 523 )
524 524 self.all_magic_menu.addAction(xaction)
525 525
526 526 def restart_kernel_active_frontend(self):
527 527 self.active_frontend.request_restart_kernel()
528 528
529 529 def interrupt_kernel_active_frontend(self):
530 530 self.active_frontend.request_interrupt_kernel()
531 531
532 532 def cut_active_frontend(self):
533 533 self.active_frontend.cut_action.trigger()
534 534
535 535 def copy_active_frontend(self):
536 536 self.active_frontend.copy_action.trigger()
537 537
538 538 def copy_raw_active_frontend(self):
539 539 self.active_frontend._copy_raw_action.trigger()
540 540
541 541 def paste_active_frontend(self):
542 542 self.active_frontend.paste_action.trigger()
543 543
544 544 def undo_active_frontend(self):
545 545 self.active_frontend.undo()
546 546
547 547 def redo_active_frontend(self):
548 548 self.active_frontend.redo()
549 549
550 550 def reset_magic_active_frontend(self):
551 551 self.active_frontend.execute("%reset")
552 552
553 553 def history_magic_active_frontend(self):
554 554 self.active_frontend.execute("%history")
555 555
556 556 def save_magic_active_frontend(self):
557 557 self.active_frontend.save_magic()
558 558
559 559 def clear_magic_active_frontend(self):
560 560 self.active_frontend.execute("%clear")
561 561
562 562 def who_magic_active_frontend(self):
563 563 self.active_frontend.execute("%who")
564 564
565 565 def who_ls_magic_active_frontend(self):
566 566 self.active_frontend.execute("%who_ls")
567 567
568 568 def whos_magic_active_frontend(self):
569 569 self.active_frontend.execute("%whos")
570 570
571 571 def print_action_active_frontend(self):
572 572 self.active_frontend.print_action.trigger()
573 573
574 574 def export_action_active_frontend(self):
575 575 self.active_frontend.export_action.trigger()
576 576
577 577 def select_all_active_frontend(self):
578 578 self.active_frontend.select_all_action.trigger()
579 579
580 580 def increase_font_size_active_frontend(self):
581 581 self.active_frontend.increase_font_size.trigger()
582 582
583 583 def decrease_font_size_active_frontend(self):
584 584 self.active_frontend.decrease_font_size.trigger()
585 585
586 586 def reset_font_size_active_frontend(self):
587 587 self.active_frontend.reset_font_size.trigger()
588 588
589 589 def guiref_active_frontend(self):
590 590 self.active_frontend.execute("%guiref")
591 591
592 592 def intro_active_frontend(self):
593 593 self.active_frontend.execute("?")
594 594
595 595 def quickref_active_frontend(self):
596 596 self.active_frontend.execute("%quickref")
597 597 #---------------------------------------------------------------------------
598 598 # QWidget interface
599 599 #---------------------------------------------------------------------------
600 600
601 601 def closeEvent(self, event):
602 602 """ Forward the close event to every tabs contained by the windows
603 603 """
604 604 # Do Not loop on the widget count as it change while closing
605 605 widget_list=[ self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
606 606 for widget in widget_list:
607 607 self.close_tab(widget)
608 608 event.accept()
609 609
610 610 #-----------------------------------------------------------------------------
611 611 # Aliases and Flags
612 612 #-----------------------------------------------------------------------------
613 613
614 614 flags = dict(ipkernel_flags)
615 615 qt_flags = {
616 616 'existing' : ({'IPythonQtConsoleApp' : {'existing' : 'kernel*.json'}},
617 617 "Connect to an existing kernel. If no argument specified, guess most recent"),
618 618 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
619 619 "Use a pure Python kernel instead of an IPython kernel."),
620 620 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
621 621 "Disable rich text support."),
622 622 }
623 623 qt_flags.update(boolean_flag(
624 624 'gui-completion', 'ConsoleWidget.gui_completion',
625 625 "use a GUI widget for tab completion",
626 626 "use plaintext output for completion"
627 627 ))
628 628 qt_flags.update(boolean_flag(
629 629 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
630 630 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
631 631 to force a direct exit without any confirmation.
632 632 """,
633 633 """Don't prompt the user when exiting. This will terminate the kernel
634 634 if it is owned by the frontend, and leave it alive if it is external.
635 635 """
636 636 ))
637 637 flags.update(qt_flags)
638 638
639 639 aliases = dict(ipkernel_aliases)
640 640
641 641 qt_aliases = dict(
642 642 hb = 'IPythonQtConsoleApp.hb_port',
643 643 shell = 'IPythonQtConsoleApp.shell_port',
644 644 iopub = 'IPythonQtConsoleApp.iopub_port',
645 645 stdin = 'IPythonQtConsoleApp.stdin_port',
646 646 ip = 'IPythonQtConsoleApp.ip',
647 647 existing = 'IPythonQtConsoleApp.existing',
648 648 f = 'IPythonQtConsoleApp.connection_file',
649 649
650 650 style = 'IPythonWidget.syntax_style',
651 651 stylesheet = 'IPythonQtConsoleApp.stylesheet',
652 652 colors = 'ZMQInteractiveShell.colors',
653 653
654 654 editor = 'IPythonWidget.editor',
655 655 paging = 'ConsoleWidget.paging',
656 656 ssh = 'IPythonQtConsoleApp.sshserver',
657 657 )
658 658 aliases.update(qt_aliases)
659 659
660 660
661 661 #-----------------------------------------------------------------------------
662 662 # IPythonQtConsole
663 663 #-----------------------------------------------------------------------------
664 664
665 665
666 666 class IPythonQtConsoleApp(BaseIPythonApplication):
667 667 name = 'ipython-qtconsole'
668 668 default_config_file_name='ipython_config.py'
669 669
670 670 description = """
671 671 The IPython QtConsole.
672 672
673 673 This launches a Console-style application using Qt. It is not a full
674 674 console, in that launched terminal subprocesses will not be able to accept
675 675 input.
676 676
677 677 The QtConsole supports various extra features beyond the Terminal IPython
678 678 shell, such as inline plotting with matplotlib, via:
679 679
680 680 ipython qtconsole --pylab=inline
681 681
682 682 as well as saving your session as HTML, and printing the output.
683 683
684 684 """
685 685 examples = _examples
686 686
687 687 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
688 688 flags = Dict(flags)
689 689 aliases = Dict(aliases)
690 690
691 691 kernel_argv = List(Unicode)
692 692
693 693 # create requested profiles by default, if they don't exist:
694 694 auto_create = CBool(True)
695 695 # connection info:
696 696 ip = Unicode(LOCALHOST, config=True,
697 697 help="""Set the kernel\'s IP address [default localhost].
698 698 If the IP address is something other than localhost, then
699 699 Consoles on other machines will be able to connect
700 700 to the Kernel, so be careful!"""
701 701 )
702 702
703 703 sshserver = Unicode('', config=True,
704 704 help="""The SSH server to use to connect to the kernel.""")
705 705 sshkey = Unicode('', config=True,
706 706 help="""Path to the ssh key to use for logging in to the ssh server.""")
707 707
708 708 hb_port = Int(0, config=True,
709 709 help="set the heartbeat port [default: random]")
710 710 shell_port = Int(0, config=True,
711 711 help="set the shell (XREP) port [default: random]")
712 712 iopub_port = Int(0, config=True,
713 713 help="set the iopub (PUB) port [default: random]")
714 714 stdin_port = Int(0, config=True,
715 715 help="set the stdin (XREQ) port [default: random]")
716 716 connection_file = Unicode('', config=True,
717 717 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
718 718
719 719 This file will contain the IP, ports, and authentication key needed to connect
720 720 clients to this kernel. By default, this file will be created in the security-dir
721 721 of the current profile, but can be specified by absolute path.
722 722 """)
723 723 def _connection_file_default(self):
724 724 return 'kernel-%i.json' % os.getpid()
725 725
726 726 existing = Unicode('', config=True,
727 727 help="""Connect to an already running kernel""")
728 728
729 729 stylesheet = Unicode('', config=True,
730 730 help="path to a custom CSS stylesheet")
731 731
732 732 pure = CBool(False, config=True,
733 733 help="Use a pure Python kernel instead of an IPython kernel.")
734 734 plain = CBool(False, config=True,
735 735 help="Use a plaintext widget instead of rich text (plain can't print/save).")
736 736
737 737 def _pure_changed(self, name, old, new):
738 738 kind = 'plain' if self.plain else 'rich'
739 739 self.config.ConsoleWidget.kind = kind
740 740 if self.pure:
741 741 self.widget_factory = FrontendWidget
742 742 elif self.plain:
743 743 self.widget_factory = IPythonWidget
744 744 else:
745 745 self.widget_factory = RichIPythonWidget
746 746
747 747 _plain_changed = _pure_changed
748 748
749 749 confirm_exit = CBool(True, config=True,
750 750 help="""
751 751 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
752 752 to force a direct exit without any confirmation.""",
753 753 )
754 754
755 755 # the factory for creating a widget
756 756 widget_factory = Any(RichIPythonWidget)
757 757
758 758 def parse_command_line(self, argv=None):
759 759 super(IPythonQtConsoleApp, self).parse_command_line(argv)
760 760 if argv is None:
761 761 argv = sys.argv[1:]
762 762
763 763 self.kernel_argv = list(argv) # copy
764 764 # kernel should inherit default config file from frontend
765 765 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
766 766 # Scrub frontend-specific flags
767 767 for a in argv:
768 768 if a.startswith('-') and a.lstrip('-') in qt_flags:
769 769 self.kernel_argv.remove(a)
770 770 swallow_next = False
771 771 for a in argv:
772 772 if swallow_next:
773 773 self.kernel_argv.remove(a)
774 774 swallow_next = False
775 775 continue
776 776 if a.startswith('-'):
777 777 split = a.lstrip('-').split('=')
778 778 alias = split[0]
779 779 if alias in qt_aliases:
780 780 self.kernel_argv.remove(a)
781 781 if len(split) == 1:
782 782 # alias passed with arg via space
783 783 swallow_next = True
784 784
785 785 def init_connection_file(self):
786 786 """find the connection file, and load the info if found.
787 787
788 788 The current working directory and the current profile's security
789 789 directory will be searched for the file if it is not given by
790 790 absolute path.
791 791
792 792 When attempting to connect to an existing kernel and the `--existing`
793 793 argument does not match an existing file, it will be interpreted as a
794 794 fileglob, and the matching file in the current profile's security dir
795 795 with the latest access time will be used.
796 796 """
797 797 if self.existing:
798 798 try:
799 799 cf = find_connection_file(self.existing)
800 800 except Exception:
801 801 self.log.critical("Could not find existing kernel connection file %s", self.existing)
802 802 self.exit(1)
803 803 self.log.info("Connecting to existing kernel: %s" % cf)
804 804 self.connection_file = cf
805 805 # should load_connection_file only be used for existing?
806 806 # as it is now, this allows reusing ports if an existing
807 807 # file is requested
808 808 try:
809 809 self.load_connection_file()
810 810 except Exception:
811 811 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
812 812 self.exit(1)
813 813
814 814 def load_connection_file(self):
815 815 """load ip/port/hmac config from JSON connection file"""
816 816 # this is identical to KernelApp.load_connection_file
817 817 # perhaps it can be centralized somewhere?
818 818 try:
819 819 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
820 820 except IOError:
821 821 self.log.debug("Connection File not found: %s", self.connection_file)
822 822 return
823 823 self.log.debug(u"Loading connection file %s", fname)
824 824 with open(fname) as f:
825 825 s = f.read()
826 826 cfg = json.loads(s)
827 827 if self.ip == LOCALHOST and 'ip' in cfg:
828 828 # not overridden by config or cl_args
829 829 self.ip = cfg['ip']
830 830 for channel in ('hb', 'shell', 'iopub', 'stdin'):
831 831 name = channel + '_port'
832 832 if getattr(self, name) == 0 and name in cfg:
833 833 # not overridden by config or cl_args
834 834 setattr(self, name, cfg[name])
835 835 if 'key' in cfg:
836 836 self.config.Session.key = str_to_bytes(cfg['key'])
837 837
838 838 def init_ssh(self):
839 839 """set up ssh tunnels, if needed."""
840 840 if not self.sshserver and not self.sshkey:
841 841 return
842 842
843 843 if self.sshkey and not self.sshserver:
844 844 # specifying just the key implies that we are connecting directly
845 845 self.sshserver = self.ip
846 846 self.ip = LOCALHOST
847 847
848 848 # build connection dict for tunnels:
849 849 info = dict(ip=self.ip,
850 850 shell_port=self.shell_port,
851 851 iopub_port=self.iopub_port,
852 852 stdin_port=self.stdin_port,
853 853 hb_port=self.hb_port
854 854 )
855 855
856 856 self.log.info("Forwarding connections to %s via %s"%(self.ip, self.sshserver))
857 857
858 858 # tunnels return a new set of ports, which will be on localhost:
859 859 self.ip = LOCALHOST
860 860 try:
861 861 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
862 862 except:
863 863 # even catch KeyboardInterrupt
864 864 self.log.error("Could not setup tunnels", exc_info=True)
865 865 self.exit(1)
866 866
867 867 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
868 868
869 869 cf = self.connection_file
870 870 base,ext = os.path.splitext(cf)
871 871 base = os.path.basename(base)
872 872 self.connection_file = os.path.basename(base)+'-ssh'+ext
873 873 self.log.critical("To connect another client via this tunnel, use:")
874 874 self.log.critical("--existing %s" % self.connection_file)
875 875
876 876 def _new_connection_file(self):
877 877 return os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % uuid.uuid4())
878 878
879 879 def init_kernel_manager(self):
880 880 # Don't let Qt or ZMQ swallow KeyboardInterupts.
881 881 signal.signal(signal.SIGINT, signal.SIG_DFL)
882 882 sec = self.profile_dir.security_dir
883 883 try:
884 884 cf = filefind(self.connection_file, ['.', sec])
885 885 except IOError:
886 886 # file might not exist
887 887 if self.connection_file == os.path.basename(self.connection_file):
888 888 # just shortname, put it in security dir
889 889 cf = os.path.join(sec, self.connection_file)
890 890 else:
891 891 cf = self.connection_file
892 892
893 893 # Create a KernelManager and start a kernel.
894 894 self.kernel_manager = QtKernelManager(
895 895 ip=self.ip,
896 896 shell_port=self.shell_port,
897 897 iopub_port=self.iopub_port,
898 898 stdin_port=self.stdin_port,
899 899 hb_port=self.hb_port,
900 900 connection_file=cf,
901 901 config=self.config,
902 902 )
903 903 # start the kernel
904 904 if not self.existing:
905 905 kwargs = dict(ipython=not self.pure)
906 906 kwargs['extra_arguments'] = self.kernel_argv
907 907 self.kernel_manager.start_kernel(**kwargs)
908 908 elif self.sshserver:
909 909 # ssh, write new connection file
910 910 self.kernel_manager.write_connection_file()
911 911 self.kernel_manager.start_channels()
912 912
913 913 def create_tab_with_new_frontend(self):
914 914 """ Create new tab attached to new kernel, launched on localhost.
915 915 """
916 916 ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST
917 917 kernel_manager = QtKernelManager(
918 918 ip=ip,
919 919 connection_file=self._new_connection_file(),
920 920 config=self.config,
921 921 )
922 922 # start the kernel
923 923 kwargs = dict(ipython=not self.pure)
924 924 kwargs['extra_arguments'] = self.kernel_argv
925 925 kernel_manager.start_kernel(**kwargs)
926 926 kernel_manager.start_channels()
927 927 widget = self.widget_factory(config=self.config,
928 928 local_kernel=True)
929 929 widget.kernel_manager = kernel_manager
930 930 widget._existing=False;
931 931 widget._confirm_exit=True;
932 932 widget._may_close=True;
933 933 self.window.add_tab_with_frontend(widget)
934 934
935 935 def create_tab_attached_to_current_tab_kernel(self):
936 936 current_widget = self.window.tab_widget.currentWidget()
937 937 current_widget_index = self.window.tab_widget.indexOf(current_widget)
938 938 current_widget.kernel_manager = current_widget.kernel_manager;
939 939 current_widget_name = self.window.tab_widget.tabText(current_widget_index);
940 940 kernel_manager = QtKernelManager(
941 941 connection_file=current_widget.kernel_manager.connection_file,
942 942 config = self.config,
943 943 )
944 944 kernel_manager.load_connection_file()
945 945 kernel_manager.start_channels()
946 946 widget = self.widget_factory(config=self.config,
947 947 local_kernel=False)
948 948 widget._confirm_exit=True;
949 949 widget._may_close=False;
950 950 widget.kernel_manager = kernel_manager
951 951 self.window.add_tab_with_frontend(widget,name=str('('+current_widget_name+') slave'))
952 952
953 953 def init_qt_elements(self):
954 954 # Create the widget.
955 955 self.app = QtGui.QApplication([])
956 956
957 957 base_path = os.path.abspath(os.path.dirname(__file__))
958 958 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
959 959 self.app.icon = QtGui.QIcon(icon_path)
960 960 QtGui.QApplication.setWindowIcon(self.app.icon)
961 961
962 962 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
963 963 self.widget = self.widget_factory(config=self.config,
964 964 local_kernel=local_kernel)
965 965 self.widget._existing = self.existing;
966 966 self.widget._may_close = not self.existing;
967 967 self.widget._confirm_exit = not self.existing;
968 968
969 969 self.widget.kernel_manager = self.kernel_manager
970 970 self.window = MainWindow(self.app, self.widget, self.existing,
971 971 may_close=local_kernel,
972 972 confirm_exit=self.confirm_exit)
973 973 self.window.log = self.log
974 974 self.window.add_tab_with_frontend(self.widget)
975 975 self.window.init_menu_bar()
976 976 self.window.setWindowTitle('Python' if self.pure else 'IPython')
977 977
978 978 def init_colors(self):
979 979 """Configure the coloring of the widget"""
980 980 # Note: This will be dramatically simplified when colors
981 981 # are removed from the backend.
982 982
983 983 if self.pure:
984 984 # only IPythonWidget supports styling
985 985 return
986 986
987 987 # parse the colors arg down to current known labels
988 988 try:
989 989 colors = self.config.ZMQInteractiveShell.colors
990 990 except AttributeError:
991 991 colors = None
992 992 try:
993 993 style = self.config.IPythonWidget.colors
994 994 except AttributeError:
995 995 style = None
996 996
997 997 # find the value for colors:
998 998 if colors:
999 999 colors=colors.lower()
1000 1000 if colors in ('lightbg', 'light'):
1001 1001 colors='lightbg'
1002 1002 elif colors in ('dark', 'linux'):
1003 1003 colors='linux'
1004 1004 else:
1005 1005 colors='nocolor'
1006 1006 elif style:
1007 1007 if style=='bw':
1008 1008 colors='nocolor'
1009 1009 elif styles.dark_style(style):
1010 1010 colors='linux'
1011 1011 else:
1012 1012 colors='lightbg'
1013 1013 else:
1014 1014 colors=None
1015 1015
1016 1016 # Configure the style.
1017 1017 widget = self.widget
1018 1018 if style:
1019 1019 widget.style_sheet = styles.sheet_from_template(style, colors)
1020 1020 widget.syntax_style = style
1021 1021 widget._syntax_style_changed()
1022 1022 widget._style_sheet_changed()
1023 1023 elif colors:
1024 1024 # use a default style
1025 1025 widget.set_default_style(colors=colors)
1026 1026 else:
1027 1027 # this is redundant for now, but allows the widget's
1028 1028 # defaults to change
1029 1029 widget.set_default_style()
1030 1030
1031 1031 if self.stylesheet:
1032 1032 # we got an expicit stylesheet
1033 1033 if os.path.isfile(self.stylesheet):
1034 1034 with open(self.stylesheet) as f:
1035 1035 sheet = f.read()
1036 1036 widget.style_sheet = sheet
1037 1037 widget._style_sheet_changed()
1038 1038 else:
1039 1039 raise IOError("Stylesheet %r not found."%self.stylesheet)
1040 1040
1041 1041 def initialize(self, argv=None):
1042 1042 super(IPythonQtConsoleApp, self).initialize(argv)
1043 1043 self.init_connection_file()
1044 1044 default_secure(self.config)
1045 1045 self.init_ssh()
1046 1046 self.init_kernel_manager()
1047 1047 self.init_qt_elements()
1048 1048 self.init_colors()
1049 1049 self.init_window_shortcut()
1050 1050
1051 1051 def init_window_shortcut(self):
1052 1052
1053 1053 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
1054 1054 self.window,
1055 1055 shortcut="Ctrl+PgDown",
1056 1056 statusTip="Cahange to next tab",
1057 1057 triggered=self.window.prev_tab)
1058 1058
1059 1059 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
1060 1060 self.window,
1061 1061 shortcut="Ctrl+PgUp",
1062 1062 statusTip="Cahange to next tab",
1063 1063 triggered=self.window.next_tab)
1064 1064
1065 1065 self.fullScreenAct = QtGui.QAction("&Full Screen",
1066 1066 self.window,
1067 1067 shortcut="Ctrl+Meta+Space",
1068 1068 statusTip="Toggle between Fullscreen and Normal Size",
1069 1069 triggered=self.toggleFullScreen)
1070 1070
1071 1071
1072 1072
1073 1073 self.tabAndNewKernelAct =QtGui.QAction("Tab with &New kernel",
1074 1074 self.window,
1075 1075 shortcut="Ctrl+T",
1076 1076 triggered=self.create_tab_with_new_frontend)
1077 1077 self.window.kernel_menu.addAction(self.tabAndNewKernelAct)
1078 1078
1079 1079 self.tabSameKernalAct =QtGui.QAction("Tab with Sa&me kernel",
1080 1080 self.window,
1081 1081 shortcut="Ctrl+Shift+T",
1082 1082 triggered=self.create_tab_attached_to_current_tab_kernel)
1083 1083 self.window.kernel_menu.addAction(self.tabSameKernalAct)
1084 1084 self.window.kernel_menu.addSeparator()
1085 1085
1086 1086 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
1087 1087 self.window,
1088 1088 triggered=self._open_online_help)
1089 1089 self.window.help_menu.addAction(self.onlineHelpAct)
1090 1090 # creating shortcut in menubar only for Mac OS as I don't
1091 1091 # know the shortcut or if the windows manager assign it in
1092 1092 # other platform.
1093 1093 if sys.platform == 'darwin':
1094 1094 self.minimizeAct = QtGui.QAction("Mini&mize",
1095 1095 self.window,
1096 1096 shortcut="Ctrl+m",
1097 1097 statusTip="Minimize the window/Restore Normal Size",
1098 1098 triggered=self.toggleMinimized)
1099 1099 self.maximizeAct = QtGui.QAction("Ma&ximize",
1100 1100 self.window,
1101 1101 shortcut="Ctrl+Shift+M",
1102 1102 statusTip="Maximize the window/Restore Normal Size",
1103 1103 triggered=self.toggleMaximized)
1104 1104
1105 1105
1106 1106 self.window_menu = self.window.window_menu
1107 1107 self.kernel_menu = self.window.kernel_menu
1108 1108
1109 1109 self.kernel_menu.addAction(self.next_tab_act)
1110 1110 self.kernel_menu.addAction(self.prev_tab_act)
1111 1111 self.window_menu.addSeparator()
1112 1112 self.window_menu.addAction(self.minimizeAct)
1113 1113 self.window_menu.addAction(self.maximizeAct)
1114 1114 self.window_menu.addSeparator()
1115 1115 self.window_menu.addAction(self.fullScreenAct)
1116 1116
1117 1117 else:
1118 1118 # if we don't put it in a menu, we add it to the window so
1119 1119 # that it can still be triggerd by shortcut
1120 1120 self.window.addAction(self.fullScreenAct)
1121 1121
1122 1122 # Don't activate toggleMenubar on mac, doen't work,
1123 1123 # as toolbar always here
1124 1124 self.toggle_menu_bar_act = QtGui.QAction("&Toggle Menu Bar",
1125 1125 self.window,
1126 1126 shortcut="Ctrl+Meta+H",
1127 1127 statusTip="Toggle menubar betwin visible and not",
1128 1128 triggered=self.toggle_menu_bar)
1129 self.window_menu.addAction(self.toggle_menu_bar_act)
1129 self.window.window_menu.addAction(self.toggle_menu_bar_act)
1130 1130
1131 1131 def toggle_menu_bar(self):
1132 1132 menu_bar = self.window.menuBar();
1133 1133 if not menu_bar.isVisible():
1134 1134 menu_bar.setVisible(False)
1135 1135 else:
1136 1136 menu_bar.setVisible(True)
1137 1137
1138 1138 def toggleMinimized(self):
1139 1139 if not self.window.isMinimized():
1140 1140 self.window.showMinimized()
1141 1141 else:
1142 1142 self.window.showNormal()
1143 1143
1144 1144 def _open_online_help(self):
1145 1145 filename="http://ipython.org/ipython-doc/stable/index.html"
1146 1146 webbrowser.open(filename, new=1, autoraise=True)
1147 1147
1148 1148 def toggleMaximized(self):
1149 1149 if not self.window.isMaximized():
1150 1150 self.window.showMaximized()
1151 1151 else:
1152 1152 self.window.showNormal()
1153 1153
1154 1154 # Min/Max imizing while in full screen give a bug
1155 1155 # when going out of full screen, at least on OSX
1156 1156 def toggleFullScreen(self):
1157 1157 if not self.window.isFullScreen():
1158 1158 self.window.showFullScreen()
1159 1159 if sys.platform == 'darwin':
1160 1160 self.maximizeAct.setEnabled(False)
1161 1161 self.minimizeAct.setEnabled(False)
1162 1162 else:
1163 1163 self.window.showNormal()
1164 1164 if sys.platform == 'darwin':
1165 1165 self.maximizeAct.setEnabled(True)
1166 1166 self.minimizeAct.setEnabled(True)
1167 1167
1168 1168 def start(self):
1169 1169
1170 1170 # draw the window
1171 1171 self.window.show()
1172 1172
1173 1173 # Start the application main loop.
1174 1174 self.app.exec_()
1175 1175
1176 1176 #-----------------------------------------------------------------------------
1177 1177 # Main entry point
1178 1178 #-----------------------------------------------------------------------------
1179 1179
1180 1180 def main():
1181 1181 app = IPythonQtConsoleApp()
1182 1182 app.initialize()
1183 1183 app.start()
1184 1184
1185 1185
1186 1186 if __name__ == '__main__':
1187 1187 main()
General Comments 0
You need to be logged in to leave comments. Login now