def __init__(self):
        self.__db_manager = DBManager()
        self.__helper = GeneralHelpers()
        self.__plot_manager = PlotManager()
        self.__import_manager = ImportManager()
        self.__feature_manager = FeatureManager()

        self.years = ("2012", "2013", "2014", "2015")
def createPlotManager(_viewManager=None,
                      _useVTKFlag=False):  # called from SimpleTabView
    plotSupportFlag = checkSupportForPlotting(_useVTKFlag)
    print '------ PlotManagerSetup.py:    plotSupportFlag=', plotSupportFlag
    if plotSupportFlag < 0:  # via Qwt
        from PlotManager import PlotManager
        print '------ PlotManagerSetup.py:    importing PyQwt PlotManager'
        return PlotManager(_viewManager, plotSupportFlag)
    elif plotSupportFlag > 0:  # via VTK
        from PlotManagerVTK import PlotManager
        return PlotManager(_viewManager, plotSupportFlag)
    else:  # plotting not possible
        return PlotManagerBase(_viewManager, plotSupportFlag)
Esempio n. 3
0
def createPlotManager(_viewManager=None, preferred_manager_type=None):  # called from SimpleTabView
    """

    @param _viewManager:instance of viewManager
    @param preferred_manager_type: not used in currnt API in the future versions
    it will be used to instantiate preferred plotting backend
    @return: instance of PlotManagerBase (inherited)
    """
    # plotSupportFlag = checkSupportForPlotting(_useVTKFlag)


    # import pyqtgraph
    # import PlotManager
    # from PlotManager import PlotManager
    # return PlotManager(_viewManager, True)
    # return None

    try:
        import pyqtgraph
        from PlotManager import PlotManager
        return PlotManager(_viewManager, True)
    except ImportError as e:
        print 'Could not create PLotManager. resorting to PLotManagerBase - plots will not work properly. Here is the exception:'
        print e
        return PlotManagerBase(_viewManager, False)
def createPlotManager(_viewManager=None):
    plotSupportFlag = checkSupportForPlotting()
    if plotSupportFlag:
        from PlotManager import PlotManager
        return PlotManager(_viewManager, plotSupportFlag)
    else:
        return PlotManagerBase(_viewManager, plotSupportFlag)
Esempio n. 5
0
    def __init__(self, id, xml_view_child):

        print("New view: ",xml_view_child.attrib)
        self.id = id
        self.name = xml_view_child.attrib["name"]

        self.fig, self.ax = plt.subplots(int(xml_view_child.attrib["nrows"]), int(xml_view_child.attrib["ncols"]))

        self.plots = {}

        for xml_plot_child in xml_view_child:
            plot_id = xml_plot_child.attrib["id"]
            self.plots[plot_id] = PlotManager(plot_id, self.ax)
Esempio n. 6
0
    def __trueInit(self, sgPyQt=None):
        if sgPyQt is None:
            import SalomePyQt
            sgPyQt = SalomePyQt.SalomePyQt()
        self._sgPyQt = sgPyQt
        self._modelViews = {}
        self._browserContextualMenu = None
        self._blockNotifications = False
        self._blockViewClosing = False
        self._callbacks = []

        self._plotManager = PlotManager(self)

        if self.WITH_CURVE_BROWSER:
            self._curveBrowserView = CurveBrowserView(self)
            self.associate(self._plotManager, self._curveBrowserView)
        else:
            self._curveBrowserView = None
        if self.WITH_CURVE_TABS:
            self._curveTabsView = CurveTabsView(self)
            self.associate(self._plotManager, self._curveTabsView)
        else:
            self._curveTabsView = None
        PlotController.__UNIQUE_INSTANCE = self
Esempio n. 7
0
 def __init__(self, canvas, *arg, **kw):
     self._width = kw.get('width', 800)
     self._height = kw.get('height', 600)
     if (canvas != None):
         self._canvas = canvas
     else:
         self._canvas = vcs.init(geometry={
             'width': self._width,
             'height': self._height
         },
                                 bg=1)
         self._canvas.open()
         self._canvas.backend.renWin.AddObserver("ModifiedEvent",
                                                 self.modifiedEvent)
     self._plot = PlotManager(self._canvas)
     self._plot.graphics_method = vcs.getisofill()  # default
     self._plot.template = vcs.gettemplate('default')  # default
     self._insideModifiedEvent = False
 def __trueInit(self, sgPyQt=None):
   if sgPyQt is None:
     import SalomePyQt
     sgPyQt = SalomePyQt.SalomePyQt()
   self._sgPyQt = sgPyQt
   self._modelViews = {}
   self._browserContextualMenu = None
   self._blockNotifications = False
   self._blockViewClosing = False
   self._callbacks = []
   
   self._plotManager = PlotManager(self)
   
   if self.WITH_CURVE_BROWSER:
     self._curveBrowserView = CurveBrowserView(self)
     self.associate(self._plotManager, self._curveBrowserView)
   else:
     self._curveBrowserView = None  
   if self.WITH_CURVE_TABS:
     self._curveTabsView = CurveTabsView(self)
     self.associate(self._plotManager, self._curveTabsView)
   else:
     self._curveTabsView = None
   PlotController.__UNIQUE_INSTANCE = self
