Ejemplo n.º 1
0
 def getSampConnect(self):
     ''' Get a SAMP listening client connection.
     '''
     # Get a SAMP client
     client = SAMPIntegratedClient()
     client.connect()
     r = Receiver(client)
     return client
Ejemplo n.º 2
0
def SAMP_conn():
    """a context manager to give the controlled block a SAMP connection.

	The program will disconnect as the controlled block is exited.
	"""
    client = SAMPIntegratedClient(name="serialquery", description="A serial SCS querier.")
    client.connect()
    try:
        yield client
    finally:
        client.disconnect()
 class Receiver(object):
     def __init__(self, client):
         self.client = SAMPIntegratedClient()
         self.received = False
     def receive_call(self, private_key, sender_id, msg_id, mtype, params, extra):
         self.params = params
         self.mtype = mtype
         self.received = True
         self.client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}})
     def receive_notification(self, private_key, sender_id, mtype, params, extra):
         self.mtype = mtype
         self.params = params
         self.received = True
Ejemplo n.º 4
0
def connection(
    client_name="pyvo client", description="A generic PyVO client", **kwargs
):
    """
    a context manager to give the controlled block a SAMP connection.
    The program will disconnect as the controlled block is exited.
    """
    client = SAMPIntegratedClient(
        name=client_name, description=description, **kwargs)
    client.connect()
    try:
        yield client
    finally:
        client.disconnect()
Ejemplo n.º 5
0
def broadcastTable(fileName):
    """
    Sends a table via SAMP.

    Parameters
    ----------
    fileName : str
        Name of sky model file to broadcast

    """
    from astropy.vo.samp import SAMPHubServer, SAMPIntegratedClient, SAMPHubError
    import urlparse

    client = SAMPIntegratedClient()
    client.connect()

    params = {}
    params["url"] = urlparse.urljoin('file:', os.path.abspath(fileName))
    params["name"] = "LSMTool sky model"
    message = {}
    message["samp.mtype"] = "table.load.votable"
    message["samp.params"] = params

    # Send message
    client.call_all('lsmtool', message)

    # Disconnect from the SAMP hub
    client.disconnect()
Ejemplo n.º 6
0
    def connect_samp(self):
        # Create a client. This has to be done on connect, because a
        # disconnected client cannot be reconnected.
        # TODO: Proper icon support.
        self.samp_client = SAMPIntegratedClient(
            metadata = {
                "samp.name":"Orange Client",
                "samp.description.text":"Orange SAMP connectivity",
                "OrangeClient.version":"0.01",
                "samp.icon.url":"http://www.astro.rug.nl/~buddel/tmp/samp.png",
           }
        )

        try:
            self.samp_client.connect()
            #self.samp_client.bind_receive_notification("table.load.votable", self.received_table_load_votable)
            self.samp_client.bind_receive_call("table.load.votable", self.received_table_load_votable_call)
            #self.SAMP_client.bind_receive_call("table.this.is.cool.table", self.table_this_is_cool_table)
            self.samp_client.bind_receive_call("target.object.highlight", self.received_target_object_highlight_call)

            self.infoa.setText("SAMP connected.")
            self.button_connect.setHidden(True)
            self.button_disconnect.setHidden(False)
            
        except Exception as e:
            self.infoa.setText("SAMP error: %s" % e)
    def capCommand(self, *args, **kwargs):
        self.cli = SAMPIntegratedClient()
        self.cli.connect()
        self.r = self.Receiver(self.cli)
        self.dlg.label.setText("Binding methods")
        map(self.bindSamp,["qgis.message","qgis.load.vectorlayer","qgis.script", "table.load.votable"])
        while self.connectionState:
            self.dlg.label.setText("starting")
            self.r.received = False
            while not self.r.received and self.connectionState:
                self.dlg.label.setText("waiting")
                time.sleep(2)
            self.dlg.label.setText("Command recieved")
#            self.cli.disconnect()
            if self.r.mtype == 'qgis.message': 
                self.dlg.label.setText('Message: ' + self.r.params['script'])
            elif self.r.mtype == 'qgis.load.vectorlayer':
                self.dlg.label.setText('loading')
                self.mLoadVectorLayer(self.r.params['url'], self.r.params['name'])
                self.dlg.label.setText('done')
            elif self.r.mtype == 'qgis.script':
                self.dlg.label.setText('Exec: ' + self.r.params['script'])
                exec(self.r.params['script'])
            elif self.r.mtype == 'table.load.votable': #url, table-id, name
                self.dlg.label.setText('Votable url: '+self.r.params['url'])
                vot = Table.read(self.r.params['url'])
