예제 #1
0
class HelpDockPane(TraitsDockPane):
    """
    A DockPane to view help for the current module
    """

    #### TaskPane interface ###############################################

    id = 'edu.mit.synbio.cytoflowgui.help_pane'
    name = 'Help'

    # the Task that serves as the controller
    task = Instance(Task)

    view_plugins = List(IViewPlugin)
    op_plugins = List(IOperationPlugin)

    help_id = Str

    html = HTML("<b>Welcome to Cytoflow!</b>")

    traits_view = View(
        Item('html',
             editor=HTMLEditor(base_url=pathlib.Path(__file__).parent.joinpath(
                 'help').as_posix()),
             show_label=False))

    def create_contents(self, parent):

        self.ui = self.edit_traits(kind='subpanel', parent=parent)

        layout = QtGui.QHBoxLayout()
        control = HintedWidget()

        layout.addWidget(self.ui.control)
        control.setLayout(layout)
        control.setParent(parent)
        parent.setWidget(control)

        return control

    @on_trait_change('help_id', post_init=True)
    def _on_help_id_changed(self):
        for plugin in self.view_plugins:
            if self.help_id == plugin.view_id:
                try:
                    self.html = plugin.get_help()
                except AttributeError:
                    pass
                finally:
                    return

        for plugin in self.op_plugins:
            if self.help_id == plugin.operation_id:
                try:
                    self.html = plugin.get_help()
                except AttributeError:
                    pass
                finally:
                    return
예제 #2
0
class ConsumerCheckAbout(HasTraits):

    about_render = HTML(about_html())
    traits_view = View(
        Item('about_render', show_label=False),
        title="About",
        height=430,
        width=400,
        buttons=['OK'],
    )
예제 #3
0
class HTMLEditorDemo(HasTraits):
    """ Defines the main HTMLEditor demo class. """

    # Define a HTML trait to view
    my_html_trait = HTML(sample_text)

    # Demo view
    traits_view = View(
        UItem(
            'my_html_trait',
            # we specify the editor explicitly in order to set format_text:
            editor=HTMLEditor(format_text=True)),
        title='HTMLEditor',
        buttons=['OK'],
        width=800,
        height=600,
        resizable=True)
예제 #4
0
class HelpDockPane(TraitsDockPane):
    """
    A DockPane to view help for the current module
    """

    #### TaskPane interface ###############################################

    id = 'edu.mit.synbio.help_pane'
    name = 'Help'

    # the Task that serves as the controller
    task = Instance(Task)

    view_plugins = List(IViewPlugin)
    op_plugins = List(IOperationPlugin)

    help_id = Str

    html = HTML("<b>Welcome to Cytoflow!</b>")

    traits_view = View(
        Item('html',
             editor=HTMLEditor(base_url=pathlib.Path(__file__).parent.joinpath(
                 'help').as_posix()),
             show_label=False))

    @on_trait_change('help_id', post_init=True)
    def _on_help_id_changed(self):
        for plugin in self.view_plugins:
            if self.help_id == plugin.view_id:
                try:
                    self.html = plugin.get_help()
                except AttributeError:
                    pass
                finally:
                    return

        for plugin in self.op_plugins:
            if self.help_id == plugin.operation_id:
                try:
                    self.html = plugin.get_help()
                except AttributeError:
                    pass
                finally:
                    return
예제 #5
0
class MainApp(HasStrictTraits):
    """ A dummy main app to show the demo. """

    #: Information about the example.
    information = HTML()

    #: Information about the example.
    credentials = Instance(Credentials, ())

    def _information_default(self):
        return """
        <html>
        <head>
        <style media="screen" type="text/css">
        {css}
        </style>
        </head>
        <body>
        <h1>Validating Dialog Example</h1>

        <p>This example shows how to dynamically validate a user's entries in a
        TraitsUI dialog.  The example shows four things:</p>

        <ul>
        <li><p>how to enable/disable the 'OK' dialog button by tracking various
        error states and incrementing/decrementing the <code>ui.errors</code>
        count.</p></li>

        <li><p>how to perform additional checks when the user clicks the 'OK'
        dialog button, displaying an appropriate error message and returning
        control to the dialog on failure.</p></li>

        <li><p>setting an editor's 'invalid' state via a trait, which colors the
        textbox background to the error color.</p></li>

        <li><p>displaying feedback to the user in a number of additional ways,
        such as text explanations, alert icons, and icons with varying
        feedback levels.</p></li>
        </ul>
        </body>
        """.format(css=css)