class Main:
    """
    Main class, makes necessary function calls to necessary classes
    """

    def __init__(self):
        self.__db_manager = DBManager()
        self.__helper = GeneralHelpers()
        self.__plot_manager = PlotManager()
        self.__import_manager = ImportManager()
        self.__feature_manager = FeatureManager()

        self.years = ("2012", "2013", "2014", "2015")

    def retrieve_tweets(self, file_path_of_ids):
        """
        Runs Import Manager to retrieve and import tweets
        :param file_path_of_ids: String, file path of tweets to import
        :return: void
        """
        self.__import_manager.run(file_path_of_ids)

    def extract_features_and_generate_arff(self, n=3, analyzer='char', year='2012'):
        """
        Makes necessary function calls to extract features for given year and to generate arff file
        :param n: int, ngram count
        :param analyzer: string, word or char
        :param year: string, 2012, 2013, 2014, 2015 or ALL
        :return: string, path of generated arff file
        """

        # Getting tweets with year
        print("Getting tweets for year "+ year)
        tweets_for_given_year = self.__db_manager.get_tweets_for_year(year)

        print("Generating document and classes of tweets.")
        document, classes = self.__feature_manager.create_document_and_classes_for_tweets(tweets_for_given_year, True)

        print("Fitting the data, finding ngrams and frequencies.")
        ngrams, arff_data, vectorizer, X = self.__feature_manager.fit_data(document, classes, n, analyzer)

        print("Formatting the data for arff lib format.")
        formatted_arff_data = self.__feature_manager.format_data_for_arff(ngrams, arff_data)

        print("Generating file.")
        # Experiment name, 1grams, 2grams, 3grams.. or words
        experiment_name = str(n)+'Gram' if analyzer == 'char' else 'Word'

        # File name, TTNet_3grams_2012
        file_name = MODEL_NAME + '_' + experiment_name + '_' + year

        # File name randomized TTNet_3grams_2012_asfas12.arff
        file_name = self.__helper.generate_random_file_name(file_name, ARFF_FILE_EXTENSION)

        # Arff file path ...../DataSet-ARFF/3Gram/TTNet/TTNet_3grams_2012_asfas12.arff
        arff_file_path = PROJECT_ROOT_DIRECTORY + DATASET_ARFF_DIR_NAME + experiment_name + '/' + MODEL_NAME + '/'

        # Generating the file with data
        self.__helper.generate_arff_file(arff_file_path, file_name, formatted_arff_data)

        print("Arff file generated at path:"+arff_file_path+file_name)

    def run_experiment_with_scikit_learn(self, n=1, analyzer='word'):
        """
        Makes necessary method calls to run the experiment on scikit learn.
        :param n: int, count n in n-gram
        :param analyzer: string, either 'word' or 'char'
        :return: void
        """
        # Retrieving all tweets from database
        print("Retrieving all tweets from database.")
        tweets_for_all_years = {}
        # Iterating over all years
        for year in self.years:
            # Retrieving tweets for the year
            tweets_for_year = self.__db_manager.get_tweets_for_year(year)
            tweets_for_all_years[year] = tweets_for_year

        # Creating a big list of tweets
        print("Creating a big list of tweets.")
        all_tweets = []
        # Appending all tweets together
        for year, tweets in tweets_for_all_years.iteritems():
            all_tweets += tweets

        # Generating document
        print("Generating document and classes by preprocessing")
        # Preprocessing and generation of document
        document, classes = self.__feature_manager.create_document_and_classes_for_tweets(all_tweets, True)

        # Getting years' tweets counts
        print("Getting years' tweets counts.")
        years_tweets_counts = {}
        for year in self.years:
            years_tweets_counts[year] = len(tweets_for_all_years[year])

        all_processes = []
        self.all_experiments_results = []

        pool = Pool(cpu_count()-1 or 1)
        copy_reg.pickle(types.MethodType, self._reduce_method)

        print("Running experiments.")
        t0 = time.time()
        for i in range(0, N_EXPERIMENTS):
            print("Experiment:"+str(i))
            experiment_manager = ExperimentManager(i, years_tweets_counts, n, analyzer)
            r = pool.apply_async(experiment_manager.run_experiment, args=(document, classes,), callback=self._accumulate_experiments_scores)
            all_processes.append(r)

        for a_process in all_processes:
            a_process.wait()

        t1 = time.time()

        print("Elapsed time:", t1- t0, " seconds")

        pool.close()
        pool.join()

        print("Cumulating all the experiments' scores.")
        final_results_from_all_experiments = self.__helper.cumulate_years_scores(self.all_experiments_results)
        return final_results_from_all_experiments

    def _reduce_method(self, m):
        """

        :param m:
        :return:
        """
        if m.im_self is None:
            return getattr, (m.im_class, m.im_func.func_name)
        else:
            return getattr, (m.im_self, m.im_func.func_name)

    def _accumulate_experiments_scores(self, an_experiments_result):
        """
        Accumulates experiments' scores
        :return: void
        """
        an_experiments_result = self.__helper.calculate_relative_scores(an_experiments_result)
        self.all_experiments_results.append(an_experiments_result)

    def plot_experiment_results(self, root_dir):
        """
        Plots experiment's results from log files
        :param root_dir: string
        :return: void
        """
        lines_scores = self.__helper.get_accuracy_scores_for_experiment_years_from_root_dir(root_dir)
        self.__plot_manager.plot_experiments_results(lines_scores)

    def plot_all_experiment_results_with_scikit_learn(self, all_line_scores_of_all_experiments):
        """
        Plots all line scores of all experiments
        :param all_line_scores_of_all_experiments: dict
        :return: void
        """
        self.__plot_manager.plot_experiments_results_with_scikit_learn(all_line_scores_of_all_experiments)

    def plot_years_scores(self, root_dir):
        """
        Makes necessary function calls to plot years scores
        :param dir: string
        :return: void
        """
        self.__plot_manager.plot_years_scores_from_root_directory(root_dir)

    def plot_2012_vs_rest(self, root_dir):
        """
        Makes necessary function calls to plot 2012 vs REST scores
        :param root_dir: string
        :return: void
        """
        self.__plot_manager.plot_2012_vs_rest(root_dir)

    def plot_top_feature_frequencies_in_years(self):
        """
        Makes necessary function calls to plot top features frequencies' in years
        :return: void
        """
        years_features_counts = {}

        for year in self.years:
            years_features_counts[year] = self.find_frequency_dictionary_for_year(year)

        self.__plot_manager.plot_top_feature_frequencies_in_years(years_features_counts)

    def find_frequency_dictionary_for_year(self, year):
        """
        Finds frequencies of each feature for given year
        :param year: string
        :return: dict
        """
        # For this particular method, find_roots=True, n=1, analyzer=word because we're working with top info gain words

        tweets_for_the_year = self.__db_manager.get_tweets_for_year(year)
        document, classes = self.__feature_manager.create_document_and_classes_for_tweets(tweets_for_the_year, find_roots=True)
        ngrams, arff_data, vectorizer, X = self.__feature_manager.fit_data(document, classes, n=1, analyzer='word')

        terms = vectorizer.get_feature_names()
        freqs = X.sum(axis=0).A1

        result = sorted(zip(freqs, terms), reverse=True)

        freqs = [elm[0] for elm in result]
        terms = [elm[1] for elm in result]

        final_result = dict(zip(terms, freqs))

        return final_result

    def plot_years_intersection_scores(self):
        """
        Makes necessary function callst to plot a matrix which shows years' vocabularies similarities
        :return: void
        """
        years_features_counts = {}

        for year in self.years:
            years_features_counts[year] = self.find_frequency_dictionary_for_year(year)
            
        self.__plot_manager.plot_years_intersection_scores(years_features_counts)

    def import_new_tweets_from_csv(self, root_path):
        """

        :param root_path:
        :return:
        """
        self.__import_manager.import_new_tweets_from_csv(root_path)
