Ejemplo n.º 1
0
def main():
    # Print the ID of the main process
    print_process_id()

    app = guisupport.get_app_qt4()

    # Create an in-process kernel
    # >>> print_process_id()
    # will print the same process ID as the main process
    kernel_manager = QtInProcessKernelManager()
    kernel_manager.start_kernel()
    kernel = kernel_manager.kernel
    kernel.gui = 'qt4'
    kernel.shell.push({'foo': 43, 'print_process_id': print_process_id})

    kernel_client = kernel_manager.client()
    kernel_client.start_channels()

    def stop():
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel()
        app.exit()

    control = RichIPythonWidget()
    control.kernel_manager = kernel_manager
    control.kernel_client = kernel_client
    control.exit_requested.connect(stop)
    control.show()

    guisupport.start_event_loop_qt4(app)
Ejemplo n.º 2
0
def main():
    """Start kernel manager and client, create window, run app event loop"""
    app = guisupport.get_app_qt4()

    if INPROCESS:
        from IPython.qt.inprocess import QtInProcessKernelManager
        km = QtInProcessKernelManager()
    else:
        from IPython.qt.manager import QtKernelManager
        km = QtKernelManager()
    km.start_kernel()
    km.kernel.gui = 'qt4'
    kc = km.client()
    kc.start_channels()

    widget = RichIPythonWidget()
    widget.kernel_manager = km
    widget.kernel_client = kc
    if CLEANSHUTDOWN: # slow exit on CTRL+D
        def stop():
            kc.stop_channels()
            km.shutdown_kernel()
            app.exit()
        widget.exit_requested.connect(stop)
    else: # fast exit on CTRL+D
        widget.exit_requested.connect(app.quit)
    widget.show()
    guisupport.start_event_loop_qt4(app)
def main():
    # Print the ID of the main process
    print_process_id()

    app = guisupport.get_app_qt4()

    # Create an in-process kernel
    # >>> print_process_id()
    # will print the same process ID as the main process
    kernel_manager = QtAsyncInProcessKernelManager()
    kernel_manager.start_kernel()
    kernel = kernel_manager.kernel
    kernel.gui = 'qt4'
    kernel.shell.push({'foo': 43, 'print_process_id': print_process_id})

    kernel_client = kernel_manager.client()
    kernel_client.start_channels()

    def stop():
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel()
        app.exit()

    control = RichIPythonWidget()
    control.kernel_manager = kernel_manager
    control.kernel_client = kernel_client
    control.exit_requested.connect(stop)
    control.show()

    guisupport.start_event_loop_qt4(app)
Ejemplo n.º 4
0
def show_ipython_console():
    # from https://github.com/ipython/ipython/blob/1.x/examples/inprocess/embedded_qtconsole.py
    # this might be able to be a dockable panel at some point in the future.
    # it should also only allow one window open at a time - I think it steals stdout at start
    # and opening a new window stops output working on the old one!
    app = guisupport.get_app_qt4()

    # Create an in-process kernel
    # >>> print_process_id()
    # will print the same process ID as the main process
    kernel_manager = QtInProcessKernelManager()
    kernel_manager.start_kernel()
    kernel = kernel_manager.kernel
    kernel.gui = 'qt4'

    kernel_client = kernel_manager.client()
    kernel_client.start_channels()

    def stop():
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel()
        app.exit()

    control = RichIPythonWidget()
    control.kernel_manager = kernel_manager
    control.kernel_client = kernel_client
    control.exit_requested.connect(stop)
    control.show()

    guisupport.start_event_loop_qt4(app)
Ejemplo n.º 5
0
def show_ipython_console():
    # from https://github.com/ipython/ipython/blob/1.x/examples/inprocess/embedded_qtconsole.py
    # this might be able to be a dockable panel at some point in the future.
    # it should also only allow one window open at a time - I think it steals stdout at start
    # and opening a new window stops output working on the old one!
    app = guisupport.get_app_qt4()

    # Create an in-process kernel
    # >>> print_process_id()
    # will print the same process ID as the main process
    kernel_manager = QtInProcessKernelManager()
    kernel_manager.start_kernel()
    kernel = kernel_manager.kernel
    kernel.gui = 'qt4'

    kernel_client = kernel_manager.client()
    kernel_client.start_channels()

    def stop():
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel()
        app.exit()

    control = RichIPythonWidget()
    control.kernel_manager = kernel_manager
    control.kernel_client = kernel_client
    control.exit_requested.connect(stop)
    control.show()

    guisupport.start_event_loop_qt4(app)
Ejemplo n.º 6
0
def main():

    #LETS ALSO DO SOME IPYTHOPN PARRALLEL STUFF

    global splicer
    app = guisupport.get_app_qt4()
    from LZM100 import splicer
    # Create an in-process kernel
    # >>> print_process_id()
    # will print the same process ID as the main process
    kernel_manager = QtInProcessKernelManager()
    kernel_manager.start_kernel()
    kernel = kernel_manager.kernel
    kernel.gui = 'qt4'
    kernel.shell.push({'splicer': splicer})

    kernel_client = kernel_manager.client()
    kernel_client.start_channels()

    def stop():
        kernel_client.stop_channels()
        kernel_manager.shutdown_kernel()
        app.exit()

    control = RichIPythonWidget()
    control.kernel_manager = kernel_manager
    control.kernel_client = kernel_client
    control.exit_requested.connect(stop)
    control.show()

    #app = QtGui.QApplication(sys.argv)
    #ex = Example()
    b = TaperDesign(Taper())
    sys.exit(app.exec_())
Ejemplo n.º 7
0
 def show(self):
     """show() -> None
     Store previous state and starts capturing all interactive input and 
     output.
     
     """
     # capture all interactive input/output
     sys.stdout   = self
     sys.stderr   = self
     sys.stdin    = self
     RichIPythonWidget.show(self)
class INinjaConsole(plugin.Plugin):
    def initialize(self):
        # Init your plugin
        self.misc_s = self.locator.get_service('misc')
        explorer_container = self.locator.get_service('explorer')
        tree = explorer_container.get_tree_projects()


        kernel_manager = QtInProcessKernelManager()
        kernel_manager.start_kernel()
        kernel = kernel_manager.kernel
        kernel.gui = 'qt4'

        kernel_client = kernel_manager.client()
        kernel_client.start_channels()

        def stop():
            kernel_client.stop_channels()
            kernel_manager.shutdown_kernel()

        self.ipython_console = RichIPythonWidget()
        self.ipython_console.kernel_manager = kernel_manager
        self.ipython_console.kernel_client = kernel_client
        self.ipython_console.exit_requested.connect(stop)
        self.ipython_console.show()


        self.misc_s.add_widget(self.ipython_console, IMAGES["console"],
                                "IPython console")
        addp = SIGNAL("addProjectToConsole(QString)")
        delp = SIGNAL("removeProjectFromConsole(QString)")
        self.connect(tree, addp, self._add_project)
        self.connect(tree, delp, self._del_project)

    def _add_project(self, project_path):
        self.ipython_console.execute("import sys; sys.path += ['%s']" %
                                    project_path)

    def _del_project(self, project_path):
        """This is ugly, but I was in a hurry and copied form console
        widget"""
        self.ipython_console.execute("import sys; "
            "sys.path = [path for path in sys.path "
            "if path != '%s']" % project_path)

    def finish(self):
        # Shutdown your plugin
        pass

    def get_preferences_widget(self):
        # Return a widget for customize your plugin
        pass
class INinjaConsole(plugin.Plugin):
    def initialize(self):
        # Init your plugin
        self.misc_s = self.locator.get_service('misc')
        explorer_container = self.locator.get_service('explorer')
        tree = explorer_container.get_tree_projects()

        kernel_manager = QtInProcessKernelManager()
        kernel_manager.start_kernel()
        kernel = kernel_manager.kernel
        kernel.gui = 'qt4'

        kernel_client = kernel_manager.client()
        kernel_client.start_channels()

        def stop():
            kernel_client.stop_channels()
            kernel_manager.shutdown_kernel()

        self.ipython_console = RichIPythonWidget()
        self.ipython_console.kernel_manager = kernel_manager
        self.ipython_console.kernel_client = kernel_client
        self.ipython_console.exit_requested.connect(stop)
        self.ipython_console.show()

        self.misc_s.add_widget(self.ipython_console, IMAGES["console"],
                               "IPython console")
        addp = SIGNAL("addProjectToConsole(QString)")
        delp = SIGNAL("removeProjectFromConsole(QString)")
        self.connect(tree, addp, self._add_project)
        self.connect(tree, delp, self._del_project)

    def _add_project(self, project_path):
        self.ipython_console.execute("import sys; sys.path += ['%s']" %
                                     project_path)

    def _del_project(self, project_path):
        """This is ugly, but I was in a hurry and copied form console
        widget"""
        self.ipython_console.execute("import sys; "
                                     "sys.path = [path for path in sys.path "
                                     "if path != '%s']" % project_path)

    def finish(self):
        # Shutdown your plugin
        pass

    def get_preferences_widget(self):
        # Return a widget for customize your plugin
        pass
Ejemplo n.º 10
0
class IPythonInProcess(object):
    def __init__(self,common,customBanner=None):
        if customBanner!=None: self.banner=customBanner
        self.common = common
        self.kernel_manager = None
        self.kernel = None
        self.control = None
        self.kernel_client = None
        self.init_qtconsole()
        
    def init_qtconsole(self):
        self.main()
    
    def print_process_id(self):
        print('Process ID is:', os.getpid())
    
    
    def main(self):
        # Print the ID of the main process
        #self.print_process_id()
    
        #app = guisupport.get_app_qt4()
    
        # Create an in-process kernel
        # >>> print_process_id()
        # will print the same process ID as the main process
        self.kernel_manager = QtInProcessKernelManager()
        self.kernel_manager.start_kernel()
        self.kernel = self.kernel_manager.kernel
        self.kernel.gui = 'qt4'
        self.kernel.shell.push(self.common.__dict__)
        self.kernel_client = self.kernel_manager.client()
        self.kernel_client.start_channels()
        
        def stop():
            self.kernel_client.stop_channels()
            self.kernel_manager.shutdown_kernel()
            #app.exit()
    
        self.control = RichIPythonWidget(banner = self.banner)
        self.control.kernel_manager = self.kernel_manager
        self.control.kernel_client = self.kernel_client
        self.control.exit_requested.connect(stop)
        
        #start widget with certain inputs:
        #import pylab, which includes all numpy; import pandas as pd
        #second argument is whether the execution is hidden e.g. whether a line is used
        #I have turned on hide.
        
        self.control._execute('import pylab as pl; import pandas as pd',True)
        
    def SHOW(self):
        self.control.show()
    
        #guisupport.start_event_loop_qt4(app)

    def pushVariables(self,variableDict):
        """ Given a dictionary containing name / value pairs, push those variables to the IPython console widget """
        self.kernel_manager.kernel.shell.push(variableDict)
    def clearTerminal(self):
        """ Clears the terminal """
        self._control.clear()
    def printText(self,text):
        """ Prints some plain text to the console """
        self._append_plain_text(text)        
    def executeCommand(self,command):
        """ Execute a command in the frame of the console widget """
        self.control._execute(command,False)