#                self.dlg.label.setText(str(len(vot))
#                self.dlg.label.setText('table captured')
#                vot.show_in_browser(jsviewer=True) #should open table in a browser
                destination=tempfile.mkdtemp()
#                destination+='/vot.shp'
                destination+='/vot.geojson'
#                destination='/home/mminin/Documents/temp/shp/crism01.shp'
                self.dlg.label.setText('converting to geojson')
#                self.convertVOTtoSHP(vot, destination)
                self.convertVOTtoGEOJSON(vot, destination)
                self.dlg.label.setText('loading to map')
#                if self.r.params
                self.mLoadVectorLayer(destination, self.r.params['name'])
                self.dlg.label.setText('loaded')
                time.sleep(5)
            time.sleep(2)
            self.dlg.label.setText("slept")
        self.cli.disconnect()
        self.dlg.label.setText("Disconnected")
        return
Ejemplo n.º 8
0
def broadcastTable(fileName):
    """
    Sends a table via SAMP.

    Parameters
    ----------
    fileName : str
        Name of sky model file to broadcast

    """
    from astropy.vo.samp import SAMPHubServer, SAMPIntegratedClient, SAMPHubError
    import urlparse

    client = SAMPIntegratedClient()
    client.connect()

    params = {}
    params["url"] = urlparse.urljoin('file:', os.path.abspath(fileName))
    params["name"] = "LSMTool sky model"
    message = {}
    message["samp.mtype"] = "table.load.votable"
    message["samp.params"] = params

    # Send message
    client.call_all('lsmtool', message)

    # Disconnect from the SAMP hub
    client.disconnect()
Ejemplo n.º 9
0
def send_file( infile ):
     
     """

     Sending a file (image or table) to Aladin Sky Atlas using the SAMPIntegratedClient class.
             http://docs.astropy.org/en/stable/vo/samp/example_table_image.html

     """
     
     from astropy.vo.samp import SAMPIntegratedClient
     
     client = SAMPIntegratedClient()
     client.connect()

     params = {}
     import urlparse
     import os.path
     params[ "url" ] = urlparse.urljoin( 'file:',
				 os.path.abspath( infile ) )

     message = {}
     message[ "samp.mtype" ] = "image.load.fits"
     message[ "samp.params" ] = params
     
     client.notify_all( message )

     client.disconnect()
Ejemplo n.º 10
0
def SAMP_send_fits(filename,longname):
    client = SAMPIntegratedClient()
    client.connect()
    params = {}
    params["url"] = 'file://'+os.getcwd()+'/'+filename
    params["name"] = longname
    message = {}
    message["samp.mtype"] = "image.load.fits"
    message["samp.params"] = params
    client.notify_all(message)
    client.disconnect()
Ejemplo n.º 11
0
def SAMP_conn():
    """a context manager to give the controlled block a SAMP connection.

	The program will disconnect as the controlled block is exited.
	"""
    client = SAMPIntegratedClient(name="serialquery",
                                  description="A serial SCS querier.")
    client.connect()
    try:
        yield client
    finally:
        client.disconnect()
Ejemplo n.º 12
0
Archivo: samp.py Proyecto: eteq/pyvo
def connection(client_name="pyvo client",
               description="A generic PyVO client",
               **kwargs):
    """
    a context manager to give the controlled block a SAMP connection.
    The program will disconnect as the controlled block is exited.
    """
    client = SAMPIntegratedClient(name=client_name,
                                  description=description,
                                  **kwargs)
    client.connect()
    try:
        yield client
    finally:
        client.disconnect()
Ejemplo n.º 13
0
def send_script( script ):

     """

         Sending a script to Aladin Sky Atlas using the SAMPIntegratedClient class.
               http://docs.astropy.org/en/stable/vo/samp/example_table_image.html

     """

     from astropy.vo.samp import SAMPIntegratedClient
     
     client = SAMPIntegratedClient()
     client.connect()

     params = {}
     message = {} 
     message[ "samp.mtype" ] = "script.aladin.send"
     message[ "samp.params" ] = { "script" : script }  

     client.notify_all( message )

     client.disconnect()
Ejemplo n.º 14
0
 def __init__(self):
     self._client = SAMPIntegratedClient()