class PlotController(object):
  """ Controller for 2D curve plotting functionalities.
  """
  __UNIQUE_INSTANCE = None  # my poor impl. of a singleton
  
  ## For testing purposes:
  WITH_CURVE_BROWSER = True
  WITH_CURVE_TABS = True
  
  def __init__(self, sgPyQt=None):
    if self.__UNIQUE_INSTANCE is None:
      self.__trueInit(sgPyQt)
    else:
      raise Exception("The PlotController must be a singleton - use GetInstance()")

  def __trueInit(self, sgPyQt=None):
    if sgPyQt is None:
      import SalomePyQt
      sgPyQt = SalomePyQt.SalomePyQt()
    self._sgPyQt = sgPyQt
    self._modelViews = {}
    self._browserContextualMenu = None
    self._blockNotifications = False
    self._blockViewClosing = False
    self._callbacks = []
    
    self._plotManager = PlotManager(self)
    
    if self.WITH_CURVE_BROWSER:
      self._curveBrowserView = CurveBrowserView(self)
      self.associate(self._plotManager, self._curveBrowserView)
    else:
      self._curveBrowserView = None  
    if self.WITH_CURVE_TABS:
      self._curveTabsView = CurveTabsView(self)
      self.associate(self._plotManager, self._curveTabsView)
    else:
      self._curveTabsView = None
    PlotController.__UNIQUE_INSTANCE = self
  
  @classmethod
  def GetInstance(cls, sgPyQt=None):
    if cls.__UNIQUE_INSTANCE is None:
      # First instanciation:
      PlotController(sgPyQt)
    return cls.__UNIQUE_INSTANCE
  
  @classmethod
  def Destroy(cls):
    cls.__UNIQUE_INSTANCE = None
  
  def setFixedSizeWidget(self):
    """ For testing purposes - ensure visible Qt widgets have a fixed size.
    """
    if self.WITH_CURVE_BROWSER:
      self._curveBrowserView.treeWidget.resize(100,200)
    if self.WITH_CURVE_TABS:
      self._sgPyQt._tabWidget.resize(600,600)
  
  def associate(self, model, view):
    """
    Associates a model to a view, and sets the view to listen to this model 
    changes.
    
    :param model: Model -- The model to be associated to the view.
    :param view: View -- The view.
    
    """    
    if model is None or view is None:
        return
  
    view.setModel(model)
    self.setModelListener(model, view)
  
  def setModelListener(self, model, view):
    """
    Sets a view to listen to all changes of the given model
    """
    l = self._modelViews.setdefault(model, [])
    if not view in l and view is not None:
      l.append(view) 
  
  def removeModelListeners(self, model):
    """ 
    Removes the given model from the list of listeners. All views previously connected to this model
    won't receive its update notification anymore.
    """
    self._modelViews.pop(model)
  
  def notify(self, model, what=""):
    """
    Notifies the view when model changes.
    
    :param model: Model -- The updated model.
    """
    if model is None or self._blockNotifications:
      return
    
    if not self._modelViews.has_key(model):
      return
    
    for view in self._modelViews[model]:
      method = "on%s" % what
      if what != "" and what is not None and hasattr(view, method):
        exec "view.%s()" % method
      elif hasattr(view, "update"):
        # Generic update:
        view.update()
    
  def setBrowserContextualMenu(self, menu):
    """ Provide a menu to be contextually shown in the curve browser """
    self._browserContextualMenu = menu
    
  def setCurvePlotRequestingClose(self, bool):
    self._blockViewClosing = bool
    
  def onCurrentCurveChange(self):
    ps = self._plotManager.getCurrentPlotSet()
    if not ps is None:
      crv = ps.getCurrentCurve()
      if crv is not None:
        crv_id = crv.getID() 
        for c in self._callbacks:
          c(crv_id)
    
  #####
  ##### Public static API
  #####
  
  @classmethod
  def AddCurve(cls, x, y, curve_label="", x_label="", y_label="", append=True):
    """ Add a new curve and make the plot set where it is drawn the active one.
        If no plot set exists, or none is active, a new plot set will be created, even if append is True.
        @param x x data
        @param y y data
        @param curve_label label of the curve being ploted (optional, default to empty string). This is what is
        shown in the legend.
        @param x_label label for the X axis
        @param y_label label for the Y axis
        @param append whether to add the curve to the active plot set (default) or into a new one.
        @return the id of the created curve, and the id of the corresponding plot set.
    """
    from XYView import XYView
    control = cls.GetInstance()
    pm = control._plotManager
    t = TableModel(control)
    data = np.transpose(np.vstack([x, y]))
    t.setData(data)
    # ensure a single Matplotlib repaint for all operations to come in AddCurve
    prevLock = pm.isRepaintLocked()
    if not prevLock:
      pm.lockRepaint()
    curveID, plotSetID = control.plotCurveFromTable(t, x_col_index=0, y_col_index=1, 
                                                    curve_label=curve_label, append=append)
    ps = pm._plotSets[plotSetID]
    if x_label != "":
      ps.setXLabel(x_label)
    if y_label != "": 
      ps.setYLabel(y_label)
    if not prevLock:
      pm.unlockRepaint()
    return curveID, plotSetID

  @classmethod  
  def ExtendCurve(cls, crv_id, x, y):
    """ Add new points to an already created curve
    @raise if invalid plot set ID is given
    """
    control = cls.GetInstance()
    ps = control._plotManager.getPlotSetContainingCurve(crv_id)
    if ps is None:
      raise ValueError("Curve ID (%d) not found for extension!" % crv_id)
    crv_mod = ps._curves[crv_id]
    data = np.transpose(np.vstack([x, y]))
    crv_mod.extendData(data)
    
  @classmethod
  def ResetCurve(cls, crv_id):
    """ Reset a given curve: all data are cleared, but the curve is still 
    alive with all its attributes (color, etc ...). Mostly used in conjunction
    with ExtendCurve above
    @raise if invalid plot set ID is given
    """
    control = cls.GetInstance()
    ps = control._plotManager.getPlotSetContainingCurve(crv_id)
    if ps is None:
      raise ValueError("Curve ID (%d) not found for reset!" % crv_id)
    crv_mod = ps._curves[crv_id]
    crv_mod.resetData()
    
  @classmethod
  def AddPlotSet(cls, title=""):
    """ Creates a new plot set (a tab with several curves) and returns its ID. A title can be passed,
    otherwise a default one will be created.
    By default this new plot set becomes the active one.
    """
    control = cls.GetInstance()
    ps = control._plotManager.createXYPlotSet()
    control.setModelListener(ps, control._curveBrowserView)
    # Controller itself must be notified for curve picking:
    control.setModelListener(ps, control)
    if title != "":
      ps.setTitle(title)
    return ps.getID()
            
  @classmethod
  def CopyCurve(cls, curve_id, plot_set_id):
    """ Copy a given curve to a given plot set ID
    @return ID of the newly created curve
    """
    control = cls.GetInstance()
    psID = cls.GetPlotSetID(curve_id)
    if psID == -1:
      raise ValueError("Curve ID (%d) not found for duplication!" % curve_id)
    plot_set_src = control._plotManager._plotSets[psID]
    plot_set_tgt = control._plotManager._plotSets.get(plot_set_id, None)
    if plot_set_tgt is None:
      raise ValueError("Plot set ID (%d) invalid for duplication!" % plot_set_id)
    crv = plot_set_src._curves[curve_id]
    new_crv = crv.clone()
    control.setModelListener(new_crv, control._curveBrowserView)
    plot_set_tgt.addCurve(new_crv)
    return new_crv.getID()
      
  @classmethod
  def DeleteCurve(cls, curve_id=-1):
    """ By default, delete the current curve, if any. Otherwise do nothing.
        @return the id of the deleted curve or -1
    """
    Logger.Debug("Delete curve")
    control = cls.GetInstance()
    # Find the right plot set:
    if curve_id == -1:
      curve_id = cls.GetCurrentCurveID()
      if curve_id == -1:
        # No current curve, do nothing
        return -1 
      
    psID = cls.GetPlotSetID(curve_id)
    if psID == -1:
      raise ValueError("Curve ID (%d) not found for deletion!" % curve_id)
    crv = control._plotManager._plotSets[psID]._curves[curve_id]
    control._plotManager._plotSets[psID].removeCurve(curve_id)
    control.removeModelListeners(crv)
    return curve_id
  
  @classmethod
  def DeletePlotSet(cls, plot_set_id=-1):
    """ By default, delete the current plot set, if any. Otherwise do nothing.
        This will automatically make the last added plot set the current one.
        @return the id of the deleted plot set or -1
    """
    Logger.Debug("PlotController::DeletePlotSet %d" % plot_set_id)
    control = cls.GetInstance()
    # Find the right plot set:
    if plot_set_id == -1:
      plot_set_id = cls.GetCurrentPlotSetID()
      if plot_set_id == -1:
        # No current, do nothing
        return -1

    ps = control._plotManager.removeXYPlotSet(plot_set_id)
    for _, crv in ps._curves.items():
      control.removeModelListeners(crv)
    control.removeModelListeners(ps)
    psets = control._plotManager._plotSets 
    if len(psets):
      control._plotManager.setCurrentPlotSet(psets.keys()[-1])
    return plot_set_id
  
  @classmethod
  def usedMem(cls):
      import gc
      gc.collect()
      import resource
      m = resource.getrusage(resource.RUSAGE_SELF)[2]*resource.getpagesize()/1e6
      print "** Used memory: %.2f Mb" % m
  
  @classmethod
  def DeleteCurrentItem(cls):
    """ Delete currently active item, be it a plot set or a curve.
    @return (True, plot_sed_id) if a plot set was deleted or (False, curve_id) if a curve was deleted, or (True, -1)
    if nothing was deleted.
    """
    c_id = cls.GetCurrentCurveID()
    ps_id = cls.GetCurrentPlotSetID()
    ret = True, -1
    if ps_id == -1:
      Logger.Info("PlotController.DeleteCurrentItem(): nothing selected, nothing to delete!")
      return True,-1
    # Do we delete a curve or a full plot set    
    if c_id == -1:
      cls.DeletePlotSet(ps_id)
      ret = True, ps_id
    else:
      cls.DeleteCurve(c_id)
      ret = False, c_id
    return ret
  
  @classmethod
  def ClearPlotSet(cls, ps_id=-1):
    """ Clear all curves in a given plot set. By default clear the current plot set without deleting it,
    if no default plot set is currently active, do nothing.
    @return id of the cleared plot set
    @raise if invalid plot set ID is given
    """
    pm = cls.GetInstance()._plotManager
    if ps_id == -1:
      ps_id = cls.GetCurrentPlotSetID()
      if ps_id == -1:
        return ps_id
    ps = pm._plotSets.get(ps_id, None)
    if ps is None:
      raise ValueError("Invalid plot set ID (%d)!" % ps_id)
    ps.eraseAll()
    return ps_id
  