Ejemplo n.º 11
0
def console(client, args):

	local = {
		'client': client,
		'auth': client.auth,
		'reset': client.reset,
		'config': client.config,
		'execute': client.execute,
	}

	if args['scriptName']:
		#############################################################
		# EXECUTE FILE                                              #
		#############################################################

		sys.argv = []
		sys.argv.append(args['scriptName'])
		sys.argv.extend(args['scriptArgs'])

		try:
			f = open(sys.argv[0])
			source_code = f.read()
			f.close()

		except IOError as e:
			raise pyAMI.exception.Error(e)

		compiled_code = compile(source_code, sys.argv[0], 'exec')

		safe_exec(compiled_code, globals(), local)

		#############################################################

	else:
		#############################################################
		# RUN CONSOLE                                               #
		#############################################################

		sys.argv = [sys.argv[0]]

		#############################################################

		banner = pyAMI.config.banner if args['nosplash'] == False else ''

		#############################################################

		if args['gIPython']:
			#####################################################
			# IPYTHON GRAPHICAL MODE                            #
			#####################################################

			try:
				from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
				from IPython.qt.inprocess import QtInProcessKernelManager
				from IPython.lib import guisupport

			except ImportError as e:
				raise pyAMI.exception.Error('module `IPython` not properly installed: %s' % e)

			################################
			# HACK IPYTHON BANNER          #
			################################

			RichIPythonWidget._banner_default = lambda self: banner

			################################
			# KERNEL_MANAGER               #
			################################

			kernel_manager = QtInProcessKernelManager()
			kernel_manager.start_kernel()

			################################
			# KERNEL_CLIENT                #
			################################

			kernel_client = kernel_manager.client()
			kernel_client.start_channels()

			################################
			# PYAMI                        #
			################################

			kernel_manager.kernel.shell.push(local)

			################################
			# APPLICATION                  #
			################################

			a = guisupport.get_app_qt4()

			def stop():
				kernel_client.stop_channels()
				kernel_manager.shutdown_kernel()
				a.exit()

			control = RichIPythonWidget()
			control.kernel_manager = kernel_manager
			control.kernel_client = kernel_client
			control.exit_requested.connect(stop)
			control.show()

			guisupport.start_event_loop_qt4(a)

			#####################################################

		elif args['cIPython']:
			#####################################################
			# IPYTHON CONSOLE MODE                              #
			#####################################################

			try:
				from IPython.terminal.embed import InteractiveShellEmbed

			except ImportError as e:
				raise pyAMI.exception.Error('module `IPython` not properly installed: %s' % e)

			################################
			# SHELL                        #
			################################

			shell = InteractiveShellEmbed(banner1 = banner, banner2 = None)

			shell(local_ns = local)

			#####################################################

		else:
			#####################################################
			# DEFAULT MODE                                      #
			#####################################################

			code.interact(banner = banner, local = local)

			#####################################################

	return 0