Ejemplo n.º 15
0
class AladinViaSAMP(object):
    def __init__(self):
        self._client = SAMPIntegratedClient()

    def send_file(self, infile=str()):
        """Sending a file (image or table) to Aladin Sky Atlas using the SAMPIntegratedClient class.
             http://docs.astropy.org/en/stable/vo/samp/example_table_image.html
        """

        self._client.connect()

        params = {}
        params["url"] = urlparse.urljoin('file:', os.path.abspath(infile))
        message = {}
        message["samp.mtype"] = "image.load.fits"
        message["samp.params"] = params

        self._client.notify_all(message)
        self._client.disconnect()

    def send_script_command(self, script=str()):
        """Sending a script to Aladin Sky Atlas using the SAMPIntegratedClient class.
           http://docs.astropy.org/en/stable/vo/samp/example_table_image.html
         """

        self._client.connect()

        params = {}
        message = {}
        message["samp.mtype"] = "script.aladin.send"
        message["samp.params"] = {"script": script}

        self._client.notify_all(message)
        self._client.disconnect()
Ejemplo n.º 16
0
                layerTimeStamp = layerInfo['timestamp']
                layers.append(
                    LayerHandler(client, layerTimeStamp, observatory,
                                 instrument, detector, measurement, cutout))

            handler = VSOHandler(client, start, end, timestamp, cadence,
                                 layers)

            return handler


# The next cell connects to an active Hub (see http://docs.astropy.org/en/stable/vo/samp/example_table_image.html)

# In[ ]:

client = SAMPIntegratedClient()
client.connect()

r = Receiver(client)
client.bind_receive_call("jhv.vso.load", r.receive_call)
client.bind_receive_notification("jhv.vso.load", r.receive_notification)

# ### Downloading the data
# When a message is sent over SAMP, run the following to create a new Handler with the parameters given over SAMP.
#
# A Handler object named handler will be created which offers several utility methods to create a VSOQuery and download the data.
# * __handler.createQuerySingleResult()__
#   Creates a VSO-Query to only retrieve the currently show images in JHelioviewer for each active Layer
# * __handler.createQuery()__
#   Creates a VSO-Query for the complete timespan visible in JHelioviewer
# * __handler.showQuery()__
Ejemplo n.º 17
0
if len(sys.argv) < 2:
    os.system(folder + "/pipe.py")
    sys.exit(1)

if 'web' not in sys.argv[1]:
    os.system(folder + "/pipe.py " + " ".join(sys.argv[2:]))
    sys.exit(1)

import time
from astropy.vo.samp import SAMPIntegratedClient
from astropy.table import Table
import websocket
import webbrowser

# Instantiate the client and connect to the hub
client = SAMPIntegratedClient(name="samp-ws bridge")
client.connect()

host = "127.0.0.1"
port = "9000"

if len(sys.argv) > 2:
    host = sys.argv[2]

if len(sys.argv) > 3:
    port = int(sys.argv[3])


# Set up a receiver class
class Receiver(object):
    def __init__(self, client):
Ejemplo n.º 18
0
class AladinViaSAMP(object):

    def __init__(self):
        self._client = SAMPIntegratedClient()
        
    def send_file(self, infile=str()):
        """Sending a file (image or table) to Aladin Sky Atlas using the SAMPIntegratedClient class.
             http://docs.astropy.org/en/stable/vo/samp/example_table_image.html
        """   
     
        self._client.connect()

        params = {}       
        params[ "url" ] = urlparse.urljoin( 'file:',
				 os.path.abspath( infile ) )
        message = {}
        message[ "samp.mtype" ] = "image.load.fits"
        message[ "samp.params" ] = params
     
        self._client.notify_all(message)
        self._client.disconnect()

    def send_script_command(self, script=str()):
        """Sending a script to Aladin Sky Atlas using the SAMPIntegratedClient class.
           http://docs.astropy.org/en/stable/vo/samp/example_table_image.html
         """

        self._client.connect()

        params = {}
        message = {} 
        message[ "samp.mtype" ] = "script.aladin.send"
        message[ "samp.params" ] = { "script" : script }  

        self._client.notify_all(message)
        self._client.disconnect()
Ejemplo n.º 19
0
 def __init__(self):
     self._client = SAMPIntegratedClient()