#   @classmethod
#   def ClearAll(cls):
#     # TODO: optimize
#     pm = cls.GetInstance()._plotManager
#     ids = pm._plotSets.keys()
#     for i in ids:
#       cls.DeletePlotSet(i)
  
  @classmethod
  def SetXLabel(cls, x_label, plot_set_id=-1):
    """  By default set the X axis label for the current plot set, if any. Otherwise do nothing.
         @return True if the label was set
    """
    pm = cls.GetInstance()._plotManager
    if plot_set_id == -1:
      plot_set_id = cls.GetCurrentPlotSetID()
      if plot_set_id == -1:
        # Do nothing
        return False 
    ps = pm._plotSets.get(plot_set_id, None)
    if ps is None:
      raise Exception("Invalid plot set ID (%d)!" % plot_set_id)
    ps.setXLabel(x_label)
    return True
     
  @classmethod 
  def SetYLabel(cls, y_label, plot_set_id=-1):
    """ By default set the Y axis label for the current plot set, if any. Otherwise do nothing.
         @return True if the label was set
    """
    pm = cls.GetInstance()._plotManager
    if plot_set_id == -1:
      plot_set_id = cls.GetCurrentPlotSetID()
      if plot_set_id == -1:
        # Do nothing
        return False 
    ps = pm._plotSets.get(plot_set_id, None)
    if ps is None:
      raise Exception("Invalid plot set ID (%d)!" % plot_set_id)
    ps.setYLabel(y_label)
    return True
     
  @classmethod 
  def SetPlotSetTitle(cls, title, plot_set_id=-1):
    """ By default set the title for the current plot set, if any. Otherwise do nothing.
         @return True if the title was set
    """
    pm = cls.GetInstance()._plotManager
    if plot_set_id == -1:
      plot_set_id = cls.GetCurrentPlotSetID()
      if plot_set_id == -1:
        # Do nothing
        return False 
    ps = pm._plotSets.get(plot_set_id, None)
    if ps is None:
      raise Exception("Invalid plot set ID (%d)!" % plot_set_id)
    ps.setTitle(title)
    return True
  
  @classmethod
  def GetPlotSetID(cls, curve_id):
    """ @return plot set id for a given curve or -1 if invalid curve ID
    """
    control = cls.GetInstance()
    cps = control._plotManager.getPlotSetContainingCurve(curve_id)
    if cps is None:
      return -1
    return cps.getID()
  
  @classmethod
  def GetPlotSetIDByName(cls, name):
    """ @return the first plot set whose name matches the provided name. Otherwise returns -1
    """ 
    pm = cls.GetInstance()._plotManager
    for _, ps in pm._plotSets.items():
      if ps._title == name:
        return ps.getID()
    return -1
  
  @classmethod
  def GetAllPlotSets(cls):
    """ @return two lists: plot set names, and corresponding plot set IDs
    """
    pm = cls.GetInstance()._plotManager
    it = pm._plotSets.items()
    ids, inst, titles = [], [], []
    if len(it):  
      ids, inst = zip(*it)        
    if len(inst):
      titles = [i.getTitle() for i in inst]
    return list(ids), titles
  
  @classmethod
  def GetCurrentCurveID(cls):
    """ @return current curve ID or -1 if no curve is currently active
    """
    control = cls.GetInstance()
    crv = control._plotManager.getCurrentCurve()
    if crv is None:
      return -1
    return crv.getID()
     
  @classmethod   
  def GetCurrentPlotSetID(cls):
    """ @return current plot set ID or -1 if no plot set is currently active
    """
    control = cls.GetInstance()
    cps = control._plotManager.getCurrentPlotSet()
    if cps is None:
      return -1
    return cps.getID()  

  @classmethod
  def SetCurrentPlotSet(cls, ps_id):
    """ Set the current active plot set. Use -1 to unset any current plot set.
    @throw if invalid ps_id
    """
    control = cls.GetInstance()
    control._plotManager.setCurrentPlotSet(ps_id)

  @classmethod
  def SetCurrentCurve(cls, crv_id):
    """ Set the current active curve.
    @return corresponding plot set ID
    @throw if invalid crv_id
    """
    control = cls.GetInstance()
    ps_id = control._plotManager.setCurrentCurve(crv_id)
    return ps_id

  @classmethod
  def ActiveViewChanged(cls, viewID):
    """ This method is to be plugged direclty in the activeViewChanged() slot of a standard
    Python SALOME module so that the curve browser stays in sync with the selected SALOME view
    """
    control = cls.GetInstance()
    # Get XYView from SALOME view ID
    xyview = control._curveTabsView._XYViews.get(viewID, None)
    if not xyview is None:
      plotSetID = xyview.getModel().getID()
      control._plotManager.setCurrentPlotSet(plotSetID)

  @classmethod
  def ToggleCurveBrowser(cls, active):
    if cls.__UNIQUE_INSTANCE is not None:
      raise Exception("ToggleCurveBrowser() must be invoked before doing anything in plot2D!")
    cls.WITH_CURVE_BROWSER = active
    
  @classmethod
  def IsValidPlotSetID(cls, plot_set_id):
    """ 
    @return True if plot_set_id is the identifier of a valid and existing plot set.
    """
    control = cls.GetInstance()
    return control._plotManager._plotSets.has_key(plot_set_id)

  @classmethod
  def GetSalomeViewID(cls, plot_set_id):
    """
    @return the salome view ID associated to a given plot set. -1 if invalid plot_set_id
    """
    control = cls.GetInstance()
    d = control._curveTabsView.mapModId2ViewId()
    return d.get(plot_set_id, -1)

  @classmethod
  def OnSalomeViewTryClose(cls, salome_view_id):
    control = cls.GetInstance()
    if not control._blockViewClosing:
      Logger.Debug("PlotController::OnSalomeViewTryClose %d" % salome_view_id)