Ejemplo n.º 12
0
class DataViewer(QtWidgets.QMainWindow):
    """
    The class is used by instantiating and then entering the main Qt loop with, e.g.:
        app = DataViewer(sys.argv)
        app.exec_()
    """
    def __init__(self, argv):
        """
        Initialize class, setting up windows and widgets.
        """
        # Define this as the QApplication object
        self.qtapp = QtWidgets.QApplication.instance()
        if not self.qtapp:
            self.qtapp = QtWidgets.QApplication(argv)

        QtWidgets.QMainWindow.__init__(self)
        self.this_dir, self.this_filename = os.path.split(__file__)

        # Make settings collection
        self.settings = LQCollection()

        # Set up sub-windows and arrange into primary py4DSTEM window
        self.diffraction_space_widget = self.setup_diffraction_space_widget()
        self.real_space_widget = self.setup_real_space_widget()
        self.control_widget = self.setup_control_widget()
        self.console_widget = self.setup_console_widget()
        self.main_window = self.setup_main_window()

        # Set up temporary datacube
        self.datacube = DataCube(data=np.zeros((10, 10, 10, 10)))

        # Set up initial views in real and diffraction space
        self.update_diffraction_space_view()
        self.update_virtual_detector_shape()
        self.update_virtual_detector_mode()
        self.update_real_space_view()
        self.diffraction_space_widget.ui.normDivideRadio.setChecked(True)
        self.diffraction_space_widget.normRadioChanged()

        return

    ###############################################
    ############ Widget setup methods #############
    ###############################################

    def setup_control_widget(self):
        """
        Set up the control window for diffraction space.
        """
        #self.control_widget = load_qt_ui_file(sibling_path(__file__, "control_widget.ui"))
        self.control_widget = ControlPanel()
        self.control_widget.setWindowTitle("Control Panel")

        ############################ Controls ###############################
        # For each control:                                                 #
        #   -creates items in self.settings                                 #
        #   -connects UI changes to updates in self.settings                #
        #   -connects updates in self.settings items to function calls      #
        #   -connects button clicks to function calls                       #
        #####################################################################

        # Load
        self.settings.New('data_filename', dtype='file')
        self.settings.data_filename.connect_to_browse_widgets(
            self.control_widget.lineEdit_LoadFile,
            self.control_widget.pushButton_BrowseFiles)
        self.settings.data_filename.updated_value.connect(self.load_file)

        # Preprocess
        self.settings.New('R_Nx', dtype=int, initial=1)
        self.settings.New('R_Ny', dtype=int, initial=1)
        self.settings.New('bin_r', dtype=int, initial=1)
        self.settings.New('bin_q', dtype=int, initial=1)
        self.settings.New('crop_r_showROI', dtype=bool, initial=False)
        self.settings.New('crop_q_showROI', dtype=bool, initial=False)
        self.settings.New('isCropped_r', dtype=bool, initial=False)
        self.settings.New('isCropped_q', dtype=bool, initial=False)
        self.settings.New('crop_rx_min', dtype=int, initial=0)
        self.settings.New('crop_rx_max', dtype=int, initial=0)
        self.settings.New('crop_ry_min', dtype=int, initial=0)
        self.settings.New('crop_ry_max', dtype=int, initial=0)
        self.settings.New('crop_qx_min', dtype=int, initial=0)
        self.settings.New('crop_qx_max', dtype=int, initial=0)
        self.settings.New('crop_qy_min', dtype=int, initial=0)
        self.settings.New('crop_qy_max', dtype=int, initial=0)

        self.settings.R_Nx.connect_bidir_to_widget(
            self.control_widget.spinBox_Nx)
        self.settings.R_Ny.connect_bidir_to_widget(
            self.control_widget.spinBox_Ny)
        self.settings.bin_r.connect_bidir_to_widget(
            self.control_widget.spinBox_Bin_Real)
        self.settings.bin_q.connect_bidir_to_widget(
            self.control_widget.spinBox_Bin_Diffraction)
        self.settings.crop_r_showROI.connect_bidir_to_widget(
            self.control_widget.checkBox_Crop_Real)
        self.settings.crop_q_showROI.connect_bidir_to_widget(
            self.control_widget.checkBox_Crop_Diffraction)

        self.settings.R_Nx.updated_value.connect(self.update_scan_shape_Nx)
        self.settings.R_Ny.updated_value.connect(self.update_scan_shape_Ny)
        self.settings.crop_r_showROI.updated_value.connect(
            self.toggleCropROI_real)
        self.settings.crop_q_showROI.updated_value.connect(
            self.toggleCropROI_diffraction)

        self.control_widget.pushButton_CropData.clicked.connect(self.crop_data)
        self.control_widget.pushButton_BinData.clicked.connect(self.bin_data)
        self.control_widget.pushButton_EditFileMetadata.clicked.connect(
            self.edit_file_metadata)
        self.control_widget.pushButton_EditDirectoryMetadata.clicked.connect(
            self.edit_directory_metadata)
        self.control_widget.pushButton_SaveFile.clicked.connect(self.save_file)
        self.control_widget.pushButton_SaveDirectory.clicked.connect(
            self.save_directory)

        # Virtual detectors
        self.settings.New('virtual_detector_shape', dtype=int, initial=0)
        self.settings.New('virtual_detector_mode', dtype=int, initial=0)

        self.settings.virtual_detector_shape.connect_bidir_to_widget(
            self.control_widget.buttonGroup_DetectorShape)
        self.settings.virtual_detector_mode.connect_bidir_to_widget(
            self.control_widget.buttonGroup_DetectorMode)

        self.settings.virtual_detector_shape.updated_value.connect(
            self.update_virtual_detector_shape)
        self.settings.virtual_detector_mode.updated_value.connect(
            self.update_virtual_detector_mode)

        return self.control_widget

    def setup_diffraction_space_widget(self):
        """
        Set up the diffraction space window.
        """
        # Create pyqtgraph ImageView object
        self.diffraction_space_widget = pg.ImageView()
        self.diffraction_space_widget.setImage(np.zeros((512, 512)))

        # Create virtual detector ROI selector
        self.virtual_detector_roi = pg.RectROI([256, 256], [50, 50],
                                               pen=(3, 9))
        self.diffraction_space_widget.getView().addItem(
            self.virtual_detector_roi)
        self.virtual_detector_roi.sigRegionChangeFinished.connect(
            self.update_real_space_view)

        # Name and return
        self.diffraction_space_widget.setWindowTitle('Diffraction Space')
        return self.diffraction_space_widget

    def setup_real_space_widget(self):
        """
        Set up the real space window.
        """
        # Create pyqtgraph ImageView object
        self.real_space_widget = pg.ImageView()
        self.real_space_widget.setImage(np.zeros((512, 512)))

        # Add point selector connected to displayed diffraction pattern
        self.real_space_point_selector = pg_point_roi(
            self.real_space_widget.getView())
        self.real_space_point_selector.sigRegionChanged.connect(
            self.update_diffraction_space_view)

        # Name and return
        self.real_space_widget.setWindowTitle('Real Space')
        return self.real_space_widget

    def setup_console_widget(self):
        self.kernel_manager = QtInProcessKernelManager()
        self.kernel_manager.start_kernel()
        self.kernel = self.kernel_manager.kernel
        self.kernel.gui = 'qt4'
        self.kernel.shell.push({'np': np, 'app': self})
        self.kernel_client = self.kernel_manager.client()
        self.kernel_client.start_channels()

        self.console_widget = RichJupyterWidget()
        self.console_widget.setWindowTitle("py4DSTEM IPython Console")
        self.console_widget.kernel_manager = self.kernel_manager
        self.console_widget.kernel_client = self.kernel_client

        return self.console_widget

    def setup_main_window(self):
        """
        Setup main window, arranging sub-windows inside
        """
        self.main_window = QtWidgets.QWidget()
        self.main_window.setWindowTitle("py4DSTEM")

        layout_data = QtWidgets.QHBoxLayout()
        layout_data.addWidget(self.diffraction_space_widget, 1)
        layout_data.addWidget(self.real_space_widget, 1)

        layout_data_and_control = QtWidgets.QHBoxLayout()
        layout_data_and_control.addWidget(self.control_widget, 0)
        layout_data_and_control.addLayout(layout_data, 1)
        layout_data_and_control.setSpacing(0)
        layout_data_and_control.setContentsMargins(0, 0, 0, 0)

        self.main_window.setLayout(layout_data_and_control)

        self.main_window.setGeometry(0, 0, 3600, 1600)
        self.console_widget.setGeometry(0, 1800, 1600, 250)
        self.main_window.show()
        self.main_window.raise_()
        self.console_widget.show()
        self.console_widget.raise_()
        return self.main_window

    ##################################################################
    ############## Methods connecting to user inputs #################
    ##################################################################

    ##################################################################
    # In general, these methods collect any relevant user inputs,    #
    # then pass them to functions defined elsewhere, often in e.g.   #
    # the process directory.                                         #
    # Additional functionality here should be avoided, to ensure     #
    # consistent output between processing run through the GUI       #
    # or from the command line.                                      #
    ##################################################################

    ################ Load ################

    def load_file(self):
        """
        Loads a file by creating and storing a DataCube object.
        """
        fname = self.settings.data_filename.val
        print("Loading file", fname)

        # Instantiate DataCube object
        self.datacube = None
        gc.collect()
        self.datacube = read(fname)

        # Update scan shape information
        self.settings.R_Nx.update_value(self.datacube.R_Nx)
        self.settings.R_Ny.update_value(self.datacube.R_Ny)

        # Update data views
        self.update_diffraction_space_view()
        self.update_virtual_detector_shape()
        self.update_virtual_detector_mode()
        self.update_real_space_view()

        # Normalize diffraction space view
        self.diffraction_space_widget.ui.normDivideRadio.setChecked(True)
        self.diffraction_space_widget.normRadioChanged()

        # Set scan size maxima
        self.control_widget.spinBox_Nx.setMaximum(self.datacube.R_N)
        self.control_widget.spinBox_Ny.setMaximum(self.datacube.R_N)

        return

    ############## Preprocess ##############

    ### Scan Shape ###

    def update_scan_shape_Nx(self):
        R_Nx = self.settings.R_Nx.val
        self.settings.R_Ny.update_value(int(self.datacube.R_N / R_Nx))
        R_Ny = self.settings.R_Ny.val

        self.datacube.set_scan_shape(R_Nx, R_Ny)
        self.update_real_space_view()

    def update_scan_shape_Ny(self):
        R_Ny = self.settings.R_Ny.val
        self.settings.R_Nx.update_value(int(self.datacube.R_N / R_Ny))
        R_Nx = self.settings.R_Nx.val

        self.datacube.set_scan_shape(R_Nx, R_Ny)
        self.update_real_space_view()

    ### Crop ###

    def toggleCropROI_real(self, show=True):
        """
        If show=True, makes an RIO.  If False, removes the ROI.
        """
        if show:
            self.crop_roi_real = pg.RectROI(
                [0, 0], [self.datacube.R_Nx, self.datacube.R_Ny],
                pen=(3, 9),
                removable=True,
                translateSnap=True,
                scaleSnap=True)
            self.crop_roi_real.setPen(color='r')
            self.real_space_widget.getView().addItem(self.crop_roi_real)
        else:
            if hasattr(self, 'crop_roi_real'):
                self.real_space_widget.getView().removeItem(self.crop_roi_real)
                self.crop_roi_real = None
            else:
                pass

    def toggleCropROI_diffraction(self, show=True):
        """
        If show=True, makes an RIO.  If False, removes the ROI.
        """
        if show:
            self.crop_roi_diffraction = pg.RectROI(
                [0, 0], [self.datacube.Q_Nx, self.datacube.Q_Ny],
                pen=(3, 9),
                removable=True,
                translateSnap=True,
                scaleSnap=True)
            self.crop_roi_diffraction.setPen(color='r')
            self.diffraction_space_widget.getView().addItem(
                self.crop_roi_diffraction)
        else:
            if hasattr(self, 'crop_roi_diffraction'):
                self.diffraction_space_widget.getView().removeItem(
                    self.crop_roi_diffraction)
                self.crop_roi_diffraction = None
            else:
                pass

    def crop_data(self):

        # Diffraction space
        if self.control_widget.checkBox_Crop_Diffraction.isChecked():
            # Get crop limits from ROI
            slices_q, transforms_q = self.crop_roi_diffraction.getArraySlice(
                self.datacube.data4D[0, 0, :, :],
                self.diffraction_space_widget.getImageItem())
            slice_qx, slice_qy = slices_q
            crop_Qx_min, crop_Qx_max = slice_qx.start, slice_qx.stop - 1
            crop_Qy_min, crop_Qy_max = slice_qy.start, slice_qy.stop - 1
            crop_Qx_min, crop_Qx_max = max(0, crop_Qx_min), min(
                self.datacube.Q_Nx, crop_Qx_max)
            crop_Qy_min, crop_Qy_max = max(0, crop_Qy_min), min(
                self.datacube.Q_Ny, crop_Qy_max)
            # Move ROI selector
            x0, y0 = self.virtual_detector_roi.x(
            ), self.virtual_detector_roi.y()
            x0_len, y0_len = self.virtual_detector_roi.size()
            xf = int(x0 * (crop_Qx_max - crop_Qx_min) / self.datacube.Q_Nx)
            yf = int(y0 * (crop_Qy_max - crop_Qy_min) / self.datacube.Q_Ny)
            xf_len = int(x0_len * (crop_Qx_max - crop_Qx_min) /
                         self.datacube.Q_Nx)
            yf_len = int(y0_len * (crop_Qy_max - crop_Qy_min) /
                         self.datacube.Q_Ny)
            self.virtual_detector_roi.setPos((xf, yf))
            self.virtual_detector_roi.setSize((xf_len, yf_len))
            # Crop data
            self.datacube.crop_data_diffraction(crop_Qx_min, crop_Qx_max,
                                                crop_Qy_min, crop_Qy_max)
            # Update settings
            self.settings.crop_qx_min.update_value(crop_Qx_min)
            self.settings.crop_qx_max.update_value(crop_Qx_max)
            self.settings.crop_qy_min.update_value(crop_Qy_min)
            self.settings.crop_qy_max.update_value(crop_Qy_max)
            self.settings.isCropped_q.update_value(True)
            # Uncheck crop checkbox and remove ROI
            self.control_widget.checkBox_Crop_Diffraction.setChecked(False)
            # Update display
            self.update_diffraction_space_view()
        else:
            self.settings.isCropped_q.update_value(False)

        # Real space
        if self.control_widget.checkBox_Crop_Real.isChecked():
            # Get crop limits from ROI
            slices_r, transforms_r = self.crop_roi_real.getArraySlice(
                self.datacube.data4D[:, :, 0, 0],
                self.real_space_widget.getImageItem())
            slice_rx, slice_ry = slices_r
            crop_Rx_min, crop_Rx_max = slice_rx.start, slice_rx.stop - 1
            crop_Ry_min, crop_Ry_max = slice_ry.start, slice_ry.stop - 1
            crop_Rx_min, crop_Rx_max = max(0, crop_Rx_min), min(
                self.datacube.R_Nx, crop_Rx_max)
            crop_Ry_min, crop_Ry_max = max(0, crop_Ry_min), min(
                self.datacube.R_Ny, crop_Ry_max)
            # Move point selector
            x0, y0 = self.real_space_point_selector.x(
            ), self.real_space_point_selector.y()
            xf = int(x0 * (crop_Rx_max - crop_Rx_min) / self.datacube.R_Nx)
            yf = int(y0 * (crop_Ry_max - crop_Ry_min) / self.datacube.R_Ny)
            self.real_space_point_selector.setPos((xf, yf))
            # Crop data
            self.datacube.crop_data_real(crop_Rx_min, crop_Rx_max, crop_Ry_min,
                                         crop_Ry_max)
            # Update settings
            self.settings.crop_rx_min.update_value(crop_Rx_min)
            self.settings.crop_rx_max.update_value(crop_Rx_max)
            self.settings.crop_ry_min.update_value(crop_Ry_min)
            self.settings.crop_ry_max.update_value(crop_Ry_max)
            self.settings.isCropped_r.update_value(True)
            self.settings.R_Nx.update_value(self.datacube.R_Nx,
                                            send_signal=False)
            self.settings.R_Ny.update_value(self.datacube.R_Ny,
                                            send_signal=False)
            # Uncheck crop checkbox and remove ROI
            self.control_widget.checkBox_Crop_Real.setChecked(False)
            # Update display
            self.update_real_space_view()
        else:
            self.settings.isCropped_r.update_value(False)

    ### Bin ###

    def bin_data(self):
        # Get bin factors from GUI
        bin_factor_Q = self.settings.bin_q.val
        bin_factor_R = self.settings.bin_r.val
        if bin_factor_Q > 1:
            # Move ROI selector
            x0, y0 = self.virtual_detector_roi.x(
            ), self.virtual_detector_roi.y()
            x0_len, y0_len = self.virtual_detector_roi.size()
            xf = int(x0 / bin_factor_Q)
            yf = int(y0 / bin_factor_Q)
            xf_len = int(x0_len / bin_factor_Q)
            yf_len = int(y0_len / bin_factor_Q)
            self.virtual_detector_roi.setPos((xf, yf))
            self.virtual_detector_roi.setSize((xf_len, yf_len))
            # Bin data
            self.datacube.bin_data_diffraction(bin_factor_Q)
            # Update display
            self.update_diffraction_space_view()
        if bin_factor_R > 1:
            # Move point selector
            x0, y0 = self.real_space_point_selector.x(
            ), self.real_space_point_selector.y()
            xf = int(x0 / bin_factor_R)
            yf = int(y0 / bin_factor_R)
            self.real_space_point_selector.setPos((xf, yf))
            # Bin data
            self.datacube.bin_data_real(bin_factor_R)
            # Update settings
            self.settings.R_Nx.update_value(self.datacube.R_Nx,
                                            send_signal=False)
            self.settings.R_Ny.update_value(self.datacube.R_Ny,
                                            send_signal=False)
            # Update display
            self.update_real_space_view()
        # Set bin factors back to 1
        self.settings.bin_q.update_value(1)
        self.settings.bin_r.update_value(1)

    ### Metadata ###

    def edit_file_metadata(self):
        """
        Creates a popup dialog with tabs for different metadata groups, and fields in each
        group with current, editable metadata values.
        """
        # Make widget
        self.EditMetadataWidget = EditMetadataWidget(self.datacube)
        self.EditMetadataWidget.setWindowTitle("Metadata Editor")
        self.EditMetadataWidget.show()
        self.EditMetadataWidget.raise_()

        # Cancel or save
        self.EditMetadataWidget.pushButton_Cancel.clicked.connect(
            self.cancel_editMetadata)
        self.EditMetadataWidget.pushButton_Save.clicked.connect(
            self.save_editMetadata)

    def cancel_editMetadata(self):
        self.EditMetadataWidget.close()

    def save_editMetadata(self):
        print("Updating metadata...")
        for i in range(self.EditMetadataWidget.tabs.count()):
            tab = self.EditMetadataWidget.tabs.widget(i)
            # Get appropriate metadata dict
            tabname = self.EditMetadataWidget.tabs.tabText(i)
            metadata_dict_name = [
                name for name in self.datacube.metadata.__dict__.keys()
                if tabname[1:] in name
            ][0]
            metadata_dict = getattr(self.datacube.metadata, metadata_dict_name)
            for row in tab.layout().children():
                key = row.itemAt(0).widget().text()
                try:
                    value = row.itemAt(1).widget().text()
                except AttributeError:
                    # Catches alternate widget (QPlainTextEdit) in comments tab
                    value = row.itemAt(1).widget().toPlainText()
                try:
                    value = float(value)
                except ValueError:
                    pass
                metadata_dict[key] = value
        self.EditMetadataWidget.close()
        print("Done.")

    def edit_directory_metadata(self):
        print('edit directory metadata pressed')
        pass

    ### Save ###

    def save_file(self):
        """
        Saving files to the .h5 format.
        This method:
            1) opens a separate dialog
            2) puts a name in the "Save as:" field according to the original filename and any
               preprocessing that's been done
            2) Exits with or without saving when 'Save' or 'Cancel' buttons are pressed.
        """
        # Make widget
        save_path = os.path.splitext(
            self.settings.data_filename.val)[0] + '.h5'
        self.save_widget = SaveWidget(save_path)
        self.save_widget.setWindowTitle("Save as...")
        self.save_widget.show()
        self.save_widget.raise_()

        # Cancel or save
        self.save_widget.pushButton_Cancel.clicked.connect(self.cancel_saveas)
        self.save_widget.pushButton_Execute.clicked.connect(
            self.execute_saveas)

    def cancel_saveas(self):
        self.save_widget.close()

    def execute_saveas(self):
        f = self.save_widget.lineEdit_SavePath.text()
        print("Saving file to {}".format(f))
        save_dataobject(self.datacube, f)
        self.save_widget.close()

    def save_directory(self):
        print('save directory metadata pressed')
        pass

    ################# Virtual Detectors #################

    def update_virtual_detector_shape(self):
        """
        Virtual detector shapes are mapped to integers, following the IDs assigned to the
        radio buttons in VirtualDetectorWidget in dialogs.py.  They are as follows:
            1: Rectangular
            2: Circular
            3: Annular
        """
        detector_shape = self.settings.virtual_detector_shape.val
        x, y = self.diffraction_space_view.shape
        x0, y0 = x / 2, y / 2
        xr, yr = x / 10, y / 10

        # Remove existing detector
        if hasattr(self, 'virtual_detector_roi'):
            self.diffraction_space_widget.view.scene().removeItem(
                self.virtual_detector_roi)
        if hasattr(self, 'virtual_detector_roi_inner'):
            self.diffraction_space_widget.view.scene().removeItem(
                self.virtual_detector_roi_inner)
        if hasattr(self, 'virtual_detector_roi_outer'):
            self.diffraction_space_widget.view.scene().removeItem(
                self.virtual_detector_roi_outer)

        # Rectangular detector
        if detector_shape == 0:
            self.virtual_detector_roi = pg.RectROI(
                [int(x0 - xr / 2), int(y0 - yr / 2)],
                [int(xr), int(yr)],
                pen=(3, 9))
            self.diffraction_space_widget.getView().addItem(
                self.virtual_detector_roi)
            self.virtual_detector_roi.sigRegionChangeFinished.connect(
                self.update_real_space_view)

        # Circular detector
        elif detector_shape == 1:
            self.virtual_detector_roi = pg.CircleROI(
                [int(x0 - xr / 2), int(y0 - yr / 2)],
                [int(xr), int(yr)],
                pen=(3, 9))
            self.diffraction_space_widget.getView().addItem(
                self.virtual_detector_roi)
            self.virtual_detector_roi.sigRegionChangeFinished.connect(
                self.update_real_space_view)

        # Annular dector
        elif detector_shape == 2:
            # Make outer detector
            self.virtual_detector_roi_outer = pg.CircleROI(
                [int(x0 - xr), int(y0 - yr)],
                [int(2 * xr), int(2 * yr)],
                pen=(3, 9))
            self.diffraction_space_widget.getView().addItem(
                self.virtual_detector_roi_outer)

            # Make inner detector
            self.virtual_detector_roi_inner = pg.CircleROI(
                [int(x0 - xr / 2), int(y0 - yr / 2)],
                [int(xr), int(yr)],
                pen=(4, 9),
                movable=False)
            self.diffraction_space_widget.getView().addItem(
                self.virtual_detector_roi_inner)

            # Connect size/position of inner and outer detectors
            self.virtual_detector_roi_outer.sigRegionChangeFinished.connect(
                self.update_annulus_pos)
            self.virtual_detector_roi_outer.sigRegionChangeFinished.connect(
                self.update_annulus_radii)
            self.virtual_detector_roi_inner.sigRegionChangeFinished.connect(
                self.update_annulus_radii)

            # Connect to real space view update function
            self.virtual_detector_roi_outer.sigRegionChangeFinished.connect(
                self.update_real_space_view)
            self.virtual_detector_roi_inner.sigRegionChangeFinished.connect(
                self.update_real_space_view)

        else:
            raise ValueError(
                "Unknown detector shape value {}.  Must be 0, 1, or 2.".format(
                    detector_shape))

        self.update_virtual_detector_mode()
        self.update_real_space_view()

    def update_annulus_pos(self):
        """
        Function to keep inner and outer rings of annulus aligned.
        """
        R_outer = self.virtual_detector_roi_outer.size().x() / 2
        R_inner = self.virtual_detector_roi_inner.size().x() / 2
        # Only outer annulus is draggable; when it moves, update position of inner annulus
        x0 = self.virtual_detector_roi_outer.pos().x() + R_outer
        y0 = self.virtual_detector_roi_outer.pos().y() + R_outer
        self.virtual_detector_roi_inner.setPos(x0 - R_inner, y0 - R_inner)

    def update_annulus_radii(self):
        R_outer = self.virtual_detector_roi_outer.size().x() / 2
        R_inner = self.virtual_detector_roi_inner.size().x() / 2
        if R_outer < R_inner:
            x0 = self.virtual_detector_roi_outer.pos().x() + R_outer
            y0 = self.virtual_detector_roi_outer.pos().y() + R_outer
            self.virtual_detector_roi_outer.setSize(2 * R_inner + 6)
            self.virtual_detector_roi_outer.setPos(x0 - R_inner - 3,
                                                   y0 - R_inner - 3)

    def update_virtual_detector_mode(self):
        """
        Virtual detector modes are mapped to integers, following the IDs assigned to the
        radio buttons in VirtualDetectorWidget in dialogs.py.  They are as follows:
            0: Integrate
            1: Difference, X
            2: Difference, Y
            3: CoM, Y
            4: CoM, X
        """
        detector_mode = self.settings.virtual_detector_mode.val
        detector_shape = self.settings.virtual_detector_shape.val

        # Integrating detector
        if detector_mode == 0:
            if detector_shape == 0:
                self.get_virtual_image = self.datacube.get_virtual_image_rect_integrate
            elif detector_shape == 1:
                self.get_virtual_image = self.datacube.get_virtual_image_circ_integrate
            elif detector_shape == 2:
                self.get_virtual_image = self.datacube.get_virtual_image_annular_integrate
            else:
                raise ValueError(
                    "Unknown detector shape value {}".format(detector_shape))

        # Difference detector
        elif detector_mode == 1:
            if detector_shape == 0:
                self.get_virtual_image = self.datacube.get_virtual_image_rect_diffX
            elif detector_shape == 1:
                self.get_virtual_image = self.datacube.get_virtual_image_circ_diffX
            elif detector_shape == 2:
                self.get_virtual_image = self.datacube.get_virtual_image_annular_diffX
            else:
                raise ValueError(
                    "Unknown detector shape value {}".format(detector_shape))
        elif detector_mode == 2:
            if detector_shape == 0:
                self.get_virtual_image = self.datacube.get_virtual_image_rect_diffY
            elif detector_shape == 1:
                self.get_virtual_image = self.datacube.get_virtual_image_circ_diffY
            elif detector_shape == 2:
                self.get_virtual_image = self.datacube.get_virtual_image_annular_diffY
            else:
                raise ValueError(
                    "Unknown detector shape value {}".format(detector_shape))

        # CoM detector
        elif detector_mode == 3:
            if detector_shape == 0:
                self.get_virtual_image = self.datacube.get_virtual_image_rect_CoMX
            elif detector_shape == 1:
                self.get_virtual_image = self.datacube.get_virtual_image_circ_CoMX
            elif detector_shape == 2:
                self.get_virtual_image = self.datacube.get_virtual_image_annular_CoMX
            else:
                raise ValueError(
                    "Unknown detector shape value {}".format(detector_shape))
        elif detector_mode == 4:
            if detector_shape == 0:
                self.get_virtual_image = self.datacube.get_virtual_image_rect_CoMY
            elif detector_shape == 1:
                self.get_virtual_image = self.datacube.get_virtual_image_circ_CoMY
            elif detector_shape == 2:
                self.get_virtual_image = self.datacube.get_virtual_image_annular_CoMY
            else:
                raise ValueError(
                    "Unknown detector shape value {}".format(detector_shape))

        else:
            raise ValueError(
                "Unknown detector mode value {}".format(detector_mode))

        self.update_real_space_view()

    ################## Get virtual images ##################

    def update_diffraction_space_view(self):
        roi_state = self.real_space_point_selector.saveState()
        x0, y0 = roi_state['pos']
        xc, yc = int(x0 + 1), int(y0 + 1)

        # Set the diffraction space image
        new_diffraction_space_view, success = self.datacube.get_diffraction_space_view(
            xc, yc)
        if success:
            self.diffraction_space_view = new_diffraction_space_view
            self.diffraction_space_widget.setImage(self.diffraction_space_view,
                                                   autoLevels=False,
                                                   autoRange=False)
        else:
            pass
        return

    def update_real_space_view(self):
        detector_shape = self.settings.virtual_detector_shape.val

        # Rectangular detector
        if detector_shape == 0:
            # Get slices corresponding to ROI
            slices, transforms = self.virtual_detector_roi.getArraySlice(
                self.datacube.data4D[0, 0, :, :],
                self.diffraction_space_widget.getImageItem())
            slice_x, slice_y = slices

            # Get the virtual image and set the real space view
            new_real_space_view, success = self.get_virtual_image(
                slice_x, slice_y)
            if success:
                self.real_space_view = new_real_space_view
                self.real_space_widget.setImage(self.real_space_view,
                                                autoLevels=True)
            else:
                pass

        # Circular detector
        elif detector_shape == 1:
            # Get slices corresponding to ROI
            slices, transforms = self.virtual_detector_roi.getArraySlice(
                self.datacube.data4D[0, 0, :, :],
                self.diffraction_space_widget.getImageItem())
            slice_x, slice_y = slices

            # Get the virtual image and set the real space view
            new_real_space_view, success = self.get_virtual_image(
                slice_x, slice_y)
            if success:
                self.real_space_view = new_real_space_view
                self.real_space_widget.setImage(self.real_space_view,
                                                autoLevels=True)
            else:
                pass

        # Annular detector
        elif detector_shape == 2:
            # Get slices corresponding to ROI
            slices, transforms = self.virtual_detector_roi_outer.getArraySlice(
                self.datacube.data4D[0, 0, :, :],
                self.diffraction_space_widget.getImageItem())
            slice_x, slice_y = slices
            slices_inner, transforms = self.virtual_detector_roi_inner.getArraySlice(
                self.datacube.data4D[0, 0, :, :],
                self.diffraction_space_widget.getImageItem())
            slice_inner_x, slice_inner_y = slices_inner
            R = 0.5 * ((slice_inner_x.stop - slice_inner_x.start) /
                       (slice_x.stop - slice_x.start) +
                       (slice_inner_y.stop - slice_inner_y.start) /
                       (slice_y.stop - slice_y.start))

            # Get the virtual image and set the real space view
            new_real_space_view, success = self.get_virtual_image(
                slice_x, slice_y, R)
            if success:
                self.real_space_view = new_real_space_view
                self.real_space_widget.setImage(self.real_space_view,
                                                autoLevels=True)
            else:
                pass

        else:
            print(
                "Error: unknown detector shape value {}.  Must be 0, 1, or 2.".
                format(detector_shape))

        return

    def exec_(self):
        return self.qtapp.exec_()