예제 #6
0
class DemoFileBase(DemoTreeNodeObject):
    #: Parent of this file:
    parent = Any()

    #: Name of file system path to this file:
    path = Property(depends_on='parent.path,name')

    #: Name of the file:
    name = Str()

    #: UI form of the 'name':
    nice_name = Property()

    #: Files don't allow children:
    allows_children = Bool(False)

    #: Description of what the demo does:
    description = HTML()

    #: The base URL for links:
    base_url = Property(depends_on='path')

    #: The css file for this node.
    css_filename = Str("default.css")

    #: Log of all print messages displayed:
    log = Code()

    _nice_name = Str()

    def init(self):
        self.log = ""

    # -------------------------------------------------------------------------
    #  Implementation of the 'path' property:
    # -------------------------------------------------------------------------

    def _get_path(self):
        return join(self.parent.path, self.name)

    def _get_base_url(self):
        if isdir(self.path):
            base_dir = self.path
        else:
            base_dir = dirname(self.path)
        return base_dir

    # -------------------------------------------------------------------------
    #  Implementation of the 'nice_name' property:
    # -------------------------------------------------------------------------

    def _get_nice_name(self):
        if not self._nice_name:
            name, ext = splitext(self.name)
            self._nice_name = user_name_for(name)
        return self._nice_name

    def _set_nice_name(self, value):
        old = self.nice_name
        self._nice_name = value
        self.trait_property_changed("nice_name", old, value)

    # -------------------------------------------------------------------------
    #  Returns whether or not the object has children:
    # -------------------------------------------------------------------------

    def has_children(self):
        """ Returns whether or not the object has children.
        """
        return False

    def get_children(self):
        """ Gets the demo file's children.
        """
        return []