#       control._sgPyQt.setViewClosable(salome_view_id, False)
      # Get XYView from SALOME view ID
      xyview = control._curveTabsView._XYViews.get(salome_view_id, None)
      if not xyview is None:
        plotSetID = xyview.getModel().getID()
        Logger.Debug("PlotController::OnSalomeViewTryClose internal CurvePlot view ID is %d" % plotSetID)
        control._plotManager.removeXYPlotSet(plotSetID)
      else:
        Logger.Warning("Internal error - could not match SALOME view ID %d with CurvePlot view!" % salome_view_id)

  @classmethod
  def SetCurveMarker(cls, crv_id, marker):
    """ Change curve marker. Available markers are:
    CURVE_MARKERS = [ "o" ,#  circle
                    "*",  # star
                    "+",  # plus
                    "x",  # x
                    "s",  # square
                    "p",  # pentagon
                    "h",  # hexagon1
                    "8",  # octagon
                    "D",  # diamond
                    "^",  # triangle_up
                    "<",  # triangle_left
                    ">",  # triangle_right
                    "1",  # tri_down
                    "2",  # tri_up
                    "3",  # tri_left
                    "4",  # tri_right
                    "v",  # triangle_down
                    "H",  # hexagon2
                    "d",  # thin diamond
                    "",   # NO MARKER
                   ]
    @raise if invalid curve ID or marker
    """
    from XYView import XYView
    from CurveView import CurveView
    if not marker in XYView.CURVE_MARKERS:
      raise ValueError("Invalid marker: '%s'" % marker)
    
    cont = cls.GetInstance()
    for mod, views in cont._modelViews.items():
      if isinstance(mod, CurveModel) and mod.getID() == crv_id:
        for v in views:
          if isinstance(v, CurveView):
            v.setMarker(marker)
            # Update curve display and legend:
            v._parentXYView.repaint()
            v._parentXYView.showHideLegend()
            found = True
        
    if not found:
      raise Exception("Invalid curve ID or curve currently not displayed (curve_id=%d)!" % crv_id)

  @classmethod
  def SetCurveLabel(cls, crv_id, label):
    """ Change curve label
    @raise if invalid curve id
    """
    cont = cls.GetInstance()
    cps = cont._plotManager.getPlotSetContainingCurve(crv_id)
    if cps is None:
      raise ValueError("Invalid curve ID: %d" % crv_id)
    cps._curves[crv_id].setTitle(label)

  @classmethod
  def __XYViewOperation(cls, func, ps_id, args, kwargs):
    """ Private. To factorize methods accessing the XYView to change a display element. """
    from XYPlotSetModel import XYPlotSetModel
    from XYView import XYView
    
    cont = cls.GetInstance()
    for mod, views in cont._modelViews.items():
      if isinstance(mod, XYPlotSetModel) and mod.getID() == ps_id:
        for v in views:
          if isinstance(v, XYView):
            exec "v.%s(*args, **kwargs)" % func
            found = True
    if not found:
      raise Exception("Invalid plot set ID or plot set currently not displayed (ps_id=%d)!" % ps_id)


  @classmethod
  def SetXLog(cls, ps_id, log=True):
    """ Toggle the X axis into logarithmic scale.
    @param ps_id plot set ID
    @param log if set to True, log scale is used, otherwise linear scale is used
    @raise if invalid plot set ID
    """
    args, kwargs = [log], {}
    cls.__XYViewOperation("setXLog", ps_id, args, kwargs)

  @classmethod
  def SetYLog(cls, ps_id, log=True):
    """ Toggle the Y axis into logarithmic scale.
    @param ps_id plot set ID
    @param log if set to True, log scale is used, otherwise linear scale is used
    @raise if invalid plot set ID
    """
    args, kwargs = [log], {}
    cls.__XYViewOperation("setYLog", ps_id, args, kwargs)
     
  @classmethod
  def SetXSciNotation(cls, ps_id, sciNotation=False):
    """ Change the format (scientific notation or not) of the X axis.
    @param ps_id plot set ID
    @param sciNotation if set to True, scientific notation is used, otherwise plain notation is used
    @raise if invalid plot set ID
    """
    args, kwargs = [sciNotation], {}
    cls.__XYViewOperation("setXSciNotation", ps_id, args, kwargs)
   
  @classmethod
  def SetYSciNotation(cls, ps_id, sciNotation=False):
    """ Change the format (scientific notation or not) of the Y axis.
    @param ps_id plot set ID
    @param sciNotation if set to True, scientific notation is used, otherwise plain notation is used
    @raise if invalid plot set ID
    """
    args, kwargs = [sciNotation], {}
    cls.__XYViewOperation("setYSciNotation", ps_id, args, kwargs)

  @classmethod
  def SetLegendVisible(cls, ps_id, visible=True):
    """ Change the visibility of the legend.
    @param ps_id plot set ID
    @param visible if set to True, show legend, otherwise hide it.
    @raise if invalid plot set ID
    """
    args, kwargs = [visible], {}
    cls.__XYViewOperation("setLegendVisible", ps_id, args, kwargs)
    

  ###
  ### More advanced functions
  ###
  @classmethod
  def RegisterCallback(cls, callback):
    cont = cls.GetInstance()
    cont._callbacks.append(callback)
  
  @classmethod
  def ClearCallbacks(cls):
    cont = cls.GetInstance()
    cont._callbacks = []
  
  @classmethod
  def LockRepaint(cls):
    control = cls.GetInstance()
    control._plotManager.lockRepaint()
  
  @classmethod
  def UnlockRepaint(cls):
    control = cls.GetInstance()
    control._plotManager.unlockRepaint()  
  
  def createTable(self, data, table_name="table"):
    t = TableModel(self)
    t.setData(data)
    t.setTitle(table_name)
    return t
     
  def plotCurveFromTable(self, table, x_col_index=0, y_col_index=1, curve_label="", append=True):
    """
    :returns: a tuple containing the unique curve ID and the plot set ID 
    """
    # Regardless of 'append', we must create a view if none there:
    if self._plotManager.getCurrentPlotSet() is None or not append:
      ps = self._plotManager.createXYPlotSet()
      self.setModelListener(ps, self._curveBrowserView)
      # For curve picking, controller must listen:
      self.setModelListener(ps, self)
      cps_title = table.getTitle()
    else:
      cps_title = None

    cps = self._plotManager.getCurrentPlotSet()
    
    cm = CurveModel(self, table, y_col_index)
    cm.setXAxisIndex(x_col_index)
    
    # X axis label
    tix = table.getColumnTitle(x_col_index)
    if tix != "":
      cps.setXLabel(tix)
    
    # Curve label
    if curve_label != "":
      cm.setTitle(curve_label)
    else:
      ti = table.getColumnTitle(y_col_index)
      if ti != "":
        cm.setTitle(ti)

    # Plot set title
    if cps_title != "" and cps_title is not None:
      Logger.Debug("about to set title to: " + cps_title)  
      cps.setTitle(cps_title)
    
    cps.addCurve(cm)
    mp = self._curveTabsView.mapModId2ViewId()
    xyview_id = mp[cps.getID()]
    xyview = self._curveTabsView._XYViews[xyview_id]
    
    if cps_title is None:  # no plot set was created above
      self._plotManager.setCurrentPlotSet(cps.getID())
      
    # Make CurveBrowser and CurveView depend on changes in the curve itself:
    self.setModelListener(cm, self._curveBrowserView)
    self.setModelListener(cm, xyview._curveViews[cm.getID()])
    # Upon change on the curve also update the full plot, notably for the auto-fit and the legend:
    self.setModelListener(cm, xyview)
        
    return cm.getID(),cps.getID()
    (MomentumIndicator(), BollingerIndicator(), RsiIndicator()))
data = MarketData(stock_name, df, ma=[50, 100, 200], indicator=indicators)
# plot_stock(df)
#
# corr = df.corr()
# ax = sns.heatmap(corr, cmap="YlGnBu")
# plt.show()

df = data.get_stock_data()
x_data, y_data_original = DataProcessing.load_data(df, seq_len)
x_train, x_test, y_train, y_test = train_test_split(x_data,
                                                    y_data_original,
                                                    test_size=0.2)

model = PredictionModelFactory.create_default(seq_len)

if load:
    model.model.load_weights(weight_file)
else:
    model.fit(x_train, y_train)
    model.model.save_weights(weight_file)
    model.model_score(x_train, y_train, x_test, y_test)
    p = model.percentage_difference(x_test, y_test)

prices = model.predict_days(data, predict_days)
print(prices)

df = data.get_stock_data()
x_data, y_data_final = DataProcessing.load_data(df, seq_len)
PlotManager.plot_result(data, y_data_final[(-predict_days - plot_days):-1],
                        y_data_original[-plot_days:-1])