Ejemplo n.º 13
0
class QtipWindow(QtGui.QMainWindow):
    """
    Main Qtip window

    Note, is the main window now, but the content will later be moved to a
    libstempo tab, as part of the Piccard suite
    """
    
    def __init__(self, parent=None, engine='libstempo', \
            parfile=None, timfile=None, **kwargs):
        super(QtipWindow, self).__init__(parent)
        self.setWindowTitle('QtIpython interface to Piccard/libstempo')
        
        # Initialise basic gui elements
        self.initUI()

        # Start the embedded IPython kernel
        self.createIPythonKernel()

        # Create the display widgets
        self.createBinaryWidget()
        self.createPlkWidget()
        self.createIPythonWidget()
        self.createOpenSomethingWidget()

        # Position the widgets
        self.initQtipLayout()
        self.setQtipLayout(whichWidget='opensomething',
                showIPython=False, firsttime=True)

        # We are still in MAJOR testing mode, so open a test-pulsar right away
        # (delete this line when going into production)
        if parfile is None or timfile is None:
            testpulsar = True
        else:
            testpulsar = False

        # Are we going to open plk straight away?
        self.requestOpenPlk(testpulsar=testpulsar, parfilename=parfile, \
                timfilename=timfile, engine=engine)

        self.show()

    def __del__(self):
        pass

    def onAbout(self):
        msg = """ A plk emulator, written in Python. Powered by PyQt, matplotlib, libstempo, and IPython:
        """
        QtGui.QMessageBox.about(self, "About the demo", msg.strip())

    def initUI(self):
        """
        Initialise the user-interface elements
        """
        # Create the main-frame widget, and the layout
        self.mainFrame = QtGui.QWidget()
        self.setCentralWidget(self.mainFrame)
        self.hbox = QtGui.QHBoxLayout()     # HBox contains all widgets

        # Create the menu action items
        self.openParTimAction = QtGui.QAction('&Open', self)        
        self.openParTimAction.setShortcut('Ctrl+O')
        self.openParTimAction.setStatusTip('Open par/tim')
        self.openParTimAction.triggered.connect(self.openParTim)

        self.exitAction = QtGui.QAction(QtGui.QIcon('exit.png'), '&Exit', self)        
        self.exitAction.setShortcut('Ctrl+Q')
        self.exitAction.setStatusTip('Exit application')
        self.exitAction.triggered.connect(self.close)

        self.toggleBinaryAction = QtGui.QAction('&Binary', self)        
        self.toggleBinaryAction.setShortcut('Ctrl+B')
        self.toggleBinaryAction.setStatusTip('Toggle binary widget')
        self.toggleBinaryAction.triggered.connect(self.toggleBinary)

        self.togglePlkAction = QtGui.QAction('&Plk', self)        
        self.togglePlkAction.setShortcut('Ctrl+P')
        self.togglePlkAction.setStatusTip('Toggle plk widget')
        self.togglePlkAction.triggered.connect(self.togglePlk)

        self.toggleIPythonAction = QtGui.QAction('&IPython', self)        
        self.toggleIPythonAction.setShortcut('Ctrl+I')
        self.toggleIPythonAction.setStatusTip('Toggle IPython')
        self.toggleIPythonAction.triggered.connect(self.toggleIPython)

        self.aboutAction = QtGui.QAction('&About', self)        
        self.aboutAction.setShortcut('Ctrl+A')
        self.aboutAction.setStatusTip('About Qtip')
        self.aboutAction.triggered.connect(self.onAbout)

        self.theStatusBar = QtGui.QStatusBar()
        #self.statusBar()
        self.setStatusBar(self.theStatusBar)

        self.engine_label = QtGui.QLabel("Tempo2")
        self.engine_label.setFrameStyle( QtGui.QFrame.Sunken|QtGui.QFrame.Panel)
        self.engine_label.setLineWidth(4)
        self.engine_label.setMidLineWidth(4)
        self.engine_label.setStyleSheet("QLabel{color:black;background-color:red}")
        self.theStatusBar.addPermanentWidget(self.engine_label)

        # On OSX, make sure the menu can be displayed (in the window itself)
        if sys.platform == 'darwin':
            # On OSX, the menubar is usually on the top of the screen, not in
            # the window. To make it in the window:
            QtGui.qt_mac_set_native_menubar(False) 

            # Otherwise, if we'd like to get the system menubar at the top, then
            # we need another menubar object, not self.menuBar as below. In that
            # case, use:
            # self.menubar = QtGui.QMenuBar()
            # TODO: Somehow this does not work. Per-window one does though

        # Create the menu
        self.menubar = self.menuBar()
        self.fileMenu = self.menubar.addMenu('&File')
        self.fileMenu.addAction(self.openParTimAction)
        self.fileMenu.addAction(self.exitAction)
        self.viewMenu = self.menubar.addMenu('&View')
        self.viewMenu.addAction(self.toggleBinaryAction)
        self.viewMenu.addAction(self.togglePlkAction)
        self.viewMenu.addAction(self.toggleIPythonAction)
        self.helpMenu = self.menubar.addMenu('&Help')
        self.helpMenu.addAction(self.aboutAction)

        # What is the status quo of the user interface?
        self.showIPython = False
        self.whichWidget = 'None'
        self.prevShowIPython = None
        self.prevWhichWidget = 'None'

    def createIPythonKernel(self):
        """
        Create the IPython Kernel
        """
        # Create an in-process kernel
        self.kernelManager = QtInProcessKernelManager()
        self.kernelManager.start_kernel()
        self.kernel = self.kernelManager.kernel

        self.kernelClient = self.kernelManager.client()
        self.kernelClient.start_channels()

        self.kernel.shell.enable_matplotlib(gui='inline')

        # Load the necessary packages in the embedded kernel
        cell = "import numpy as np, matplotlib.pyplot as plt, qtpulsar as qp"
        self.kernel.shell.run_cell(cell, store_history=False)

        # Set the in-kernel matplotlib color scheme to black.
        self.setMplColorScheme('black')     # Outside as well (do we need this?)
        self.kernel.shell.run_cell(constants.matplotlib_rc_cell_black,
                store_history=False)

    def createIPythonWidget(self):
        """
        Create the IPython widget
        """
        self.consoleWidget = RichIPythonWidget()
        #self.consoleWidget.setMinimumSize(600, 550)
        self.consoleWidget.banner = QtipBanner
        self.consoleWidget.kernel_manager = self.kernelManager
        self.consoleWidget.kernel_client = self.kernelClient
        self.consoleWidget.exit_requested.connect(self.toggleIPython)
        self.consoleWidget.set_default_style(colors='linux')
        self.consoleWidget.hide()

        # Register a call-back function for the IPython shell. This one is
        # executed insite the child-kernel.
        #self.kernel.shell.register_post_execute(self.postExecute)
        #
        # In IPython >= 2, we can use the event register
        # Events: post_run_cell, pre_run_cell, etc...`
        self.kernel.shell.events.register('pre_execute', self.preExecute)
        self.kernel.shell.events.register('post_execute', self.postExecute)
        self.kernel.shell.events.register('post_run_cell', self.postRunCell)


    def createOpenSomethingWidget(self):
        """
        Create the OpenSomething widget. Do not add it to the layout yet

        TODO:   probably should use a signal to implement this call instead of
                openParTim
        """
        self.openSomethingWidget = OpenSomethingWidget(parent=self.mainFrame, \
                openFile=self.requestOpenPlk)
        self.openSomethingWidget.hide()

    def createPlkWidget(self):
        """
        Create the Plk widget
        """
        self.plkWidget = PlkWidget(parent=self.mainFrame)
        self.plkWidget.hide()

    def createBinaryWidget(self):
        """
        Create the binary model widget
        """
        self.binaryWidget = BinaryWidget(parent=self.mainFrame)
        self.binaryWidget.hide()

    def toggleIPython(self):
        """
        Toggle the IPython widget on or off
        """
        self.setQtipLayout(showIPython = not self.showIPython)

    def toggleBinary(self):
        """
        Toggle the binary model widget on or off
        """
        self.setQtipLayout(whichWidget='binary')

    def togglePlk(self):
        """
        Toggle the plk widget on or off
        """
        self.setQtipLayout(whichWidget='plk')


    def initQtipLayout(self):
        """
        Initialise the Qtip layout
        """
        self.hbox.addWidget(self.openSomethingWidget)
        self.hbox.addWidget(self.plkWidget)
        self.hbox.addWidget(self.binaryWidget)
        self.hbox.addStretch(1)
        self.hbox.addWidget(self.consoleWidget)
        self.mainFrame.setLayout(self.hbox)

    def hideAllWidgets(self):
        """
        Hide all widgets of the mainFrame
        """
        # Remove all widgets from the main window
        # ???
        """
        while self.hbox.count():
            item = self.hbox.takeAt(0)
            if isinstance(item, QtGui.QWidgetItem):
                #item.widget().deleteLater()
                item.widget().hide()
            elif isinstance(item, QtGui.QSpacerItem):
                #self.hbox.removeItem(item)
                pass
            else:
                #fcbox.clearLayout(item.layout())
                #self.hbox.removeItem(item)
                pass
        """
        self.openSomethingWidget.hide()
        self.plkWidget.hide()
        self.binaryWidget.hide()
        self.consoleWidget.hide()

    def showVisibleWidgets(self):
        """
        Show the correct widgets in the mainFrame
        """
        # Add the widgets we need
        if self.whichWidget.lower() == 'opensomething':
            self.openSomethingWidget.show()
        elif self.whichWidget.lower() == 'plk':
            self.plkWidget.show()
        elif self.whichWidget.lower() == 'piccard':
            pass
        elif self.whichWidget.lower() == 'binary':
            self.binaryWidget.show()

        if self.showIPython:
            self.consoleWidget.show()
        else:
            pass

        if self.whichWidget.lower() == 'plk' and not self.showIPython:
            self.plkWidget.setFocusToCanvas()
        elif self.whichWidget.lower() == 'binary' and not self.showIPython:
            self.binaryWidget.setFocusToCanvas()
        #elif self.showIPython:
        #    self.consoleWidget.setFocus()

    def setQtipLayout(self, whichWidget=None, showIPython=None, firsttime=False):
        """
        Given which widgets to show, display the right widgets and hide the rest

        @param whichWidget:     Which widget to show
        @param showIPython:     Whether to show the IPython console
        """
        if not whichWidget is None:
            self.whichWidget = whichWidget
        if not showIPython is None:
            self.showIPython = showIPython

        # After hiding the widgets, wait 25 (or 0?) miliseconds before showing them again
        self.hideAllWidgets()
        QtCore.QTimer.singleShot(0, self.showVisibleWidgets)

        self.prevWhichWidget = self.whichWidget

        if self.showIPython != self.prevShowIPython:
            # IPython has been toggled
            self.prevShowIPython = self.showIPython
            if self.showIPython:
                self.resize(1350, 550)
                self.mainFrame.resize(1350, 550)
            else:
                self.resize(650, 550)
                self.mainFrame.resize(650, 550)

        if firsttime:
            # Set position slightly more to the left of the screen, so we can
            # still open IPython
            self.move(200, 100)

        self.mainFrame.setLayout(self.hbox)
        self.mainFrame.show()


    def requestOpenPlk(self, parfilename=None, timfilename=None, \
            testpulsar=False, engine='libstempo'):
        """
        Request to open a file in the plk widget

        @param parfilename:     The parfile to open. If none, ask the user
        @param timfilename:     The timfile to open. If none, ask the user
        """
        self.setQtipLayout(whichWidget='plk', showIPython=self.showIPython)

        if parfilename is None and not testpulsar:
            parfilename = QtGui.QFileDialog.getOpenFileName(self, 'Open par-file', '~/')

        if timfilename is None and not testpulsar:
            timfilename = QtGui.QFileDialog.getOpenFileName(self, 'Open tim-file', '~/')

        # Load the pulsar
        self.openPulsar(parfilename, timfilename, engine=engine, \
                testpulsar=testpulsar)

    def setMplColorScheme(self, scheme):
        """
        Set the matplotlib color scheme

        @param scheme:  'black'/'white', the color scheme
        """

        # Obtain the Widget background color
        color = self.palette().color(QtGui.QPalette.Window)
        r, g, b = color.red(), color.green(), color.blue()
        rgbcolor = (r/255.0, g/255.0, b/255.0)

        if scheme == 'white':
            rcP = constants.mpl_rcParams_white

            rcP['axes.facecolor'] = rgbcolor
            rcP['figure.facecolor'] = rgbcolor
            rcP['figure.edgecolor'] = rgbcolor
            rcP['savefig.facecolor'] = rgbcolor
            rcP['savefig.edgecolor'] = rgbcolor
        elif scheme == 'black':
            rcP = constants.mpl_rcParams_black

        for key, value in rcP.iteritems():
            matplotlib.rcParams[key] = value


    def openParTim(self, filename=None, engine='libstempo'):
        """
        Open a par-file and a tim-file
        """
        print("openParTim called with {0}".format(filename))

        # Ask the user for a par and tim file, and open these with libstempo
        if isinstance(filename, str):
            parfilename = filename
        else:
            parfilename = QtGui.QFileDialog.getOpenFileName(self, 'Open par-file', '~/')

        timfilename = QtGui.QFileDialog.getOpenFileName(self, 'Open tim-file', '~/')

        # Load the pulsar
        self.openPulsar(parfilename, timfilename, engine=engine)

    def openPulsar(self, parfilename, timfilename, engine='libstempo',
            testpulsar=False):
        """
        Open a pulsar, given a parfile and a timfile

        @param parfilename: The name of the parfile to open
        @param timfilename: The name fo the timfile to open
        @param engine:      Which pulsar timing engine to use [libstempo]
        @param testpulsar:  If True, open the test pulsar (J1744, NANOGrav)
        """
        if engine=='pint':
            trypint = True
        else:
            trypint = False

        self.engine, pclass = qp.get_engine(trypint=trypint)

        if engine == 'libstempo':
            if not testpulsar:
                # Obtain the directory name of the timfile, and change to it
                timfiletup = os.path.split(timfilename)
                dirname = timfiletup[0]
                reltimfile = timfiletup[-1]
                relparfile = os.path.relpath(parfilename, dirname)
                savedir = os.getcwd()

                # Change directory to the base directory of the tim-file to deal with
                # INCLUDE statements in the tim-file
                os.chdir(dirname)

                # Load the pulsar
                cell = "psr = qp."+pclass+"('"+relparfile+"', '"+reltimfile+"')"
                self.kernel.shell.run_cell(cell)
                psr = self.kernel.shell.ns_table['user_local']['psr']

                # Change directory back to where we were
                os.chdir(savedir)
            else:
                cell = "psr = qp."+pclass+"(testpulsar=True)"
                self.kernel.shell.run_cell(cell)
                psr = self.kernel.shell.ns_table['user_local']['psr']
        elif engine == 'pint':
            cell = "psr = qp."+pclass+"(testpulsar=True)"
            self.kernel.shell.run_cell(cell)
            psr = self.kernel.shell.ns_table['user_local']['psr']
        else:
            raise NotImplemented("Only works with PINT/libstempo")

        # Update the plk widget
        self.plkWidget.setPulsar(psr)
        self.binaryWidget.setPulsar(psr)

        # Communicating with the kernel goes as follows
        # self.kernel.shell.push({'foo': 43, 'print_process_id': print_process_id}, interactive=True)
        # print("Embedded, we have:", self.kernel.shell.ns_table['user_local']['foo'])


    def keyPressEvent(self, event, **kwargs):
        """
        Handle a key-press event

        @param event:   event that is handled here
        """

        key = event.key()

        if key == QtCore.Qt.Key_Escape:
            self.close()
        elif key == QtCore.Qt.Key_Left:
            #print("Left pressed")
            pass
        else:
            #print("Other key")
            pass

        #print("QtipWindow: key press")
        super(QtipWindow, self).keyPressEvent(event, **kwargs)

    def mousePressEvent(self, event, **kwargs):
        """
        Handle a mouse-click event

        @param event:   event that is handled here
        """
        #print("QtipWindow: mouse click")
        super(QtipWindow, self).mousePressEvent(event, **kwargs)

    def preExecute(self):
        """
        Callback function that is run prior to execution of a cell
        """
        pass

    def postExecute(self):
        """
        Callback function that is run after execution of a code
        """
        pass

    def postRunCell(self):
        """
        Callback function that is run after execution of a cell (after
        post-execute)
        """
        # TODO: Do more than just update the plot, but also update _all_ the
        # widgets. Make a callback in plkWidget for that. QtipWindow might also
        # want to loop over some stuff.
        if self.whichWidget == 'plk':
            self.plkWidget.updatePlot()
        elif self.whichWidget == 'binary':
            self.binaryWidget.updatePlot()