예제 #7
0
class CiteOverlapGUI(HasTraits):
    """GUI for Citation Overlap."""

    #: str: Default extractor option to prompt user to select an extractor.
    _DEFAULT_EXTRACTOR = "Select..."

    #: int: Default number of import views.
    _DEFAULT_NUM_IMPORTS = 3

    #: OrderedDict[str, str]: Dictionary of separator descriptions to
    # separator characters.
    _EXPORT_SEPS = OrderedDict((
        ('Tabs (.tsv)', '\t'),
        ('Comma (.csv)', ','),
        ('Bar (.csv)', '|'),
        ('Semi-colon (.csv)', ';'),
    ))

    # handler triggers
    selectSheetTab = Int(-1)  # tab index to select
    renameSheetTab = Int(-1)  # tab index to rename
    renameSheetName = Str  # new tab name

    # CONTROL PANEL TRAITS

    # citation list import views
    importMedline = Instance(CiteImport)
    importEmbase = Instance(CiteImport)
    importScopus = Instance(CiteImport)
    importOther1 = Instance(CiteImport)
    importOther2 = Instance(CiteImport)
    importOther3 = Instance(CiteImport)
    importOther4 = Instance(CiteImport)
    _importAddBtn = Button('Add Sheet')

    # extractor drop-downs
    _extractorNames = Instance(TraitsList)
    _extractorAddBtn = Button('Add Extractor')

    # button to find overlaps and progress bar
    _overlapBtn = Button('Find Overlaps')
    _progBarPct = Int(0)
    _progBarMsg = Str('Awaiting overlaps')

    # table export
    _exportBtn = Button('Export Tables')
    _exportSep = Str
    _exportSepNames = Instance(TraitsList)
    _statusBarMsg = Str

    # HELP PANEL TRAITS

    pkgDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    docsDir = os.path.join(pkgDir, "docs")
    if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
        # detect frozen env using the PyInstaller-specific attributes
        # (currently works even without this setting through symlinks)
        docsDir = os.path.realpath(os.path.join(sys._MEIPASS, "docs"))
    with open(os.path.join(docsDir, "sidebar.html"), mode="r") as help_file:
        helpMsg = "".join(help_file.readlines())

    _helpHtml = HTML(helpMsg)

    # SHEETS TRAITS

    # counter for number of "other citation" sheets
    _numCitOther = Int(0)
    _tabularArgs = {
        'editable': True,
        'auto_resize_rows': True,
        'stretch_last_section': False
    }

    # Import view groups, which need an adapter set here to avoid sharing
    # the adapter across view instances

    # MEDLINE table
    _medlineAdapter = TableArrayAdapter()
    _medlineTable = TabularEditor(adapter=_medlineAdapter, **_tabularArgs)
    _medline = CiteSheet(adapter=_medlineAdapter)

    # Embase table
    _embaseAdapter = TableArrayAdapter()
    _embaseTable = TabularEditor(adapter=_embaseAdapter, **_tabularArgs)
    _embase = CiteSheet(adapter=_embaseAdapter)

    # Scopus table
    _scopusAdapter = TableArrayAdapter()
    _scopusTable = TabularEditor(adapter=_scopusAdapter, **_tabularArgs)
    _scopus = CiteSheet(adapter=_scopusAdapter)

    # Other 1 table
    _citOther1Adapter = TableArrayAdapter()
    _citOther1Table = TabularEditor(adapter=_citOther1Adapter, **_tabularArgs)
    _citOther1 = CiteSheet(adapter=_citOther1Adapter)

    # Other 2 table
    _citOther2Adapter = TableArrayAdapter()
    _citOther2Table = TabularEditor(adapter=_citOther2Adapter, **_tabularArgs)
    _citOther2 = CiteSheet(adapter=_citOther2Adapter)

    # Other 3 table
    _citOther3Adapter = TableArrayAdapter()
    _citOther3Table = TabularEditor(adapter=_citOther3Adapter, **_tabularArgs)
    _citOther3 = CiteSheet(adapter=_citOther3Adapter)

    # Other 4 table
    _citOther4Adapter = TableArrayAdapter()
    _citOther4Table = TabularEditor(adapter=_citOther4Adapter, **_tabularArgs)
    _citOther4 = CiteSheet(adapter=_citOther4Adapter)

    # Overlaps output table
    _overlapsAdapter = TableArrayAdapter()
    _outputTable = TabularEditor(adapter=_overlapsAdapter, **_tabularArgs)
    _overlaps = CiteSheet(adapter=_overlapsAdapter)

    # TRAITUI WIDGETS

    # controls panel
    _controlsPanel = VGroup(
        VGroup(
            Item('importMedline', show_label=False, style='custom'),
            Item('importEmbase', show_label=False, style='custom'),
            Item('importScopus', show_label=False, style='custom'),
            Item('importOther1',
                 show_label=False,
                 style='custom',
                 visible_when='_numCitOther >= 1'),
            Item('importOther2',
                 show_label=False,
                 style='custom',
                 visible_when='_numCitOther >= 2'),
            Item('importOther3',
                 show_label=False,
                 style='custom',
                 visible_when='_numCitOther >= 3'),
            Item('importOther4',
                 show_label=False,
                 style='custom',
                 visible_when='_numCitOther >= 4'),
            HGroup(
                Item('_importAddBtn',
                     show_label=False,
                     springy=True,
                     enabled_when='_numCitOther <= object._DEFAULT_NUM_IMPORTS'
                     ),
                Item('_extractorAddBtn', show_label=False, springy=True),
            ),
            label='Load Citation Files',
        ),
        VGroup(
            HGroup(Item('_overlapBtn', show_label=False, springy=True), ),
            Item('_progBarPct',
                 show_label=False,
                 editor=ProgressEditor(min=0,
                                       max=100,
                                       message_name='_progBarMsg')),
            HGroup(
                Item('_exportBtn', show_label=False, springy=True),
                Item("_exportSep",
                     label="Separator",
                     editor=CheckListEditor(
                         name="object._exportSepNames.selections")),
            ),
            label='Detect Overlapping Citations',
        ),
        label="Import",
    )

    # help panel
    _helpPanel = VGroup(
        Item("_helpHtml",
             editor=HTMLEditor(format_text=True),
             show_label=False),
        label="Help",
    )

    # tabbed panel for sidebar
    _sidebarTabs = Tabbed(
        _controlsPanel,
        _helpPanel,
    )

    # Tabbed viewers of tables

    # WORKAROUND: Because of an apparent limitation in adding tabs dynamically
    # in TraitsUI (https://github.com/enthought/traitsui/pull/1456), separate
    # views are created and toggled depending on the number of "other" sheets,
    # toggled by the "visible_when" flag

    # default tabbed viewer
    _tableView1 = Tabbed(
        Item('object._medline.data',
             editor=_medlineTable,
             show_label=False,
             width=1000),
        Item('object._embase.data', editor=_embaseTable, show_label=False),
        Item('object._scopus.data', editor=_scopusTable, show_label=False),
        Item('object._overlaps.data', editor=_outputTable, show_label=False),
        visible_when='_numCitOther == 0',
    )

    # tabbed viewer of tables with one "other" sheet
    _tableView2 = Tabbed(
        Item('object._medline.data',
             editor=_medlineTable,
             show_label=False,
             width=1000),
        Item('object._embase.data', editor=_embaseTable, show_label=False),
        Item('object._scopus.data', editor=_scopusTable, show_label=False),
        Item('object._citOther1.data',
             editor=_citOther1Table,
             show_label=False),
        Item('object._overlaps.data', editor=_outputTable, show_label=False),
        visible_when='_numCitOther == 1',
    )

    # tabbed viewer of tables with two "other" sheets
    _tableView3 = Tabbed(
        Item('object._medline.data',
             editor=_medlineTable,
             show_label=False,
             width=1000),
        Item('object._embase.data', editor=_embaseTable, show_label=False),
        Item('object._scopus.data', editor=_scopusTable, show_label=False),
        Item('object._citOther1.data',
             editor=_citOther1Table,
             show_label=False),
        Item('object._citOther2.data',
             editor=_citOther2Table,
             show_label=False),
        Item('object._overlaps.data', editor=_outputTable, show_label=False),
        visible_when='_numCitOther == 2',
    )

    # tabbed viewer of tables with three "other" sheets
    _tableView4 = Tabbed(
        Item('object._medline.data',
             editor=_medlineTable,
             show_label=False,
             width=1000),
        Item('object._embase.data', editor=_embaseTable, show_label=False),
        Item('object._scopus.data', editor=_scopusTable, show_label=False),
        Item('object._citOther1.data',
             editor=_citOther1Table,
             show_label=False),
        Item('object._citOther2.data',
             editor=_citOther2Table,
             show_label=False),
        Item('object._citOther3.data',
             editor=_citOther2Table,
             show_label=False),
        Item('object._overlaps.data', editor=_outputTable, show_label=False),
        visible_when='_numCitOther == 3',
    )

    # tabbed viewer of tables with four "other" sheets
    _tableView5 = Tabbed(
        Item('object._medline.data',
             editor=_medlineTable,
             show_label=False,
             width=1000),
        Item('object._embase.data', editor=_embaseTable, show_label=False),
        Item('object._scopus.data', editor=_scopusTable, show_label=False),
        Item('object._citOther1.data',
             editor=_citOther1Table,
             show_label=False),
        Item('object._citOther2.data',
             editor=_citOther2Table,
             show_label=False),
        Item('object._citOther3.data',
             editor=_citOther2Table,
             show_label=False),
        Item('object._citOther4.data',
             editor=_citOther2Table,
             show_label=False),
        Item('object._overlaps.data', editor=_outputTable, show_label=False),
        visible_when='_numCitOther == 4',
    )

    # main view
    view = View(
        HSplit(
            _sidebarTabs,
            # only one table view should be displayed at a time
            Group(
                _tableView1,
                _tableView2,
                _tableView3,
                _tableView4,
                _tableView5,
            ),
        ),
        width=1300,  # also influenced by _tableView width
        height=800,
        title='Citation Overlap',
        resizable=True,
        handler=CiteOverlapHandler(),
        statusbar="_statusBarMsg")

    def __init__(self):
        """Initialize the GUI."""
        super().__init__()

        # set up import views
        self.importMedline = CiteImport(sheet=self._medline)
        self.importEmbase = CiteImport(sheet=self._embase)
        self.importScopus = CiteImport(sheet=self._scopus)
        self.importOther1 = CiteImport(sheet=self._citOther1)
        self.importOther2 = CiteImport(sheet=self._citOther2)
        self.importOther3 = CiteImport(sheet=self._citOther3)
        self.importOther4 = CiteImport(sheet=self._citOther4)
        self.importViews = (
            self.importMedline,
            self.importEmbase,
            self.importScopus,
            self.importOther1,
            self.importOther2,
            self.importOther3,
            self.importOther4,
        )

        # populate drop-down of available extractors from directory of
        # extractors, displaying only basename but keeping dict with full path
        extractor_paths = []
        for extractor_dir in config.extractor_dirs:
            extractor_paths.extend(glob.glob(str(extractor_dir / "*")))
        self._extractor_paths = {
            os.path.basename(f): f
            for f in extractor_paths
        }
        self._updateExtractorNames(True)
        for importer in self.importViews:
            importer.observe(self.renameTabEvent, "extractor")
            importer.observe(self.importFile, "path")
            importer.observe(self.clearSheet, "clearBtn")

        # populate drop-down of separators/delimiters
        self._exportSepNames = TraitsList()
        self._exportSepNames.selections = list(self._EXPORT_SEPS.keys())
        self._exportSep = self._exportSepNames.selections[0]

        # extractor and overlaps thread instances
        self.dbExtractor = extractor.DbExtractor()
        self._overlapsThread = None

        # last opened directory
        self._save_dir = None

    def _updateExtractorNames(self, reset=False):
        """Update the list of extractor names shown in the combo boxes.
		
		Args:
			reset (bool): True to reset to default selections; defaults to
				False.
		
		"""
        if reset:
            # pick default selections for each extractor
            selections = [e.value for e in extractor.DefaultExtractors]
            numExtra = len(self.importViews) - len(selections)
            if numExtra > 0:
                selections.extend([self._DEFAULT_EXTRACTOR] * numExtra)
        else:
            # keep current selections
            selections = [v.extractor for v in self.importViews]
        # update combo box from extractor path keys
        self._extractorNames = TraitsList()
        extractorNames = [self._DEFAULT_EXTRACTOR]
        extractorNames.extend(self._extractor_paths.keys())
        self._extractorNames.selections = extractorNames

        # assign default extractor selections
        for view, selection in zip(self.importViews, selections):
            view.extractorNames = self._extractorNames
            view.extractor = selection

    def renameTabEvent(self, event):
        """Handler to rename a spreadsheet tab.
		
		Args:
			event (:class:`traits.observation.events.TraitChangeEvent`): Event.

		"""
        self.renameSheetTab = self.importViews.index(event.object)
        self.renameSheetName = _displayExtractor(event.object.extractor)

    def clearSheet(self, event):
        """Clear the sheet associated with an import view.
		
		Args:
			event (:class:`traits.observation.events.TraitChangeEvent`): Event.

		"""
        event.object.sheet.data = np.empty((0, 0))
        del self.dbExtractor.dbsParsed[event.object.dbName]
        event.object.path = ''

    @staticmethod
    def _df_to_cols(df):
        """Convert a data frame to table columns with widths adjusted to
		fit the column width up to a given max amount.

		Args:
			df (:obj:`pd.DataFrame`): Data frame to enter into table.

		Returns:
			dict[int, int], list[(str, Any)], :obj:`np.ndarray`: Dictionary of
			column indices to width, list of column tuples given as
			``(col_name, col_ID)``, and data frame as a Numpy arry.

		"""
        colWidths = []
        colsIDs = []
        for i, col in enumerate(df.columns.values.tolist()):
            # get widths of all rows in column as well as header
            colWidth = df[col].astype(str).str.len().tolist()
            colWidth.append(len(col))
            colWidths.append(colWidth)

            # use index as ID except for group/sub-group, where using a string
            # allows the col along with row to be accessed for individual cells
            colID = col.lower() if col in ('Group', 'Subgrp') else i
            colsIDs.append((col, colID))

        # get max width for each col, taking log to slow the width increase
        # for wider strings and capping at a max width
        widths = {
            i: min((math.log1p(max(c)) * 40, TableArrayAdapter.MAX_WIDTH))
            for i, c in enumerate(colWidths)
        }

        return widths, colsIDs, df.to_numpy()

    @on_trait_change('_importAddBtn')
    def addImport(self):
        """Add import fields and a new sheet."""
        if self._numCitOther < len(
                self.importViews) - self._DEFAULT_NUM_IMPORTS:
            # trigger additional import view and tab with new sheet
            self._numCitOther += 1

    @on_trait_change('_extractorAddBtn')
    def addExtractor(self):
        """Add an extractor to the combo box."""
        try:
            # get path to extractor from file dialog
            path = self._getFileDialogPath()
        except FileNotFoundError:
            return

        # update combo box
        pathName = os.path.basename(path)
        self._extractor_paths[pathName] = path
        self._updateExtractorNames()

    def importFile(self, event):
        """Import a database file.

		Args:
			event (:class:`traits.observation.events.TraitChangeEvent`): Event.

		Returns:
			:obj:`pd.DataFrame`: Data frame of extracted file.

		"""
        path = event.object.path
        if not os.path.exists(path):
            if path:
                # file inaccessible, or manually edited, non-accessible path
                self._statusBarMsg = f'{path} could not be found, skipping'
            return None
        self._save_dir = os.path.dirname(path)

        try:
            # extract file
            extractorPath = self._extractor_paths[event.object.extractor]
            df, dbName = self.dbExtractor.extractDb(path, extractorPath)
            event.object.dbName = dbName
            try:
                self.dbExtractor.checkExtraction(df)
                self._statusBarMsg = f'Imported file from {path}'
            except SyntaxWarning as e:
                msg = \
                 f'WARNING: {str(e)}. Please check the selected file ' \
                 f'source and reload the citation file.'
                self._statusBarMsg = msg
                _logger.warning(msg)
            sheet = event.object.sheet
            if df is not None and sheet is not None:
                # output data frame to associated table
                sheet.adapter._widths, sheet.adapter.columns, sheet.data = \
                 self._df_to_cols(df)
                self.selectSheetTab = self.importViews.index(event.object)
            return df
        except (FileNotFoundError, SyntaxError) as e:
            self._statusBarMsg = str(e)
        return None

    def _updateProgBar(self, pct: int, msg: str):
        """"Update progress bar.
		
		Args:
			pct: Percentage complete, from 0-100.
			msg: Message to display.

		"""
        self._progBarPct = pct
        self._progBarMsg = msg

    def _overlapsHandler(self, result: Union[pd.DataFrame, str]):
        """Handle result from finding overlaps.
		
		Args:
			result: Data frame of overlaps or message.
		
		"""
        if isinstance(result, pd.DataFrame):
            self._progBarPct = 100
            if result is None:
                # clear any existing data in sheet if no citation lists
                self._overlaps.data = np.empty((0, 0))
                self._statusBarMsg = 'No citation lists found'
                return

            # populate overlaps sheet
            self._overlaps.adapter._widths, self._overlaps.adapter.columns, \
             self._overlaps.data = self._df_to_cols(result)
            self.selectSheetTab = self._DEFAULT_NUM_IMPORTS + self._numCitOther
            self._statusBarMsg = 'Found overlaps across databases'
        elif isinstance(result, str):
            # show message
            self._statusBarMsg = result

    @on_trait_change('_overlapBtn')
    def findOverlaps(self):
        """Find overlaps in a thread."""
        self._progBarPct = 0
        self._overlapsThread = overlaps_thread.OverlapsThread(
            self.dbExtractor, self._overlapsHandler, self._updateProgBar)
        self._overlapsThread.start()

    def _getFileDialogPath(self, default_path='', mode='open'):
        """Get a path from the user through a file dialog.

		Args:
			default_path (str): Initial path to display in the dialog; defaults
				to an emptry string. If :attr:`_save_dir` is set,
				``default_path`` will be joined to the save directory.
			mode (str): "open" for an open dialog, or "save as" for a save
				dialog.

		Returns:
			str: Chosen path.

		Raises:
			FileNotFoundError: User canceled file selection.

		"""
        if self._save_dir:
            # use directory of last chosen import file
            default_path = os.path.join(self._save_dir, default_path)

        # open a PyFace file dialog in save mode
        save_dialog = FileDialog(action=mode, default_path=default_path)
        if save_dialog.open() == OK:
            # get user selected path
            return save_dialog.path
        else:
            # user canceled file selection
            raise FileNotFoundError("User canceled file selection")

    @on_trait_change('_exportBtn')
    def exportTables(self):
        """Export tables to a files."""
        self.dbExtractor.saveSep = self._EXPORT_SEPS[self._exportSep]
        try:
            # prompt user to select an output file path for the combined list;
            # save along with filtered folders in a separate dir there
            save_path = self._getFileDialogPath(
                self.dbExtractor.DEFAULT_OVERLAPS_PATH, "save as")
            self.dbExtractor.exportDataFrames(save_path)
            self._statusBarMsg = (
                f'Saved combined table to "{save_path}" and filtered tables '
                f'alongside in "{self.dbExtractor.DEFAULT_CLEANED_DIR_PATH}"')
        except FileNotFoundError:
            print("Skipping file save")