Esempio n. 12
0
class PlotController(object):
    """ Controller for 2D curve plotting functionalities.
  """
    __UNIQUE_INSTANCE = None  # my poor impl. of a singleton

    ## For testing purposes:
    WITH_CURVE_BROWSER = True
    WITH_CURVE_TABS = True

    def __init__(self, sgPyQt=None):
        if self.__UNIQUE_INSTANCE is None:
            self.__trueInit(sgPyQt)
        else:
            raise Exception(
                "The PlotController must be a singleton - use GetInstance()")

    def __trueInit(self, sgPyQt=None):
        if sgPyQt is None:
            import SalomePyQt
            sgPyQt = SalomePyQt.SalomePyQt()
        self._sgPyQt = sgPyQt
        self._modelViews = {}
        self._browserContextualMenu = None
        self._blockNotifications = False
        self._blockViewClosing = False
        self._callbacks = []

        self._plotManager = PlotManager(self)

        if self.WITH_CURVE_BROWSER:
            self._curveBrowserView = CurveBrowserView(self)
            self.associate(self._plotManager, self._curveBrowserView)
        else:
            self._curveBrowserView = None
        if self.WITH_CURVE_TABS:
            self._curveTabsView = CurveTabsView(self)
            self.associate(self._plotManager, self._curveTabsView)
        else:
            self._curveTabsView = None
        PlotController.__UNIQUE_INSTANCE = self

    @classmethod
    def GetInstance(cls, sgPyQt=None):
        if cls.__UNIQUE_INSTANCE is None:
            # First instanciation:
            PlotController(sgPyQt)
        return cls.__UNIQUE_INSTANCE

    @classmethod
    def Destroy(cls):
        cls.__UNIQUE_INSTANCE = None

    def setFixedSizeWidget(self):
        """ For testing purposes - ensure visible Qt widgets have a fixed size.
    """
        if self.WITH_CURVE_BROWSER:
            self._curveBrowserView.treeWidget.resize(100, 200)
        if self.WITH_CURVE_TABS:
            self._sgPyQt._tabWidget.resize(600, 600)

    def associate(self, model, view):
        """
    Associates a model to a view, and sets the view to listen to this model 
    changes.
    
    :param model: Model -- The model to be associated to the view.
    :param view: View -- The view.
    
    """
        if model is None or view is None:
            return

        view.setModel(model)
        self.setModelListener(model, view)

    def setModelListener(self, model, view):
        """
    Sets a view to listen to all changes of the given model
    """
        l = self._modelViews.setdefault(model, [])
        if not view in l and view is not None:
            l.append(view)

    def removeModelListeners(self, model):
        """ 
    Removes the given model from the list of listeners. All views previously connected to this model
    won't receive its update notification anymore.
    """
        self._modelViews.pop(model)

    def notify(self, model, what=""):
        """
    Notifies the view when model changes.
    
    :param model: Model -- The updated model.
    """
        if model is None or self._blockNotifications:
            return

        if not self._modelViews.has_key(model):
            return

        for view in self._modelViews[model]:
            method = "on%s" % what
            if what != "" and what is not None and hasattr(view, method):
                exec "view.%s()" % method
            elif hasattr(view, "update"):
                # Generic update:
                view.update()

    def setBrowserContextualMenu(self, menu):
        """ Provide a menu to be contextually shown in the curve browser """
        self._browserContextualMenu = menu

    def setCurvePlotRequestingClose(self, bool):
        self._blockViewClosing = bool

    def onCurrentCurveChange(self):
        ps = self._plotManager.getCurrentPlotSet()
        if not ps is None:
            crv = ps.getCurrentCurve()
            if crv is not None:
                crv_id = crv.getID()
                for c in self._callbacks:
                    c(crv_id)

    #####
    ##### Public static API
    #####

    @classmethod
    def AddCurve(cls,
                 x,
                 y,
                 curve_label="",
                 x_label="",
                 y_label="",
                 append=True):
        """ Add a new curve and make the plot set where it is drawn the active one.
        If no plot set exists, or none is active, a new plot set will be created, even if append is True.
        @param x x data
        @param y y data
        @param curve_label label of the curve being ploted (optional, default to empty string). This is what is
        shown in the legend.
        @param x_label label for the X axis
        @param y_label label for the Y axis
        @param append whether to add the curve to the active plot set (default) or into a new one.
        @return the id of the created curve, and the id of the corresponding plot set.
    """
        from XYView import XYView
        control = cls.GetInstance()
        pm = control._plotManager
        t = TableModel(control)
        data = np.transpose(np.vstack([x, y]))
        t.setData(data)
        # ensure a single Matplotlib repaint for all operations to come in AddCurve
        prevLock = pm.isRepaintLocked()
        if not prevLock:
            pm.lockRepaint()
        curveID, plotSetID = control.plotCurveFromTable(
            t,
            x_col_index=0,
            y_col_index=1,
            curve_label=curve_label,
            append=append)
        ps = pm._plotSets[plotSetID]
        if x_label != "":
            ps.setXLabel(x_label)
        if y_label != "":
            ps.setYLabel(y_label)
        if not prevLock:
            pm.unlockRepaint()
        return curveID, plotSetID

    @classmethod
    def ExtendCurve(cls, crv_id, x, y):
        """ Add new points to an already created curve
    @raise if invalid plot set ID is given
    """
        control = cls.GetInstance()
        ps = control._plotManager.getPlotSetContainingCurve(crv_id)
        if ps is None:
            raise ValueError("Curve ID (%d) not found for extension!" % crv_id)
        crv_mod = ps._curves[crv_id]
        data = np.transpose(np.vstack([x, y]))
        crv_mod.extendData(data)

    @classmethod
    def ResetCurve(cls, crv_id):
        """ Reset a given curve: all data are cleared, but the curve is still 
    alive with all its attributes (color, etc ...). Mostly used in conjunction
    with ExtendCurve above
    @raise if invalid plot set ID is given
    """
        control = cls.GetInstance()
        ps = control._plotManager.getPlotSetContainingCurve(crv_id)
        if ps is None:
            raise ValueError("Curve ID (%d) not found for reset!" % crv_id)
        crv_mod = ps._curves[crv_id]
        crv_mod.resetData()

    @classmethod
    def AddPlotSet(cls, title=""):
        """ Creates a new plot set (a tab with several curves) and returns its ID. A title can be passed,
    otherwise a default one will be created.
    By default this new plot set becomes the active one.
    """
        control = cls.GetInstance()
        ps = control._plotManager.createXYPlotSet()
        control.setModelListener(ps, control._curveBrowserView)
        # Controller itself must be notified for curve picking:
        control.setModelListener(ps, control)
        if title != "":
            ps.setTitle(title)
        return ps.getID()

    @classmethod
    def CopyCurve(cls, curve_id, plot_set_id):
        """ Copy a given curve to a given plot set ID
    @return ID of the newly created curve
    """
        control = cls.GetInstance()
        psID = cls.GetPlotSetID(curve_id)
        if psID == -1:
            raise ValueError("Curve ID (%d) not found for duplication!" %
                             curve_id)
        plot_set_src = control._plotManager._plotSets[psID]
        plot_set_tgt = control._plotManager._plotSets.get(plot_set_id, None)
        if plot_set_tgt is None:
            raise ValueError("Plot set ID (%d) invalid for duplication!" %
                             plot_set_id)
        crv = plot_set_src._curves[curve_id]
        new_crv = crv.clone()
        control.setModelListener(new_crv, control._curveBrowserView)
        plot_set_tgt.addCurve(new_crv)
        return new_crv.getID()

    @classmethod
    def DeleteCurve(cls, curve_id=-1):
        """ By default, delete the current curve, if any. Otherwise do nothing.
        @return the id of the deleted curve or -1
    """
        Logger.Debug("Delete curve")
        control = cls.GetInstance()
        # Find the right plot set:
        if curve_id == -1:
            curve_id = cls.GetCurrentCurveID()
            if curve_id == -1:
                # No current curve, do nothing
                return -1

        psID = cls.GetPlotSetID(curve_id)
        if psID == -1:
            raise ValueError("Curve ID (%d) not found for deletion!" %
                             curve_id)
        crv = control._plotManager._plotSets[psID]._curves[curve_id]
        control._plotManager._plotSets[psID].removeCurve(curve_id)
        control.removeModelListeners(crv)
        return curve_id

    @classmethod
    def DeletePlotSet(cls, plot_set_id=-1):
        """ By default, delete the current plot set, if any. Otherwise do nothing.
        This will automatically make the last added plot set the current one.
        @return the id of the deleted plot set or -1
    """
        Logger.Debug("PlotController::DeletePlotSet %d" % plot_set_id)
        control = cls.GetInstance()
        # Find the right plot set:
        if plot_set_id == -1:
            plot_set_id = cls.GetCurrentPlotSetID()
            if plot_set_id == -1:
                # No current, do nothing
                return -1

        ps = control._plotManager.removeXYPlotSet(plot_set_id)
        for _, crv in ps._curves.items():
            control.removeModelListeners(crv)
        control.removeModelListeners(ps)
        psets = control._plotManager._plotSets
        if len(psets):
            control._plotManager.setCurrentPlotSet(psets.keys()[-1])
        return plot_set_id

    @classmethod
    def usedMem(cls):
        import gc
        gc.collect()
        import resource
        m = resource.getrusage(
            resource.RUSAGE_SELF)[2] * resource.getpagesize() / 1e6
        print "** Used memory: %.2f Mb" % m

    @classmethod
    def DeleteCurrentItem(cls):
        """ Delete currently active item, be it a plot set or a curve.
    @return (True, plot_sed_id) if a plot set was deleted or (False, curve_id) if a curve was deleted, or (True, -1)
    if nothing was deleted.
    """
        c_id = cls.GetCurrentCurveID()
        ps_id = cls.GetCurrentPlotSetID()
        ret = True, -1
        if ps_id == -1:
            Logger.Info(
                "PlotController.DeleteCurrentItem(): nothing selected, nothing to delete!"
            )
            return True, -1
        # Do we delete a curve or a full plot set
        if c_id == -1:
            cls.DeletePlotSet(ps_id)
            ret = True, ps_id
        else:
            cls.DeleteCurve(c_id)
            ret = False, c_id
        return ret

    @classmethod
    def ClearPlotSet(cls, ps_id=-1):
        """ Clear all curves in a given plot set. By default clear the current plot set without deleting it,
    if no default plot set is currently active, do nothing.
    @return id of the cleared plot set
    @raise if invalid plot set ID is given
    """
        pm = cls.GetInstance()._plotManager
        if ps_id == -1:
            ps_id = cls.GetCurrentPlotSetID()
            if ps_id == -1:
                return ps_id
        ps = pm._plotSets.get(ps_id, None)
        if ps is None:
            raise ValueError("Invalid plot set ID (%d)!" % ps_id)
        ps.eraseAll()
        return ps_id