Ejemplo n.º 14
0
def procsteppython_do_run(stepglobals, runfunc, argkw, ipythonmodelist, action,
                          scripthref, pycode_text, pycode_lineno):

    if not ipythonmodelist[0]:
        resultdict = runfunc(**argkw)
        return resultdict
    else:
        # ipython mode
        # in-process kernel, a-la https://raw.githubusercontent.com/ipython/ipython/master/examples/Embedding/inprocess_qtconsole.py

        ## Set PyQt4 API version to 2 and import it -- required for ipython compatibility
        #import sip
        #sip.setapi('QVariant', 2)
        #sip.setapi('QString', 2)
        #sip.setapi('QDateTime', 2)
        #sip.setapi('QDate', 2)
        #sip.setapi('QTextStream', 2)
        #sip.setapi('QTime', 2)
        #sip.setapi('QUrl', 2)
        #from PyQt4 import QtGui   # force IPython to use PyQt4 by importing it first

        # RHEL6 compatibility  -- if running under Python 2.6, just import IPython, get PyQt4
        if sys.version_info < (2, 7):
            from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
            from IPython.qt.inprocess import QtInProcessKernelManager
            pass
        else:

            # Under more recent OS's: Make matplotlib use PySide
            # http://stackoverflow.com/questions/6723527/getting-pyside-to-work-with-matplotlib
            import matplotlib
            matplotlib.use('Qt4Agg')
            matplotlib.rcParams['backend.qt4'] = 'PySide'
            pass

        import IPython
        from IPython.core.interactiveshell import DummyMod

        if LooseVersion(IPython.__version__) >= LooseVersion('4.0.0'):
            # Recent Jupyter/ipython: Import from qtconsole
            # Force PySide bindings
            import PySide.QtCore
            from qtconsole.qt import QtGui
            from qtconsole.inprocess import QtInProcessKernelManager

            # Obtain the running QApplication instance
            app = QtGui.QApplication.instance()
            if app is None:
                # Start our own if necessary
                app = QtGui.QApplication([])
                pass

            pass
        else:
            from IPython.qt.inprocess import QtInProcessKernelManager
            from IPython.lib import guisupport
            app = guisupport.get_app_qt4()

            pass

        kernel_manager = QtInProcessKernelManager()
        kernel_manager.start_kernel()
        kernel = kernel_manager.kernel
        kernel.gui = 'qt4'

        #sys.stderr.write("id(stepglobals)=%d" % (id(stepglobals)))

        # Make ipython use our globals as its global dictionary
        # ... but first keep a backup
        stepglobalsbackup = copy.copy(stepglobals)

        (kernel.user_module,
         kernel.user_ns) = kernel.shell.prepare_user_module(
             user_ns=stepglobals)

        # Should we attempt to run the function here?

        # (gui, backend) = kernel.shell.enable_matplotlib("qt4") #,import_all=False) # (args.gui, import_all=import_all)
        (gui, backend, clobbered) = kernel.shell.enable_pylab(
            "qt4", import_all=False)  # (args.gui, import_all=import_all)

        # kernel.shell.push(stepglobals) # provide globals as variables -- no longer necessary as it's using our namespace already

        kernel.shell.push(argkw)  # provide arguments as variables

        kernel.shell.push(
            {"kernel": kernel},
            interactive=False)  # provide kernel for debugging purposes

        kernel_client = kernel_manager.client()
        kernel_client.start_channels()
        abort_requested_list = [False
                                ]  # encapsulated in a list to make it mutable

        def stop():
            control.hide()
            kernel_client.stop_channels()
            kernel_manager.shutdown_kernel()
            app.exit()
            pass

        def abort():
            # simple exit doesn't work. See http://stackoverflow.com/questions/1527689/exit-from-ipython
            # too bad this doesn't work right now!!!
            class Quitter(object):
                def __repr__(self):
                    sys.exit()

                pass

            kernel.shell.push({"quitter": Quitter()})
            kernel.shell.ex("quitter")

            stop()
            abort_requested_list.pop()
            abort_requested_list.append(True)
            pass

        if pycode_text is None:
            kernel.shell.write("\n\nExecute %s/%s\n" %
                               (scripthref.getpath(), runfunc.__name__))
            pass
        else:
            kernel.shell.write(
                "\n\nExecute %s/%s/%s\n" %
                (scripthref.getpath(), action, runfunc.__name__))
            pass

        kernel.shell.write("Assign return value to \"ret\" and press Ctrl-D\n")
        kernel.shell.write("Set cont=True to disable interactive mode\n")
        # kernel.shell.write("call abort() to exit\n")

        if LooseVersion(IPython.__version__) >= LooseVersion('4.0.0'):
            # Recent Jupyter/ipython: Import from qtconsole
            from qtconsole.rich_jupyter_widget import RichJupyterWidget as RichIPythonWidget
            pass
        else:
            from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
            pass

        control = RichIPythonWidget()
        control.kernel_manager = kernel_manager
        control.kernel_client = kernel_client
        control.exit_requested.connect(stop)
        control.show()

        #sys.stderr.write("lines=%s\n" % (str(lines)))
        #sys.stderr.write("lines[0]=%s\n" % (str(lines[0])))

        try:
            if pycode_text is None:
                (lines, startinglineno) = inspect.getsourcelines(runfunc)

                assert (lines[0].startswith("def")
                        )  # first line of function is the defining line
                del lines[0]  # remove def line
                lines.insert(0, "if 1:\n")  # allow function to be indented
                runfunc_syntaxtree = ast.parse(
                    "".join(lines), filename=scripthref.getpath(), mode='exec'
                )  # BUG: Should set dont_inherit parameter and properly determine which __future__ import flags should be passed

                # fixup line numbers
                for syntreenode in ast.walk(runfunc_syntaxtree):
                    if hasattr(syntreenode, "lineno"):
                        syntreenode.lineno += startinglineno - 1
                        pass
                    pass

                # runfunc_syntaxtree should consist of the if statement we just added
                # use _fields attribute to look up fields of an AST element
                # (e.g. test, body, orelse for IF)
                # then those fields can be accessed directly
                assert (len(runfunc_syntaxtree.body) == 1)
                code_container = runfunc_syntaxtree.body[0]
                assert (isinstance(code_container, ast.If)
                        )  # code_container is the if statement we just wrote

                kernel.shell.push(
                    {"runfunc_syntaxtree": runfunc_syntaxtree},
                    interactive=False
                )  # provide processed syntax tree for debugging purposes

                pass
            else:
                fullsyntaxtree = ast.parse(
                    pycode_text
                )  # BUG: Should set dont_inherit parameter and properly determine which __future__ import flags should be passed
                # fixup line numbers
                for syntreenode in ast.walk(fullsyntaxtree):
                    if hasattr(syntreenode, "lineno"):
                        syntreenode.lineno += pycode_lineno - 1
                        pass
                    pass
                code_container = None
                for codeelement in fullsyntaxtree.body:
                    if isinstance(codeelement, ast.FunctionDef):
                        if codeelement.name == runfunc.__name__:
                            code_container = codeelement
                            runfunc_syntaxtree = codeelement
                            pass
                        pass

                    pass
                if code_container is None:
                    raise ValueError(
                        "Couldn't find code for %s for ipython execution" %
                        (runfunc.__name__))

                kernel.shell.push(
                    {"fullsyntaxtree": fullsyntaxtree}, interactive=False
                )  # provide full syntax tree for debugging purposes

                pass

            # identify global variables from runfunc_syntaxtree
            globalvars = set()
            for treeelem in ast.walk(runfunc_syntaxtree):
                if isinstance(treeelem, ast.Global):
                    globalvars = globalvars.union(treeelem.names)
                    pass
                pass

            kernel.shell.push({"abort": abort})  # provide abort function
            kernel.shell.push({"cont": False})  # continue defaults to False

            returnstatement = code_container.body[-1]
            if isinstance(returnstatement, ast.Return):
                # last statement is a return statement!
                # Create assign statement that assigns
                # the result to ret
                retassign = ast.Assign(targets=[
                    ast.Name(id="ret",
                             ctx=ast.Store(),
                             lineno=returnstatement.lineno,
                             col_offset=returnstatement.col_offset)
                ],
                                       value=returnstatement.value,
                                       lineno=returnstatement.lineno,
                                       col_offset=returnstatement.col_offset)
                del code_container.body[-1]  # remove returnstatement
                code_container.body.append(retassign)  # add assignment
                pass

            runfunc_lines = code_container.body

            kernel.shell.push(
                {
                    "runfunc_lines": runfunc_lines,
                    "scripthref": scripthref
                },
                interactive=False
            )  # provide processed syntax tree for debugging purposes

            # kernel.shell.run_code(compile("kernel.shell.run_ast_nodes(runfunc_lines,scriptpath,interactivity='all')","None","exec"))
            if LooseVersion(IPython.__version__) >= LooseVersion('4.0.0'):
                # Recent Jupyter/ipython: Import from qtconsole
                from qtconsole.inprocess import QtCore
                pass
            else:
                from IPython.qt.inprocess import QtCore
                pass
            QTimer = QtCore.QTimer

            def showret():
                control.execute("ret")
                pass

            def runcode():
                control.execute(
                    "kernel.shell.run_ast_nodes(runfunc_lines,scripthref.getpath(),interactivity='none')"
                )
                # QTimer.singleShot(25,showret) # get callback 25ms into main loop
                # showret disabled because it prevents you from running the
                # debugger in post-mortem mode to troubleshoot an exception:
                # import pdb; pdb.pm()
                pass

            QTimer.singleShot(25, runcode)  # get callback 25ms into main loop
            # control.execute("kernel.shell.run_ast_nodes(runfunc_lines,scripthref.getpath(),interactivity='none')")

            pass
        except:
            (exctype, excvalue) = sys.exc_info()[:2]
            sys.stderr.write(
                "%s while attempting to prepare URL %s code for interactive execution: %s\n"
                % (exctype.__name__, scripthref.absurl(), str(excvalue)))
            traceback.print_exc()
            raise

        if LooseVersion(IPython.__version__) >= LooseVersion('4.0.0'):
            # Recent Jupyter/ipython: Import from qtconsole
            app.exec_()
            pass
        else:
            # Old ipython
            guisupport.start_event_loop_qt4(app)
            pass

        if abort_requested_list[0]:
            pass

        if kernel.shell.ev("cont"):
            # cont==True -> disable interactive mode
            ipythonmodelist.pop()
            ipythonmodelist.append(False)
            pass

        try:
            retval = kernel.shell.ev(
                "ret")  # Assign result dictionary to "ret" variable
            pass
        except NameError:  # if ret not assigned, return {}
            retval = {}
            pass

        # Performing this execution changed values in stepglobals
        # but it should have only done that for variables specified
        # as 'global' in the function.

        # So: Update our backup of the value of stepglobals,
        #     according to the specified globals, and
        #     replace stepglobals with that updated backup

        stepglobalsbackup.update(
            dict([(varname, stepglobals[varname]) for varname in globalvars]))
        stepglobals.clear()
        stepglobals.update(stepglobalsbackup)

        return retval

    pass