class VOScriptReceiver:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        self.r=mvoid()
        self.r.params={'script':''}
        self.dlg = VOScriptReceiverDialog()
        self.connectionState=False
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'VOScriptReceiver_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)


        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&VOScriptReceiver')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'VOScriptReceiver')
        self.toolbar.setObjectName(u'VOScriptReceiver')
        # Tie in connection button
        self.dlg.connectBtn.clicked.connect(self.switchCState)

    def switchCState(self, *args, **kwargs): 
        self.connectionState=not self.connectionState
        if self.connectionState:
            self.t=threading.Thread(name="vodka", target=self.capCommand, args=(self,))
            self.t.start()
        else:
            self.dlg.label.setText("Disconnected")

    def LoadVectorLayer(self):
        pass


    class Receiver(object):
        def __init__(self, client):
            self.client = SAMPIntegratedClient()
            self.received = False
        def receive_call(self, private_key, sender_id, msg_id, mtype, params, extra):
            self.params = params
            self.mtype = mtype
            self.received = True
            self.client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}})
        def receive_notification(self, private_key, sender_id, mtype, params, extra):
            self.mtype = mtype
            self.params = params
            self.received = True

    def mLoadVectorLayer(self, mURL, mName):
        self.iface.mapCanvas().freeze() 
        self.dlg.label.setText('doing')
        #'/home/mminin/Documents/temp/shp/test.shp'
        self.mylayer=QgsVectorLayer(mURL, mName,'ogr')
        self.dlg.label.setText(str(self.mylayer))
        self.ProjInstance=QgsProject.instance()
        self.root=self.ProjInstance.layerTreeRoot()
        QgsMapLayerRegistry.instance().addMapLayer(self.mylayer)
        self.root.addLayer(self.mylayer)
        self.iface.mapCanvas().freeze(False) 

    def bindSamp(self, MType):
        self.cli.bind_receive_call(        MType, self.r.receive_call)
        self.cli.bind_receive_notification(MType, self.r.receive_notification)

    def convertVOTtoSHP(self, vot, destination):
        self.dlg.label.setText('init writer')
        w = shapefile.Writer(shapefile.POLYGON)
        self.dlg.label.setText('write fields')
        for colname in vot.colnames: 
            if colname=='s_region' or colname=='access_url' or colname=='thumbnail_url':
                w.field(colname, 'C', '254')
            else:
                w.field(colname)
        self.dlg.label.setText('define getParts')
        def getParts(sRegion):
            lon=sRegion.split(' ')[2:][0::2]
            llon=np.asarray([float(i) for i in lon])
            spread=llon.max()-llon.min() ###
            if spread > 180:
                lon = [[x, x-360][x>180] for x in llon]
            lat=sRegion.split(' ')[2:][1::2]
            return [[[360-float(lon[i]),float(lat[i])] for i in range(len(lat))]]
        self.dlg.label.setText('define writeRecord')
        def writeRecord(rowNumber):
            w.poly(getParts(vot['s_region'][rowNumber]))
            w.record(*list(vot[rowNumber].as_void()))
        self.dlg.label.setText('writeRecords')
        for i in range(len(vot)): writeRecord(i)
        self.dlg.label.setText('save file: ' + destination)
        w.save(destination)
        self.dlg.label.setText('saved at: ' + destination)


    def convertVOTtoGEOJSON(self, vot, destination):
        self.dlg.label.setText('defining getParts')
        def getParts(sRegion):
#            lon=sRegion.split(' ')[2:][0::2]
            lon=sRegion.split(' ')[2:][0::2] + sRegion.split(' ')[2:3]
            llon=np.asarray([float(i) for i in lon])
            spread=llon.max()-llon.min() ###
            if spread > 180:
                lon = [[x, x-360][x>180] for x in llon]
#            lat=sRegion.split(' ')[2:][1::2]
            lat=sRegion.split(' ')[2:][1::2] + sRegion.split(' ')[3:4]
            return [[[360-float(lon[i]),float(lat[i])] for i in range(len(lat))]]
        self.dlg.label.setText('making feature')
        makeFeature=lambda coords, props: {"type":"Feature","geometry": { "type": "Polygon", "coordinates": coords},"properties": props}
        self.dlg.label.setText('making dump')
        makeJSONdump=lambda featList: geojson.dumps({"type":"FeatureCollection","features":featList})
        self.dlg.label.setText('making path')