예제 #8
0
class MainWindow(HasTraits):
    title = String()
    date = String()
    category = Enum(['nus', 'travel', 'pics', 'food'])
    dirpath = Directory()
    codedir = Directory()
    html_text = String('')

    status = String('no connection')
    ftp_url = String('files.000webhost.com')
    ftp_user = String('maxinsingapore')
    ftp_dir = String('public_html/pictures')
    ftp_pw = String()

    upload_btn = Button('Upload')
    html_preview = HTML()
    preview_btn = Button('HTML preview')

    uploadthread = Instance(UploadThread)
    notuploading = Bool(True)

    html_intro_1 = '''<!DOCTYPE html><html><head><link href="main.css" rel="stylesheet"/>
        <title>Max in Singapore</title>
        
    </head>
    <body>
        
        <?php require("ground.php"); ?>
        
        <div class = "title">
            <a href="'''

    html_intro_2 = '''.php"><figure><p>back</p</figure></a>
        </div>
        <div class="center">'''

    html_end = '''              </div>
            </div>

            
                    </div>
        
                    </body>
            </html>'''

    traits_view = View(
        HGroup('ftp_url', 'ftp_user', 'ftp_pw', 'ftp_dir'),
        HGroup('title', 'date', 'category'),
        HGroup(Item('html_text', editor=CodeEditor()),
               Item('html_preview', editor=HTMLEditor())), 'preview_btn',
        Item('dirpath', label='Photo Directory'),
        Item('codedir', label='Code Directory'),
        Item('status', style='readonly'),
        Item('upload_btn', enabled_when='notuploading'))

    def _preview_btn_fired(self):
        html_intro = self.html_intro_1 + self.category + self.html_intro_2
        self.html_preview = html_intro + self.html_text + self.html_end

    def _upload_btn_fired(self):
        if self.dirpath != '' and self.codedir != '':
            self.notuploading = False
            self.uploadthread = UploadThread()
            self.uploadthread.wants_abort = False
            self.uploadthread.master = self
            self.uploadthread.start()
        else:
            self.status = "choose directories"