#   @classmethod
#   def ClearAll(cls):
#     # TODO: optimize
#     pm = cls.GetInstance()._plotManager
#     ids = pm._plotSets.keys()
#     for i in ids:
#       cls.DeletePlotSet(i)

    @classmethod
    def SetXLabel(cls, x_label, plot_set_id=-1):
        """  By default set the X axis label for the current plot set, if any. Otherwise do nothing.
         @return True if the label was set
    """
        pm = cls.GetInstance()._plotManager
        if plot_set_id == -1:
            plot_set_id = cls.GetCurrentPlotSetID()
            if plot_set_id == -1:
                # Do nothing
                return False
        ps = pm._plotSets.get(plot_set_id, None)
        if ps is None:
            raise Exception("Invalid plot set ID (%d)!" % plot_set_id)
        ps.setXLabel(x_label)
        return True

    @classmethod
    def SetYLabel(cls, y_label, plot_set_id=-1):
        """ By default set the Y axis label for the current plot set, if any. Otherwise do nothing.
         @return True if the label was set
    """
        pm = cls.GetInstance()._plotManager
        if plot_set_id == -1:
            plot_set_id = cls.GetCurrentPlotSetID()
            if plot_set_id == -1:
                # Do nothing
                return False
        ps = pm._plotSets.get(plot_set_id, None)
        if ps is None:
            raise Exception("Invalid plot set ID (%d)!" % plot_set_id)
        ps.setYLabel(y_label)
        return True

    @classmethod
    def SetPlotSetTitle(cls, title, plot_set_id=-1):
        """ By default set the title for the current plot set, if any. Otherwise do nothing.
         @return True if the title was set
    """
        pm = cls.GetInstance()._plotManager
        if plot_set_id == -1:
            plot_set_id = cls.GetCurrentPlotSetID()
            if plot_set_id == -1:
                # Do nothing
                return False
        ps = pm._plotSets.get(plot_set_id, None)
        if ps is None:
            raise Exception("Invalid plot set ID (%d)!" % plot_set_id)
        ps.setTitle(title)
        return True

    @classmethod
    def GetPlotSetID(cls, curve_id):
        """ @return plot set id for a given curve or -1 if invalid curve ID
    """
        control = cls.GetInstance()
        cps = control._plotManager.getPlotSetContainingCurve(curve_id)
        if cps is None:
            return -1
        return cps.getID()

    @classmethod
    def GetPlotSetIDByName(cls, name):
        """ @return the first plot set whose name matches the provided name. Otherwise returns -1
    """
        pm = cls.GetInstance()._plotManager
        for _, ps in pm._plotSets.items():
            if ps._title == name:
                return ps.getID()
        return -1

    @classmethod
    def GetAllPlotSets(cls):
        """ @return two lists: plot set names, and corresponding plot set IDs
    """
        pm = cls.GetInstance()._plotManager
        it = pm._plotSets.items()
        ids, inst, titles = [], [], []
        if len(it):
            ids, inst = zip(*it)
        if len(inst):
            titles = [i.getTitle() for i in inst]
        return list(ids), titles

    @classmethod
    def GetCurrentCurveID(cls):
        """ @return current curve ID or -1 if no curve is currently active
    """
        control = cls.GetInstance()
        crv = control._plotManager.getCurrentCurve()
        if crv is None:
            return -1
        return crv.getID()

    @classmethod
    def GetCurrentPlotSetID(cls):
        """ @return current plot set ID or -1 if no plot set is currently active
    """
        control = cls.GetInstance()
        cps = control._plotManager.getCurrentPlotSet()
        if cps is None:
            return -1
        return cps.getID()

    @classmethod
    def SetCurrentPlotSet(cls, ps_id):
        """ Set the current active plot set. Use -1 to unset any current plot set.
    @throw if invalid ps_id
    """
        control = cls.GetInstance()
        control._plotManager.setCurrentPlotSet(ps_id)

    @classmethod
    def SetCurrentCurve(cls, crv_id):
        """ Set the current active curve.
    @return corresponding plot set ID
    @throw if invalid crv_id
    """
        control = cls.GetInstance()
        ps_id = control._plotManager.setCurrentCurve(crv_id)
        return ps_id

    @classmethod
    def ActiveViewChanged(cls, viewID):
        """ This method is to be plugged direclty in the activeViewChanged() slot of a standard
    Python SALOME module so that the curve browser stays in sync with the selected SALOME view
    """
        control = cls.GetInstance()
        # Get XYView from SALOME view ID
        xyview = control._curveTabsView._XYViews.get(viewID, None)
        if not xyview is None:
            plotSetID = xyview.getModel().getID()
            control._plotManager.setCurrentPlotSet(plotSetID)

    @classmethod
    def ToggleCurveBrowser(cls, active):
        if cls.__UNIQUE_INSTANCE is not None:
            raise Exception(
                "ToggleCurveBrowser() must be invoked before doing anything in plot2D!"
            )
        cls.WITH_CURVE_BROWSER = active

    @classmethod
    def IsValidPlotSetID(cls, plot_set_id):
        """ 
    @return True if plot_set_id is the identifier of a valid and existing plot set.
    """
        control = cls.GetInstance()
        return control._plotManager._plotSets.has_key(plot_set_id)

    @classmethod
    def GetSalomeViewID(cls, plot_set_id):
        """
    @return the salome view ID associated to a given plot set. -1 if invalid plot_set_id
    """
        control = cls.GetInstance()
        d = control._curveTabsView.mapModId2ViewId()
        return d.get(plot_set_id, -1)

    @classmethod
    def OnSalomeViewTryClose(cls, salome_view_id):
        control = cls.GetInstance()
        if not control._blockViewClosing:
            Logger.Debug("PlotController::OnSalomeViewTryClose %d" %
                         salome_view_id)
            #       control._sgPyQt.setViewClosable(salome_view_id, False)
            # Get XYView from SALOME view ID
            xyview = control._curveTabsView._XYViews.get(salome_view_id, None)
            if not xyview is None:
                plotSetID = xyview.getModel().getID()
                Logger.Debug(
                    "PlotController::OnSalomeViewTryClose internal CurvePlot view ID is %d"
                    % plotSetID)
                control._plotManager.removeXYPlotSet(plotSetID)
            else:
                Logger.Warning(
                    "Internal error - could not match SALOME view ID %d with CurvePlot view!"
                    % salome_view_id)

    @classmethod
    def SetCurveMarker(cls, crv_id, marker):
        """ Change curve marker. Available markers are:
    CURVE_MARKERS = [ "o" ,#  circle
                    "*",  # star
                    "+",  # plus
                    "x",  # x
                    "s",  # square
                    "p",  # pentagon
                    "h",  # hexagon1
                    "8",  # octagon
                    "D",  # diamond
                    "^",  # triangle_up
                    "<",  # triangle_left
                    ">",  # triangle_right
                    "1",  # tri_down
                    "2",  # tri_up
                    "3",  # tri_left
                    "4",  # tri_right
                    "v",  # triangle_down
                    "H",  # hexagon2
                    "d",  # thin diamond
                    "",   # NO MARKER
                   ]
    @raise if invalid curve ID or marker
    """
        from XYView import XYView
        from CurveView import CurveView
        if not marker in XYView.CURVE_MARKERS:
            raise ValueError("Invalid marker: '%s'" % marker)

        cont = cls.GetInstance()
        for mod, views in cont._modelViews.items():
            if isinstance(mod, CurveModel) and mod.getID() == crv_id:
                for v in views:
                    if isinstance(v, CurveView):
                        v.setMarker(marker)
                        # Update curve display and legend:
                        v._parentXYView.repaint()
                        v._parentXYView.showHideLegend()
                        found = True

        if not found:
            raise Exception(
                "Invalid curve ID or curve currently not displayed (curve_id=%d)!"
                % crv_id)

    @classmethod
    def SetCurveLabel(cls, crv_id, label):
        """ Change curve label
    @raise if invalid curve id
    """
        cont = cls.GetInstance()
        cps = cont._plotManager.getPlotSetContainingCurve(crv_id)
        if cps is None:
            raise ValueError("Invalid curve ID: %d" % crv_id)
        cps._curves[crv_id].setTitle(label)

    @classmethod
    def __XYViewOperation(cls, func, ps_id, args, kwargs):
        """ Private. To factorize methods accessing the XYView to change a display element. """
        from XYPlotSetModel import XYPlotSetModel
        from XYView import XYView

        cont = cls.GetInstance()
        for mod, views in cont._modelViews.items():
            if isinstance(mod, XYPlotSetModel) and mod.getID() == ps_id:
                for v in views:
                    if isinstance(v, XYView):
                        exec "v.%s(*args, **kwargs)" % func
                        found = True
        if not found:
            raise Exception(
                "Invalid plot set ID or plot set currently not displayed (ps_id=%d)!"
                % ps_id)

    @classmethod
    def SetXLog(cls, ps_id, log=True):
        """ Toggle the X axis into logarithmic scale.
    @param ps_id plot set ID
    @param log if set to True, log scale is used, otherwise linear scale is used
    @raise if invalid plot set ID
    """
        args, kwargs = [log], {}
        cls.__XYViewOperation("setXLog", ps_id, args, kwargs)

    @classmethod
    def SetYLog(cls, ps_id, log=True):
        """ Toggle the Y axis into logarithmic scale.
    @param ps_id plot set ID
    @param log if set to True, log scale is used, otherwise linear scale is used
    @raise if invalid plot set ID
    """
        args, kwargs = [log], {}
        cls.__XYViewOperation("setYLog", ps_id, args, kwargs)

    @classmethod
    def SetXSciNotation(cls, ps_id, sciNotation=False):
        """ Change the format (scientific notation or not) of the X axis.
    @param ps_id plot set ID
    @param sciNotation if set to True, scientific notation is used, otherwise plain notation is used
    @raise if invalid plot set ID
    """
        args, kwargs = [sciNotation], {}
        cls.__XYViewOperation("setXSciNotation", ps_id, args, kwargs)

    @classmethod
    def SetYSciNotation(cls, ps_id, sciNotation=False):
        """ Change the format (scientific notation or not) of the Y axis.
    @param ps_id plot set ID
    @param sciNotation if set to True, scientific notation is used, otherwise plain notation is used
    @raise if invalid plot set ID
    """
        args, kwargs = [sciNotation], {}
        cls.__XYViewOperation("setYSciNotation", ps_id, args, kwargs)

    @classmethod
    def SetLegendVisible(cls, ps_id, visible=True):
        """ Change the visibility of the legend.
    @param ps_id plot set ID
    @param visible if set to True, show legend, otherwise hide it.
    @raise if invalid plot set ID
    """
        args, kwargs = [visible], {}
        cls.__XYViewOperation("setLegendVisible", ps_id, args, kwargs)

    ###
    ### More advanced functions
    ###
    @classmethod
    def RegisterCallback(cls, callback):
        cont = cls.GetInstance()
        cont._callbacks.append(callback)

    @classmethod
    def ClearCallbacks(cls):
        cont = cls.GetInstance()
        cont._callbacks = []

    @classmethod
    def LockRepaint(cls):
        control = cls.GetInstance()
        control._plotManager.lockRepaint()

    @classmethod
    def UnlockRepaint(cls):
        control = cls.GetInstance()
        control._plotManager.unlockRepaint()

    def createTable(self, data, table_name="table"):
        t = TableModel(self)
        t.setData(data)
        t.setTitle(table_name)
        return t

    def plotCurveFromTable(self,
                           table,
                           x_col_index=0,
                           y_col_index=1,
                           curve_label="",
                           append=True):
        """
    :returns: a tuple containing the unique curve ID and the plot set ID 
    """
        # Regardless of 'append', we must create a view if none there:
        if self._plotManager.getCurrentPlotSet() is None or not append:
            ps = self._plotManager.createXYPlotSet()
            self.setModelListener(ps, self._curveBrowserView)
            # For curve picking, controller must listen:
            self.setModelListener(ps, self)
            cps_title = table.getTitle()
        else:
            cps_title = None

        cps = self._plotManager.getCurrentPlotSet()

        cm = CurveModel(self, table, y_col_index)
        cm.setXAxisIndex(x_col_index)

        # X axis label
        tix = table.getColumnTitle(x_col_index)
        if tix != "":
            cps.setXLabel(tix)

        # Curve label
        if curve_label != "":
            cm.setTitle(curve_label)
        else:
            ti = table.getColumnTitle(y_col_index)
            if ti != "":
                cm.setTitle(ti)

        # Plot set title
        if cps_title != "" and cps_title is not None:
            Logger.Debug("about to set title to: " + cps_title)
            cps.setTitle(cps_title)

        cps.addCurve(cm)
        mp = self._curveTabsView.mapModId2ViewId()
        xyview_id = mp[cps.getID()]
        xyview = self._curveTabsView._XYViews[xyview_id]

        if cps_title is None:  # no plot set was created above
            self._plotManager.setCurrentPlotSet(cps.getID())

        # Make CurveBrowser and CurveView depend on changes in the curve itself:
        self.setModelListener(cm, self._curveBrowserView)
        self.setModelListener(cm, xyview._curveViews[cm.getID()])
        # Upon change on the curve also update the full plot, notably for the auto-fit and the legend:
        self.setModelListener(cm, xyview)

        return cm.getID(), cps.getID()