#        tempPath=destination+'vot.geojson'
#        mycoords=[[1,2],[3,4],[5,1]]
        self.dlg.label.setText('completing feature')
        makeCompleteFeature=lambda vot, rowN: makeFeature(getParts(vot['s_region'][rowN]),dict(zip(vot.colnames,[str(x).replace('MASKED','') for x in vot[rowN]])))
        self.dlg.label.setText('making list')
        self.dlg.label.setText('starting iter '+ str(len(vot)) )
        featList=[]
        for i in range(len(vot)):
            self.dlg.label.setText('writing number' + str(i))
            featList.append(makeCompleteFeature(vot, i))
        self.dlg.label.setText('dumping')
        d=makeJSONdump(featList)
        self.dlg.label.setText('writing')
        f=open(destination, 'w')
        f.write(d)
        f.close()
        self.dlg.label.setText('finished')

    def capCommand(self, *args, **kwargs):
        self.cli = SAMPIntegratedClient()
        self.cli.connect()
        self.r = self.Receiver(self.cli)
        self.dlg.label.setText("Binding methods")
        map(self.bindSamp,["qgis.message","qgis.load.vectorlayer","qgis.script", "table.load.votable"])
        while self.connectionState:
            self.dlg.label.setText("starting")
            self.r.received = False
            while not self.r.received and self.connectionState:
                self.dlg.label.setText("waiting")
                time.sleep(2)
            self.dlg.label.setText("Command recieved")
#            self.cli.disconnect()
            if self.r.mtype == 'qgis.message': 
                self.dlg.label.setText('Message: ' + self.r.params['script'])
            elif self.r.mtype == 'qgis.load.vectorlayer':
                self.dlg.label.setText('loading')
                self.mLoadVectorLayer(self.r.params['url'], self.r.params['name'])
                self.dlg.label.setText('done')
            elif self.r.mtype == 'qgis.script':
                self.dlg.label.setText('Exec: ' + self.r.params['script'])
                exec(self.r.params['script'])
            elif self.r.mtype == 'table.load.votable': #url, table-id, name
                self.dlg.label.setText('Votable url: '+self.r.params['url'])
                vot = Table.read(self.r.params['url'])
#                self.dlg.label.setText(str(len(vot))
#                self.dlg.label.setText('table captured')
#                vot.show_in_browser(jsviewer=True) #should open table in a browser
                destination=tempfile.mkdtemp()
#                destination+='/vot.shp'
                destination+='/vot.geojson'
#                destination='/home/mminin/Documents/temp/shp/crism01.shp'
                self.dlg.label.setText('converting to geojson')
#                self.convertVOTtoSHP(vot, destination)
                self.convertVOTtoGEOJSON(vot, destination)
                self.dlg.label.setText('loading to map')
#                if self.r.params
                self.mLoadVectorLayer(destination, self.r.params['name'])
                self.dlg.label.setText('loaded')
                time.sleep(5)
            time.sleep(2)
            self.dlg.label.setText("slept")
        self.cli.disconnect()
        self.dlg.label.setText("Disconnected")
        return

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        return QCoreApplication.translate('VOScriptReceiver', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):

        # Create the dialog (after translation) and keep reference
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/VOScriptReceiver/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'VO script receiver'),
            callback=self.run,
            parent=self.iface.mainWindow())


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&VOScriptReceiver'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar


    def run(self):
        """Run method that performs all the real work"""
        # show the dialog
        self.dlg.show()
        self.dlg.label.setText("hello")
        self.t=threading.Thread(name="hubClient", target=self.capCommand, args=(self,))
        self.t.start()
        # Run the dialog event loop
        result = self.dlg.exec_()
#        self.t.join()
        if self.connectionState: self.cli.disconnect()
        # See if OK was pressed
        if result:
            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            pass