class Comparator(HasTraits):
    """ The main application.
    """

    #### Configuration traits ##################################################

    # The root directory of the test suite.
    suitedir = Str()

    # Mapping of SVG basenames to their reference PNGs. Use None if there is no
    # reference PNG.
    svg_png = Dict()

    # The list of SVG file names.
    svg_files = List()

    # The name of the default PNG file to display when no reference PNG exists.
    default_png = Str(os.path.join(this_dir, 'images/default.png'))

    #### State traits ##########################################################

    # The currently selected SVG file.
    current_file = Str()
    abs_current_file = Property(depends_on=['current_file'])

    # The current XML ElementTree root Element and its XMLTree view model.
    current_xml = Any()
    current_xml_view = Any()

    # The profilers.
    profile_this = Instance(ProfileThis, args=())

    #### GUI traits ############################################################

    # The text showing the current mouse coordinates over any of the components.
    mouse_coords = Property(Str, depends_on=['ch_controller.svg_coords'])

    # Move forward and backward through the list of SVG files.
    move_forward = Button('>>')
    move_backward = Button('<<')

    # The description of the test.
    description = HTML()

    document = Instance(document.SVGDocument)

    # The components to view.
    kiva_component = ComponentTrait(klass=SVGComponent)
    ref_component = ComponentTrait(klass=ImageComponent, args=())
    ch_controller = Instance(MultiController)

    # The profiler views.
    parsing_sike = Instance(Sike, args=())
    drawing_sike = Instance(Sike, args=())
    wx_doc_sike = Instance(Sike, args=())
    kiva_doc_sike = Instance(Sike, args=())

    traits_view = tui.View(
        tui.Tabbed(
            tui.VGroup(
                tui.HGroup(
                    tui.Item('current_file',
                             editor=tui.EnumEditor(name='svg_files'),
                             style='simple',
                             width=1.0,
                             show_label=False),
                    tui.Item(
                        'move_backward',
                        show_label=False,
                        enabled_when="svg_files.index(current_file) != 0"),
                    tui.Item(
                        'move_forward',
                        show_label=False,
                        enabled_when=
                        "svg_files.index(current_file) != len(svg_files)-1"),
                ),
                tui.VSplit(
                    tui.HSplit(
                        tui.Item('description',
                                 label='Description',
                                 show_label=False),
                        tui.Item('current_xml_view',
                                 editor=xml_tree_editor,
                                 show_label=False),
                    ),
                    tui.HSplit(
                        tui.Item('document',
                                 editor=SVGEditor(),
                                 show_label=False),
                        tui.Item('kiva_component', show_label=False),
                        tui.Item('ref_component', show_label=False),
                        # TODO: tui.Item('agg_component', show_label=False),
                    ),
                ),
                label='SVG',
            ),
            tui.Item('parsing_sike',
                     style='custom',
                     show_label=False,
                     label='Parsing Profile'),
            tui.Item('drawing_sike',
                     style='custom',
                     show_label=False,
                     label='Kiva Drawing Profile'),
            tui.Item('wx_doc_sike',
                     style='custom',
                     show_label=False,
                     label='Creating WX document'),
            tui.Item('kiva_doc_sike',
                     style='custom',
                     show_label=False,
                     label='Creating WX document'),
        ),
        width=1280,
        height=768,
        resizable=True,
        statusbar='mouse_coords',
        title='SVG Comparator',
    )

    def __init__(self, **traits):
        super(Comparator, self).__init__(**traits)
        kiva_ch = activate_tool(self.kiva_component,
                                Crosshair(self.kiva_component))
        ref_ch = activate_tool(self.ref_component,
                               Crosshair(self.ref_component))
        self.ch_controller = MultiController(kiva_ch, ref_ch)

    @classmethod
    def fromsuitedir(cls, dirname, **traits):
        """ Find all SVG files and their related reference PNG files under
        a directory.

        This assumes that the SVGs are located under <dirname>/svg/ and the
        related PNGs under <dirname>/png/ and that there are no subdirectories.
        """
        dirname = os.path.abspath(dirname)
        svgs = glob.glob(os.path.join(dirname, 'svg', '*.svg'))
        pngdir = os.path.join(dirname, 'png')
        d = {}
        for svg in svgs:
            png = None
            base = os.path.splitext(os.path.basename(svg))[0]
            for prefix in ('full-', 'basic-', 'tiny-', ''):
                fn = os.path.join(pngdir, prefix + base + '.png')
                if os.path.exists(fn):
                    png = os.path.basename(fn)
                    break
            d[os.path.basename(svg)] = png
        svgs = sorted(d)
        x = cls(suitedir=dirname, svg_png=d, svg_files=svgs, **traits)
        x.current_file = svgs[0]
        return x

    def display_reference_png(self, filename):
        """ Read the image file and shove its data into the display component.
        """
        img = Image.open(filename)
        arr = np.array(img)
        self.ref_component.image = arr

    def display_test_description(self):
        """ Extract the test description for display.
        """
        html = ET.Element('html')

        title = self.current_xml.find('.//{http://www.w3.org/2000/svg}title')
        if title is not None:
            title_text = title.text
        else:
            title_text = os.path.splitext(self.current_file)[0]
        p = ET.SubElement(html, 'p')
        b = ET.SubElement(p, 'b')
        b.text = 'Title: '
        b.tail = title_text

        desc_text = None
        version_text = None
        desc = self.current_xml.find('.//{http://www.w3.org/2000/svg}desc')
        if desc is not None:
            desc_text = desc.text
        else:
            testcase = self.current_xml.find(
                './/{http://www.w3.org/2000/02/svg/testsuite/description/}SVGTestCase'
            )
            if testcase is not None:
                desc_text = testcase.get('desc', None)
                version_text = testcase.get('version', None)
        if desc_text is not None:
            p = ET.SubElement(html, 'p')
            b = ET.SubElement(p, 'b')
            b.text = 'Description: '
            b.tail = normalize_text(desc_text)

        if version_text is None:
            script = self.current_xml.find(
                './/{http://www.w3.org/2000/02/svg/testsuite/description/}OperatorScript'
            )
            if script is not None:
                version_text = script.get('version', None)
        if version_text is not None:
            p = ET.SubElement(html, 'p')
            b = ET.SubElement(p, 'b')
            b.text = 'Version: '
            b.tail = version_text

        paras = self.current_xml.findall(
            './/{http://www.w3.org/2000/02/svg/testsuite/description/}Paragraph'
        )
        if len(paras) > 0:
            div = ET.SubElement(html, 'div')
            for para in paras:
                p = ET.SubElement(div, 'p')
                p.text = normalize_text(para.text)
                # Copy over any children elements like <a>.
                p[:] = para[:]

        tree = ET.ElementTree(html)
        f = StringIO()
        tree.write(f)
        text = f.getvalue()
        self.description = text

    def locate_file(self, name, kind):
        """ Find the location of the given file in the suite.

        Parameters
        ----------
        name : str
            Path of the file relative to the suitedir.
        kind : either 'svg' or 'png'
            The kind of file.

        Returns
        -------
        path : str
            The full path to the file.
        """
        return os.path.join(self.suitedir, kind, name)

    def _kiva_component_default(self):
        return SVGComponent(profile_this=self.profile_this)

    def _move_backward_fired(self):
        idx = self.svg_files.index(self.current_file)
        idx = max(idx - 1, 0)
        self.current_file = self.svg_files[idx]

    def _move_forward_fired(self):
        idx = self.svg_files.index(self.current_file)
        idx = min(idx + 1, len(self.svg_files) - 1)
        self.current_file = self.svg_files[idx]

    def _get_abs_current_file(self):
        return self.locate_file(self.current_file, 'svg')

    def _current_file_changed(self, new):
        # Reset the warnings filters. While it's good to only get 1 warning per
        # file, we want to get the same warning again if a new file issues it.
        warnings.resetwarnings()

        self.profile_this.start('Parsing')
        self.current_xml = ET.parse(self.abs_current_file).getroot()
        self.current_xml_view = xml_to_tree(self.current_xml)
        resources = document.ResourceGetter.fromfilename(self.abs_current_file)
        self.profile_this.stop()
        try:
            self.profile_this.start('Creating WX document')
            self.document = document.SVGDocument(self.current_xml,
                                                 resources=resources,
                                                 renderer=WxRenderer)
        except:
            logger.exception('Error parsing document %s', new)
            self.document = None

        self.profile_this.stop()

        try:
            self.profile_this.start('Creating Kiva document')
            self.kiva_component.document = document.SVGDocument(
                self.current_xml, resources=resources, renderer=KivaRenderer)
        except Exception as e:
            logger.exception('Error parsing document %s', new)
            self.kiva_component.document

        self.profile_this.stop()

        png_file = self.svg_png.get(new, None)
        if png_file is None:
            png_file = self.default_png
        else:
            png_file = self.locate_file(png_file, 'png')
        self.display_test_description()
        self.display_reference_png(png_file)

    def _get_mouse_coords(self):
        if self.ch_controller is None:
            return ''
        else:
            return '%1.3g %1.3g' % self.ch_controller.svg_coords

    @on_trait_change('profile_this:profile_ended')
    def _update_profiling(self, new):
        if new is not None:
            name, p = new
            stats = pstats.Stats(p)
            if name == 'Parsing':
                self.parsing_sike.stats = stats
            elif name == 'Drawing':
                self.drawing_sike.stats = stats
            elif name == 'Creating WX document':
                self.wx_doc_sike.stats = stats
            elif name == 'Creating Kiva document':
                self.kiva_doc_sike.stats = stats