Ejemplo n.º 15
0
class QtipWindow(QtGui.QMainWindow):
    """
    Main Qtip window

    Note, is the main window now, but the content will later be moved to a
    libstempo tab, as part of the Piccard suite
    """

    def __init__(self, parent=None, engine='libstempo', \
            parfile=None, timfile=None, **kwargs):
        super(QtipWindow, self).__init__(parent)
        self.setWindowTitle('QtIpython interface to Piccard/libstempo')

        # Initialise basic gui elements
        self.initUI()

        # Start the embedded IPython kernel
        self.createIPythonKernel()

        # Create the display widgets
        self.createBinaryWidget()
        self.createPlkWidget()
        self.createIPythonWidget()
        self.createOpenSomethingWidget()

        # Position the widgets
        self.initQtipLayout()
        self.setQtipLayout(whichWidget='opensomething',
                           showIPython=False,
                           firsttime=True)

        # We are still in MAJOR testing mode, so open a test-pulsar right away
        # (delete this line when going into production)
        if parfile is None or timfile is None:
            testpulsar = True
        else:
            testpulsar = False

        # Are we going to open plk straight away?
        self.requestOpenPlk(testpulsar=testpulsar, parfilename=parfile, \
                timfilename=timfile, engine=engine)

        self.show()

    def __del__(self):
        pass

    def onAbout(self):
        msg = """ A plk emulator, written in Python. Powered by PyQt, matplotlib, libstempo, and IPython:
        """
        QtGui.QMessageBox.about(self, "About the demo", msg.strip())

    def initUI(self):
        """
        Initialise the user-interface elements
        """
        # Create the main-frame widget, and the layout
        self.mainFrame = QtGui.QWidget()
        self.setCentralWidget(self.mainFrame)
        self.hbox = QtGui.QHBoxLayout()  # HBox contains all widgets

        # Create the menu action items
        self.openParTimAction = QtGui.QAction('&Open', self)
        self.openParTimAction.setShortcut('Ctrl+O')
        self.openParTimAction.setStatusTip('Open par/tim')
        self.openParTimAction.triggered.connect(self.openParTim)

        self.exitAction = QtGui.QAction(QtGui.QIcon('exit.png'), '&Exit', self)
        self.exitAction.setShortcut('Ctrl+Q')
        self.exitAction.setStatusTip('Exit application')
        self.exitAction.triggered.connect(self.close)

        self.toggleBinaryAction = QtGui.QAction('&Binary', self)
        self.toggleBinaryAction.setShortcut('Ctrl+B')
        self.toggleBinaryAction.setStatusTip('Toggle binary widget')
        self.toggleBinaryAction.triggered.connect(self.toggleBinary)

        self.togglePlkAction = QtGui.QAction('&Plk', self)
        self.togglePlkAction.setShortcut('Ctrl+P')
        self.togglePlkAction.setStatusTip('Toggle plk widget')
        self.togglePlkAction.triggered.connect(self.togglePlk)

        self.toggleIPythonAction = QtGui.QAction('&IPython', self)
        self.toggleIPythonAction.setShortcut('Ctrl+I')
        self.toggleIPythonAction.setStatusTip('Toggle IPython')
        self.toggleIPythonAction.triggered.connect(self.toggleIPython)

        self.aboutAction = QtGui.QAction('&About', self)
        self.aboutAction.setShortcut('Ctrl+A')
        self.aboutAction.setStatusTip('About Qtip')
        self.aboutAction.triggered.connect(self.onAbout)

        self.theStatusBar = QtGui.QStatusBar()
        #self.statusBar()
        self.setStatusBar(self.theStatusBar)

        self.engine_label = QtGui.QLabel("Tempo2")
        self.engine_label.setFrameStyle(QtGui.QFrame.Sunken
                                        | QtGui.QFrame.Panel)
        self.engine_label.setLineWidth(4)
        self.engine_label.setMidLineWidth(4)
        self.engine_label.setStyleSheet(
            "QLabel{color:black;background-color:red}")
        self.theStatusBar.addPermanentWidget(self.engine_label)

        # On OSX, make sure the menu can be displayed (in the window itself)
        if sys.platform == 'darwin':
            # On OSX, the menubar is usually on the top of the screen, not in
            # the window. To make it in the window:
            QtGui.qt_mac_set_native_menubar(False)

            # Otherwise, if we'd like to get the system menubar at the top, then
            # we need another menubar object, not self.menuBar as below. In that
            # case, use:
            # self.menubar = QtGui.QMenuBar()
            # TODO: Somehow this does not work. Per-window one does though

        # Create the menu
        self.menubar = self.menuBar()
        self.fileMenu = self.menubar.addMenu('&File')
        self.fileMenu.addAction(self.openParTimAction)
        self.fileMenu.addAction(self.exitAction)
        self.viewMenu = self.menubar.addMenu('&View')
        self.viewMenu.addAction(self.toggleBinaryAction)
        self.viewMenu.addAction(self.togglePlkAction)
        self.viewMenu.addAction(self.toggleIPythonAction)
        self.helpMenu = self.menubar.addMenu('&Help')
        self.helpMenu.addAction(self.aboutAction)

        # What is the status quo of the user interface?
        self.showIPython = False
        self.whichWidget = 'None'
        self.prevShowIPython = None
        self.prevWhichWidget = 'None'

    def createIPythonKernel(self):
        """
        Create the IPython Kernel
        """
        # Create an in-process kernel
        self.kernelManager = QtInProcessKernelManager()
        self.kernelManager.start_kernel()
        self.kernel = self.kernelManager.kernel

        self.kernelClient = self.kernelManager.client()
        self.kernelClient.start_channels()

        self.kernel.shell.enable_matplotlib(gui='inline')

        # Load the necessary packages in the embedded kernel
        cell = "import numpy as np, matplotlib.pyplot as plt, qtpulsar as qp"
        self.kernel.shell.run_cell(cell, store_history=False)

        # Set the in-kernel matplotlib color scheme to black.
        self.setMplColorScheme('black')  # Outside as well (do we need this?)
        self.kernel.shell.run_cell(constants.matplotlib_rc_cell_black,
                                   store_history=False)

    def createIPythonWidget(self):
        """
        Create the IPython widget
        """
        self.consoleWidget = RichIPythonWidget()
        #self.consoleWidget.setMinimumSize(600, 550)
        self.consoleWidget.banner = QtipBanner
        self.consoleWidget.kernel_manager = self.kernelManager
        self.consoleWidget.kernel_client = self.kernelClient
        self.consoleWidget.exit_requested.connect(self.toggleIPython)
        self.consoleWidget.set_default_style(colors='linux')
        self.consoleWidget.hide()

        # Register a call-back function for the IPython shell. This one is
        # executed insite the child-kernel.
        #self.kernel.shell.register_post_execute(self.postExecute)
        #
        # In IPython >= 2, we can use the event register
        # Events: post_run_cell, pre_run_cell, etc...`
        self.kernel.shell.events.register('pre_execute', self.preExecute)
        self.kernel.shell.events.register('post_execute', self.postExecute)
        self.kernel.shell.events.register('post_run_cell', self.postRunCell)

    def createOpenSomethingWidget(self):
        """
        Create the OpenSomething widget. Do not add it to the layout yet

        TODO:   probably should use a signal to implement this call instead of
                openParTim
        """
        self.openSomethingWidget = OpenSomethingWidget(parent=self.mainFrame, \
                openFile=self.requestOpenPlk)
        self.openSomethingWidget.hide()

    def createPlkWidget(self):
        """
        Create the Plk widget
        """
        self.plkWidget = PlkWidget(parent=self.mainFrame)
        self.plkWidget.hide()

    def createBinaryWidget(self):
        """
        Create the binary model widget
        """
        self.binaryWidget = BinaryWidget(parent=self.mainFrame)
        self.binaryWidget.hide()

    def toggleIPython(self):
        """
        Toggle the IPython widget on or off
        """
        self.setQtipLayout(showIPython=not self.showIPython)

    def toggleBinary(self):
        """
        Toggle the binary model widget on or off
        """
        self.setQtipLayout(whichWidget='binary')

    def togglePlk(self):
        """
        Toggle the plk widget on or off
        """
        self.setQtipLayout(whichWidget='plk')

    def initQtipLayout(self):
        """
        Initialise the Qtip layout
        """
        self.hbox.addWidget(self.openSomethingWidget)
        self.hbox.addWidget(self.plkWidget)
        self.hbox.addWidget(self.binaryWidget)
        self.hbox.addStretch(1)
        self.hbox.addWidget(self.consoleWidget)
        self.mainFrame.setLayout(self.hbox)

    def hideAllWidgets(self):
        """
        Hide all widgets of the mainFrame
        """
        # Remove all widgets from the main window
        # ???
        """
        while self.hbox.count():
            item = self.hbox.takeAt(0)
            if isinstance(item, QtGui.QWidgetItem):
                #item.widget().deleteLater()
                item.widget().hide()
            elif isinstance(item, QtGui.QSpacerItem):
                #self.hbox.removeItem(item)
                pass
            else:
                #fcbox.clearLayout(item.layout())
                #self.hbox.removeItem(item)
                pass
        """
        self.openSomethingWidget.hide()
        self.plkWidget.hide()
        self.binaryWidget.hide()
        self.consoleWidget.hide()

    def showVisibleWidgets(self):
        """
        Show the correct widgets in the mainFrame
        """
        # Add the widgets we need
        if self.whichWidget.lower() == 'opensomething':
            self.openSomethingWidget.show()
        elif self.whichWidget.lower() == 'plk':
            self.plkWidget.show()
        elif self.whichWidget.lower() == 'piccard':
            pass
        elif self.whichWidget.lower() == 'binary':
            self.binaryWidget.show()

        if self.showIPython:
            self.consoleWidget.show()
        else:
            pass

        if self.whichWidget.lower() == 'plk' and not self.showIPython:
            self.plkWidget.setFocusToCanvas()
        elif self.whichWidget.lower() == 'binary' and not self.showIPython:
            self.binaryWidget.setFocusToCanvas()
        #elif self.showIPython:
        #    self.consoleWidget.setFocus()

    def setQtipLayout(self,
                      whichWidget=None,
                      showIPython=None,
                      firsttime=False):
        """
        Given which widgets to show, display the right widgets and hide the rest

        @param whichWidget:     Which widget to show
        @param showIPython:     Whether to show the IPython console
        """
        if not whichWidget is None:
            self.whichWidget = whichWidget
        if not showIPython is None:
            self.showIPython = showIPython

        # After hiding the widgets, wait 25 (or 0?) miliseconds before showing them again
        self.hideAllWidgets()
        QtCore.QTimer.singleShot(0, self.showVisibleWidgets)

        self.prevWhichWidget = self.whichWidget

        if self.showIPython != self.prevShowIPython:
            # IPython has been toggled
            self.prevShowIPython = self.showIPython
            if self.showIPython:
                self.resize(1350, 550)
                self.mainFrame.resize(1350, 550)
            else:
                self.resize(650, 550)
                self.mainFrame.resize(650, 550)

        if firsttime:
            # Set position slightly more to the left of the screen, so we can
            # still open IPython
            self.move(200, 100)

        self.mainFrame.setLayout(self.hbox)
        self.mainFrame.show()


    def requestOpenPlk(self, parfilename=None, timfilename=None, \
            testpulsar=False, engine='libstempo'):
        """
        Request to open a file in the plk widget

        @param parfilename:     The parfile to open. If none, ask the user
        @param timfilename:     The timfile to open. If none, ask the user
        """
        self.setQtipLayout(whichWidget='plk', showIPython=self.showIPython)

        if parfilename is None and not testpulsar:
            parfilename = QtGui.QFileDialog.getOpenFileName(
                self, 'Open par-file', '~/')

        if timfilename is None and not testpulsar:
            timfilename = QtGui.QFileDialog.getOpenFileName(
                self, 'Open tim-file', '~/')

        # Load the pulsar
        self.openPulsar(parfilename, timfilename, engine=engine, \
                testpulsar=testpulsar)

    def setMplColorScheme(self, scheme):
        """
        Set the matplotlib color scheme

        @param scheme:  'black'/'white', the color scheme
        """

        # Obtain the Widget background color
        color = self.palette().color(QtGui.QPalette.Window)
        r, g, b = color.red(), color.green(), color.blue()
        rgbcolor = (r / 255.0, g / 255.0, b / 255.0)

        if scheme == 'white':
            rcP = constants.mpl_rcParams_white

            rcP['axes.facecolor'] = rgbcolor
            rcP['figure.facecolor'] = rgbcolor
            rcP['figure.edgecolor'] = rgbcolor
            rcP['savefig.facecolor'] = rgbcolor
            rcP['savefig.edgecolor'] = rgbcolor
        elif scheme == 'black':
            rcP = constants.mpl_rcParams_black

        for key, value in rcP.iteritems():
            matplotlib.rcParams[key] = value

    def openParTim(self, filename=None, engine='libstempo'):
        """
        Open a par-file and a tim-file
        """
        print("openParTim called with {0}".format(filename))

        # Ask the user for a par and tim file, and open these with libstempo
        if isinstance(filename, str):
            parfilename = filename
        else:
            parfilename = QtGui.QFileDialog.getOpenFileName(
                self, 'Open par-file', '~/')

        timfilename = QtGui.QFileDialog.getOpenFileName(
            self, 'Open tim-file', '~/')

        # Load the pulsar
        self.openPulsar(parfilename, timfilename, engine=engine)

    def openPulsar(self,
                   parfilename,
                   timfilename,
                   engine='libstempo',
                   testpulsar=False):
        """
        Open a pulsar, given a parfile and a timfile

        @param parfilename: The name of the parfile to open
        @param timfilename: The name fo the timfile to open
        @param engine:      Which pulsar timing engine to use [libstempo]
        @param testpulsar:  If True, open the test pulsar (J1744, NANOGrav)
        """
        if engine == 'pint':
            trypint = True
        else:
            trypint = False

        self.engine, pclass = qp.get_engine(trypint=trypint)

        if engine == 'libstempo':
            if not testpulsar:
                # Obtain the directory name of the timfile, and change to it
                timfiletup = os.path.split(timfilename)
                dirname = timfiletup[0]
                reltimfile = timfiletup[-1]
                relparfile = os.path.relpath(parfilename, dirname)
                savedir = os.getcwd()

                # Change directory to the base directory of the tim-file to deal with
                # INCLUDE statements in the tim-file
                os.chdir(dirname)

                # Load the pulsar
                cell = "psr = qp." + pclass + "('" + relparfile + "', '" + reltimfile + "')"
                self.kernel.shell.run_cell(cell)
                psr = self.kernel.shell.ns_table['user_local']['psr']

                # Change directory back to where we were
                os.chdir(savedir)
            else:
                cell = "psr = qp." + pclass + "(testpulsar=True)"
                self.kernel.shell.run_cell(cell)
                psr = self.kernel.shell.ns_table['user_local']['psr']
        elif engine == 'pint':
            cell = "psr = qp." + pclass + "(testpulsar=True)"
            self.kernel.shell.run_cell(cell)
            psr = self.kernel.shell.ns_table['user_local']['psr']
        else:
            raise NotImplemented("Only works with PINT/libstempo")

        # Update the plk widget
        self.plkWidget.setPulsar(psr)
        self.binaryWidget.setPulsar(psr)

        # Communicating with the kernel goes as follows
        # self.kernel.shell.push({'foo': 43, 'print_process_id': print_process_id}, interactive=True)
        # print("Embedded, we have:", self.kernel.shell.ns_table['user_local']['foo'])

    def keyPressEvent(self, event, **kwargs):
        """
        Handle a key-press event

        @param event:   event that is handled here
        """

        key = event.key()

        if key == QtCore.Qt.Key_Escape:
            self.close()
        elif key == QtCore.Qt.Key_Left:
            #print("Left pressed")
            pass
        else:
            #print("Other key")
            pass

        #print("QtipWindow: key press")
        super(QtipWindow, self).keyPressEvent(event, **kwargs)

    def mousePressEvent(self, event, **kwargs):
        """
        Handle a mouse-click event

        @param event:   event that is handled here
        """
        #print("QtipWindow: mouse click")
        super(QtipWindow, self).mousePressEvent(event, **kwargs)

    def preExecute(self):
        """
        Callback function that is run prior to execution of a cell
        """
        pass

    def postExecute(self):
        """
        Callback function that is run after execution of a code
        """
        pass

    def postRunCell(self):
        """
        Callback function that is run after execution of a cell (after
        post-execute)
        """
        # TODO: Do more than just update the plot, but also update _all_ the
        # widgets. Make a callback in plkWidget for that. QtipWindow might also
        # want to loop over some stuff.
        if self.whichWidget == 'plk':
            self.plkWidget.updatePlot()
        elif self.whichWidget == 'binary':
            self.binaryWidget.updatePlot()