Ejemplo n.º 21
0
class OWSAMP(OWWidget):
    """
    The SAMP widget allows data to be received or requested through SAMP.
    SAMP is the Simple Application Messaging Protocol designed for the
    Virtual Observatory.
    """
    name = "SAMP"
    id = "orange.widgets.data.samp"
    description = """
    Creates a SAMP connection and requests or receives data."""
    long_description = """
    Creates a SAMP connection and requests or receives data."""
    icon = "icons/SAMP.svg"
    author = "Hugo Buddelmeijer"
    maintainer_email = "buddel(@at@)astro.rug.nl"
    priority = 10
    category = "Data"
    keywords = ["data", "file", "load", "read", "lazy"]
    outputs = [OutputSignal(
        "Data",
        LazyTable,
        doc = "Attribute-valued data set received over SAMP."
    )]

    # TODO: move this to LazyTable
    stop_pulling = False
    #stop_pulling = True

    region_of_interest = None
    """region_of_interest specifies what part of the data set is interesting
    according to widgets further in the scheme. See in_region_of_interest()
    of LazyRowInstance for information about its structure."""

    # catalog_of_interest = "100511" # still to complex
    #_catalog_of_interest = "892271" # based on KiDS DR2
    #_catalog_of_interest = "898581" # SA based on 892271
    #_catalog_of_interest = "898771" # SLW based on SA above
    _catalog_of_interest = "" # Can now be set through SAMP.


    """catalog_of_interest specifies the catalog that somehow has been
    set as interesting. Data is pulled from this catalog. For now this
    is hardcoded."""
    # TODO: Store the catalog_of_interest as a setting.
    # TODO: Use contexts etc. like other widgets do to handle multiple tables
    #   at the same time.
    # TODO: Allow the catalog_of_interest to be set over SAMP in some way.
    # TODO: Perhaps integrate region_of_interest with catalog_of_interest
    #   in some way?

    @property
    def catalog_of_interest(self):
        """
        The catalog of interest. Currently identified by its name, which
        currently is just a number.
        """
        return self._catalog_of_interest

    @catalog_of_interest.setter
    def catalog_of_interest(self, value):
        self._catalog_of_interest = value
        self.we_have_a_new_table()

    def we_have_a_new_table(self):
        # TODO: think of better name for this function.
        domain = self.pull_domain()
        self.old_region_of_interest = None
        #self.region_of_interest = None
        data = LazyTable.from_domain(domain=domain)
        data.widget_origin = self
        # Stop the pulling loop in our current data, if any.
        if isinstance(self.data, LazyTable):
            self.data.stop_pulling = True
        
        self.data = data
        self.send("Data", self.data)
        print("Orange Table send B")

    def pull_length(self):
        # TODO: implement
        #length = 0
        length = self.data.X.shape[0] if self.data is not None else 0
        #length = 1000000 if self.data is not None else 0        
        return length

    def __init__(self):
        super().__init__()

        self.data = None
        """The LazyTable that will be send."""

        # GUI: as simple as possible for now
        box = gui.widgetBox(self.controlArea, "SAMP Info")
        self.infoa = gui.widgetLabel(widget=box, label='SAMP status unknown.')

        box_input_catalog = gui.widgetBox(box, orientation=0)
        self.input_catalog_text = gui.widgetLabel(widget=box_input_catalog , label='Catalog')
        self.input_catalog = gui.lineEdit(widget=box_input_catalog , master=self, value='catalog_of_interest')

        #self.resize(100,50)
        self.button_disconnect = gui.button(self.controlArea, self, "&Disconnect", callback=self.disconnect_samp, default=False)
        self.button_connect = gui.button(self.controlArea, self, "&Connect", callback=self.connect_samp, default=False)
        self.button_disconnect.setHidden(True)
        #gui.button(self.controlArea, self, "&Pull Rows", callback=self.pull_rows, default=False)
        gui.button(self.controlArea, self, "&Set Table", callback=self.we_have_a_new_table, default=False)

        # Create a SAMP client and connect to HUB.
        # Do not make the client in __init__ because this does not allow
        # the client to disconnect and reconnect again.
        self.samp_client = None
        self.connect_samp()

        #self.pull_rows()

    def pull_region_of_interest(self):
        if self.region_of_interest is None:
            self.region_of_interest = {
               #'RA': (1., 359.),
               #'DEC': (-89., 89.),
            }

        if self.old_region_of_interest == self.region_of_interest:
            # We are already getting this data. With SAMP we don't have to
            # keep requesting.
            # TODO: make SAMP widget only keep sending data upon request
            pass
        else:
            self.old_region_of_interest = self.region_of_interest
            print("Pulling ROI",self.region_of_interest)
            self.pull_rows()

    counter_msg_tag = 123

    def pull_rows(self):
        """
        First experiment for data through SAMP. This is a prototype.
        """
        if self.data is None:
            self.we_have_a_new_table()

        extra_attributes = ['A', 'B', 'SLID', 'SID', 'HTM']

        # TODO: Proper SQL creation
        if self.region_of_interest is None:
            region_of_interest_in_sql = ""
        elif isinstance(self.region_of_interest, dict):
            region_of_interest_in_sql = " AND ".join(
                ''' "%s" BETWEEN %f AND %f ''' % (
                    name, values[0], values[1]
                ) for (name, values) in self.region_of_interest.items()
            ) if len(self.region_of_interest) else ""
        else:
            region_of_interest_in_sql = " AND ".join(
                to_sql(f)
                for f in self.region_of_interest.conditions
            ) if len(self.region_of_interest.conditions) else ""

        # TODO: Make it possible to specify attributes.
        #   However, this might require significant changes in the way the
        #   data is stored in the LazyTable.
        message = {
            'samp.mtype':'catalog.pull',
            'samp.params':{
                'startsc': str(self.catalog_of_interest),
                'query': urllib.parse.unquote_plus(region_of_interest_in_sql),
                #'attributes': [attribute for attribute in self.region_of_interest],
                #'attributes': [attribute for attribute in self.region_of_interest] + extra_attributes,
                'attributes': [attribute.name for attribute in self.data.domain],
            },
        }
        print("OWSAMP pull_rows", message['samp.params']['startsc'], message['samp.params']['query'])
        #        'query': urllib.unquote_plus('ROWNUM <= %i' % (row_index)),
        #        'query': urllib.unquote_plus('"R" < 300'),
        #attributes ['absMag_u', 'absMag_g', 'iC']

        # TODO: Abstract call and properly implement msg_tag.
        self.counter_msg_tag += 1
        self.samp_client.call_all("pull_nr_%i" % (self.counter_msg_tag), message)

    def pull_domain(self):
        """
        Requests information about the table over SAMP.
        """
        message = {
            'samp.mtype':'target.object.info',
            'samp.params':{
                'class': 'SourceCollection',
                'id': self.catalog_of_interest,
            },
        }

        # TODO: not use call_and_wait, just call
        id_awe = list(self.samp_client.get_subscribed_clients(mtype="target.object.info"))[0]
        reply = self.samp_client.call_and_wait(message=message, timeout="20", recipient_id = id_awe)

        columns = [a['value'] for a in reply['samp.result']['properties'] if a['name'][:10] == 'attribute|']

        # Create a Domain.
        # TODO: Y en metas, but we don't have this information!
        if False:
            attributes = [
                ContinuousVariable(name=column)
                for column in columns
            ]
            domain = Domain(attributes = attributes)
        
        # So we fake it. 'CLASS' in the column name means it is a
        # class.
        # TODO: Properly fix this. Allow more than two classes.
        #   dynamic class names.
        attributes = [
            ContinuousVariable(name=column)
            for column in columns if not 'CLASS' in column
        ]
        class_vars = [
            DiscreteVariable(name=column, values=['alpha', 'beta'])
            for column in columns if 'CLASS' in column
        ]
        domain = Domain(
            attributes=attributes,
            class_vars=class_vars,
        )

        print("Domain made")

        return domain





    def received_table_load_votable(self, private_key, sender_id, msg_id, mtype, parameters, extra):
        """
        Read the received VOTable and broadcast.
        """
        print("Call:", private_key, sender_id, msg_id, mtype, parameters, extra)

        # Retrieve and read the VOTable.
        url_table = parameters['url']

        # sys.stdout is redirected by canvas.__main__ via redirect_stdout()
        # in canvas.util.redirect to an
        # Orange.canvas.application.outputview.TextStream object. This
        # has a @queued_blocking flush(), which can result in an "Result not
        # yet ready" RuntimeError from the QueuedCallEvent class.
        # This exception is raised because astropy.io.votable.table uses
        # astropy.utils.xml.iterparser, which uses astropy.utils.data,
        # which uses the Spinner class from astropy.utils.console, which
        # finally uses stdout to output a progress indicator.
        # Orange has its own mechanisms for indicating progress, so it would
        # perhaps be better to try to use that.
        # For now, the Orange redirect of stdout is temporarily disabled
        # while the votable is being parsed.

        stdout_orange = sys.stdout
        sys.stdout = sys.__stdout__
        votable_tree = votable.parse(url_table)
        sys.stdout = stdout_orange

        print("VOTable Tree created")
        votable_table = votable_tree.get_first_table()
        #type(votable)
        #<class 'astropy.io.votable.tree.Table'>
        table = votable_table.to_table()
        #type(table)
        #<class 'astropy.table.table.Table'>
        print("AstroPy table made")



        # This does not allow classes.
        if False:
            # Convert the VOTable to a Domain.
            # TODO: Y en metas
            attributes = [
                ContinuousVariable(name=column)
                for column in table.columns
            ]
            domain = Domain(attributes = attributes)
            print("Domain made")

            # Convert the VOTable to a Table
            # Append the Table to LazyTable self.data.
            # (Re)send self.data).
            # TODO: Use from_domain() implicitly from __init__().
            # TODO: Include support to stop_pulling immediately.
            otable = Table.from_domain(
                #otable = LazyTable.from_domain(
                #otable = LazyTable(
                domain = domain,
                n_rows = len(table),
                #    stop_pulling = True,
            )
            otable.stop_pulling = True
            # TODO: set widget_origin?
            print("Orange Table initialized")
            for i, variable in enumerate(otable.domain.variables):
                otable.X[:,i] = table.columns[variable.name].data

        attributes = [
            ContinuousVariable(name=column)
            for column in table.columns if not 'CLASS' in column
        ]
        class_vars = [
            DiscreteVariable(name=column, values=['alpha', 'beta'])
            for column in table.columns if 'CLASS' in column
        ]
        domain = Domain(
            attributes=attributes,
            class_vars=class_vars,
        )

        otable = Table.from_domain(
            domain = domain,
            n_rows = len(table),
        )
        otable.stop_pulling = True
        print("Orange Table initialized")
        for i, variable in enumerate(otable.domain.attributes):
            otable.X[:,i] = table.columns[variable.name].data
        for i, variable in enumerate(otable.domain.class_vars):
            #otable.Y[:,i] = table.columns[variable.name].data
            #otable.Y[:] = table.columns[variable.name].data
            otable.Y[:] = table.columns[variable.name].data.round()

        print("Orange Table filled")
        if self.data is None:
            self.data = otable
        else:
            self.data.extend(otable)
            # TODO: Why doesn't this work?:
            #for row in otable:
            #    self.data.append(row)


        self.send("Data", self.data)
        print("Orange Table send A")

    # TODO: Implement the messages_received_cache in a nicer way.
    messages_received_cache = []
    def received_table_load_votable_call(self, private_key, sender_id, msg_id, mtype, parameters, extra):
        """
        Receive a VOTable and reply with success.

        TODO: Only reply with success if there was no problem.
        """
        if msg_id in self.messages_received_cache:
            print("Message repeated!", msg_id)
            #self.samp_client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}})
            return

        self.messages_received_cache.append(msg_id)
        print("Call:", private_key, sender_id, msg_id, mtype, parameters, extra)
        self.samp_client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}})
        print("Reply Send")
        self.received_table_load_votable(private_key, sender_id, msg_id, mtype, parameters, extra)
        print("Table loaded")

    def received_target_object_highlight_call(self, private_key, sender_id, msg_id, mtype, parameters, extra):
        if msg_id in self.messages_received_cache:
            print("Message repeated!", msg_id)
            #self.samp_client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}})
            return

        self.messages_received_cache.append(msg_id)
        print("Call:", private_key, sender_id, msg_id, mtype, parameters, extra)
        self.samp_client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}})
        print("Reply Send")
        self.catalog_of_interest = parameters['id']
        print("Table loaded")


    def disconnect_samp(self):
        """Disconnect from the SAMP HUB"""
        self.samp_client.disconnect()
        self.infoa.setText("SAMP disconnected.")
        self.button_disconnect.setHidden(True)
        self.button_connect.setHidden(False)

    def connect_samp(self):
        # Create a client. This has to be done on connect, because a
        # disconnected client cannot be reconnected.
        # TODO: Proper icon support.
        self.samp_client = SAMPIntegratedClient(
            metadata = {
                "samp.name":"Orange Client",
                "samp.description.text":"Orange SAMP connectivity",
                "OrangeClient.version":"0.01",
                "samp.icon.url":"http://www.astro.rug.nl/~buddel/tmp/samp.png",
           }
        )

        try:
            self.samp_client.connect()
            #self.samp_client.bind_receive_notification("table.load.votable", self.received_table_load_votable)
            self.samp_client.bind_receive_call("table.load.votable", self.received_table_load_votable_call)
            #self.SAMP_client.bind_receive_call("table.this.is.cool.table", self.table_this_is_cool_table)
            self.samp_client.bind_receive_call("target.object.highlight", self.received_target_object_highlight_call)

            self.infoa.setText("SAMP connected.")
            self.button_connect.setHidden(True)
            self.button_disconnect.setHidden(False)
            
        except Exception as e:
            self.infoa.setText("SAMP error: %s" % e)
    
        # For debuging.
        #self.received_table_load_votable(private_key='', sender_id='', msg_id= '', mtype='', parameters={'url':'/home/evisualization/voclass/testclassification.votable'}, extra='')


    def set_region_of_interest(self, region_of_interest):
        """
        A region of interest has been indicated, probably by the user.
        Give preference to data in this region when pulling more data.
        """
        self.region_of_interest = region_of_interest

    def onDeleteWidget(self):
        """Disconnect from the SAMP Hub on exit."""
        if isinstance(self.data, LazyTable):
            self.data.stop_pulling = True
        
        self.disconnect_samp()
        super().onDeleteWidget()
 def __init__(self, client):
     self.client = SAMPIntegratedClient()
     self.received = False