예제 #1
0
    def test_open_external_link_externally(self):
        model = HTMLModel(content="""
        <html>
            <a
              href='test://testing'
              target='_blank'
              style='display:block; width: 100%; height: 100%'>
                External Link
            </a>
        </html>
        """)
        view = View(
            Item("content", editor=HTMLEditor(open_externally=True))
        )

        with self.tester.create_ui(model, dict(view=view)) as ui:
            html_view = self.tester.find_by_name(ui, "content")
            with mock.patch("webbrowser.open_new") as mocked_browser:
                html_view.perform(MouseClick())
            self.assertIn(
                "External Link",
                html_view.inspect(HTMLContent()),
            )

            is_webkit = _is_webkit_page(html_view._target.control.page())

        if is_webkit:
            # This is the expected behavior.
            mocked_browser.assert_called_once_with("test://testing")
        else:
            # Expected failure:
            # See enthought/traitsui#1464
            # This is the current unexpected behavior if QtWebEngine is used.
            mocked_browser.assert_not_called()
예제 #2
0
def get_view(base_url_name):
    return View(
        Item("content",
             editor=HTMLEditor(
                 format_text=True,
                 base_url_name=base_url_name,
             )))
예제 #3
0
    def test_open_internal_link_externally(self):
        # this test requires Qt because it relies on the link filling up
        # the entire page through the use of CSS, which isn't supported
        # by Wx.
        model = HTMLModel(content="""
        <html>
            <a
              href='test://testing'
              target='_self'
              style='display:block; width: 100%; height: 100%'>
                Internal Link
            </a>
        </html>
        """)
        view = View(
            Item("content", editor=HTMLEditor(open_externally=True))
        )

        with self.tester.create_ui(model, dict(view=view)) as ui:
            html_view = self.tester.find_by_name(ui, "content")
            with mock.patch("webbrowser.open_new") as mocked_browser:
                html_view.perform(MouseClick())
            self.assertIn(
                "Internal Link",
                html_view.inspect(HTMLContent()),
            )

        mocked_browser.assert_called_once_with("test://testing")
예제 #4
0
    def test_open_external_link(self):
        # this test requires Qt because it relies on the link filling up
        # the entire page through the use of CSS, which isn't supported
        # by Wx.
        model = HTMLModel(content="""
        <html>
            <a
              href='test://testing'
              target='_blank'
              style='display:block; width: 100%; height: 100%'>
                External Link
            </a>
        </html>
        """)
        view = View(
            Item("content", editor=HTMLEditor())
        )

        with self.tester.create_ui(model, dict(view=view)) as ui:
            html_view = self.tester.find_by_name(ui, "content")
            with mock.patch("webbrowser.open_new") as mocked_browser:
                html_view.perform(MouseClick())
            self.assertIn(
                "External Link",
                html_view.inspect(HTMLContent()),
            )

        # See enthought/traitsui#1464
        # This is the expected behaviour:
        # mocked_browser.assert_called_once_with("test://testing")
        # However, this is the current unexpected behaviour
        mocked_browser.assert_not_called()
예제 #5
0
class TestHTML(HasPrivateTraits):

    # -------------------------------------------------------------------------
    #  Trait definitions:
    # -------------------------------------------------------------------------

    # Text string to display as HTML:
    html = Code(sample)

    # -------------------------------------------------------------------------
    #  Traits view definitions:
    # -------------------------------------------------------------------------

    view = View(
        Group(
            [Item('html#@', editor=HTMLEditor(format_text=True)), '|<>'],
            ['{Enter formatted text and/or HTML below:}@', 'html#@', '|<>'],
            '|<>',
            layout='split',
        ),
        title='HTML Editor Test',
        resizable=True,
        width=0.4,
        height=0.6,
    )
예제 #6
0
class InfoViewer(HasTraits):
    text = Str()

    view = View(Item('text', editor=HTMLEditor(format_text=True), style='readonly', show_label=False),
                resizable=True,
                width=0.3,
                height=0.3)
예제 #7
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
예제 #8
0
def html_editor():
    """ Factory function for an "editor" that displays a multi-line string as
    interpreted HTML.
    """
    global HTMLTextEditor

    if HTMLTextEditor is None:
        from traitsui.api import HTMLEditor
        HTMLTextEditor = HTMLEditor()

    return HTMLTextEditor
예제 #9
0
class ShowUsage(HasTraits):
  usage_str = Str()
  traits_view = View(
                     Item("usage_str", style='readonly', show_label=False, 
                          editor=HTMLEditor(), resizable=True),
                     width=680, resizable=True, icon=icon,
                     title='Swift Console Usage')
  def __init__(self, usage, error_str):
    if error_str != "":
      self.usage_str = "<pre>" + error_str + '<br><br><br>' + usage + "</pre>"
    else:
      self.usage_str = "<pre>" + usage + "</pre>"
예제 #10
0
class EK60Dialog(HasTraits):
    """
    This class implements a dialog box that displays bandwidth averaged sphere
    TS estimates.
    """
    html_text = Str()

    view = View(Item('html_text',
                     editor=HTMLEditor(format_text=False),
                     show_label=False),
                title='EK60',
                buttons=[OKButton],
                resizable=True)
예제 #11
0
class ModelHelpPane(TraitsDockPane):
    """ A dock pane for viewing any help associated with a model.
    """

    #### 'ITaskPane' interface ################################################

    id = "example.attractors.model_help_pane"
    name = "Model Information"

    #### 'ModelConfigPane' interface ##########################################

    model = Instance(HasTraits)

    html = Property(Str, observe="model")

    view = View(
        Item(
            "pane.html",
            editor=HTMLEditor(base_url=HELP_PATH, open_externally=True),
            show_label=False,
        ),
        width=300,
        resizable=True,
    )

    ###########################################################################
    # Protected interface.
    ###########################################################################

    @cached_property
    def _get_html(self):
        """ Fetch the help HTML for the current model.
        """
        if self.model is None:
            return "No model selected."

        # Determine the name of the model.
        model = self.model
        while hasattr(model, "adaptee"):
            model = model.adaptee
        name = model.__class__.__name__.lower()

        # Load HTML file, if possible.
        path = os.path.join(HELP_PATH, name + ".html")
        if os.path.isfile(path):
            with codecs.open(path, "r", "utf-8") as f:
                return f.read()
        else:
            return "No information available for model."
예제 #12
0
    def traits_view(self):
        v = View(
            Item('message',
                 style='custom',
                 editor=HTMLEditor(),
                 show_label=False),
            HGroup(spring, Item('dismiss_notification')),
            kind='modal',
            handler=VersionInfoHandler,
            buttons=['OK'],
            width=300,
            height=300,
            title='Version Info',
        )

        return v
예제 #13
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)
예제 #14
0
class HTMLItem(ADescriptionItem):
    """ Defines a class used for displaying a single HTML page within the tutor
        using the default Traits HTML editor.
    """

    url = Str

    view = View(
        Item('content',
             style='readonly',
             show_label=False,
             editor=HTMLEditor()))

    def _url_changed(self, url):
        """ Sets the item title when the 'url' is changed.
        """
        match = url_pat1.match(url)
        if match is not None:
            title = match.group(2).strip()
        else:
            title = url.strip()
            col = title.rfind('/')
            if col >= 0:
                title = os.path.splitext(title[col + 1:])[0]

        self.title = title

    @cached_property
    def _get_content(self):
        """ Returns the item content.
        """
        url = self.url
        if url != '':
            match = url_pat1.match(url)
            if match is not None:
                url = match.group(1) + match.group(3)

            return url

        return read_file(self.path)

    def _set_content(self, content):
        """ Sets the item content.
        """
        self._content = content
예제 #15
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
예제 #16
0
class AboutDialog(HasTraits):
    """
    This class implements an About dialog box using the TraitsUI user interface
    framework. The text displayed in the dialog is read from a file containing
    html-formatted text.
    """

    helpFile = 'about.html'
    about_text = Str()
    view = View(Item('about_text', editor=HTMLEditor(), show_label=False),
                resizable=True,
                title='About',
                buttons=[OKButton])

    def load_help_text(self):
        """
        Loads the help text from the file.
        """
        f = open(self.helpFile, 'r')
        self.about_text = f.read()
예제 #17
0
    def traits_view(self):
        """ Default view to show for this class. """
        args = []
        kw_args = {
            "title": "Preferences Page Help",
            "buttons": ["OK"],
            "width": 800,
            "height": 800,
            "resizable": True,
            "id": "apptools.preferences.ui.preferences_manager.help",
        }
        to_show = {}

        for name, trait_obj in self.traits().items():
            if name != "trait_added" and name != "trait_modified":
                to_show[name] = trait_obj.help
        for name in to_show:
            args.append(Item(name, style="readonly", editor=HTMLEditor()))

        view = View(*args, **kw_args)
        return view
예제 #18
0
    def traits_view(self):
        """ Default view to show for this class. """
        args = []
        kw_args = {
            'title': 'Preferences Page Help',
            'buttons': ['OK'],
            'width': 800,
            'height': 800,
            'resizable': True,
            'id': 'apptools.preferences.ui.preferences_manager.help'
        }
        to_show = {}

        for name, trait_obj in self.traits().items():
            if name != 'trait_added' and name != 'trait_modified':
                to_show[name] = trait_obj.help
        for name in to_show:
            args.append(Item(name, style='readonly', editor=HTMLEditor()))

        view = View(*args, **kw_args)
        return view
예제 #19
0
    def trait_view(self, name='default'):
        rest_editor = CodeEditor(lexer='rest',
                                 selected_line='selected_line',
                                 auto_scroll=True,
                                 squiggle_lines='warning_lines',
                                 selected_text='selected_text',
                                 selected_start_pos='selected_start_pos',
                                 selected_end_pos='selected_end_pos')

        warning_editor = TabularEditor(editable=False,
                                       adapter=DocUtilsWarningAdapter(),
                                       dclicked='dclicked_warning')

        html_editor = HTMLEditor(open_externally=True,
                                 base_url_name='base_url')

        return View(Group(Group(Item('object.model.rest',
                                     style='custom',
                                     editor=rest_editor),
                                Item('html', editor=html_editor),
                                id='rest_editor_view.PairView.HorzGroup',
                                show_labels=False,
                                orientation='horizontal',
                                layout='split'),
                          Item('object.model.warnings',
                               height=0.2,
                               editor=warning_editor),
                          id='rest_editor_view.PairView.VertGroup',
                          show_labels=False,
                          orientation='vertical',
                          layout='split'),
                    id='rest_editor_view.PairView',
                    handler=ReSTHTMLPairHandler(),
                    width=800,
                    height=600,
                    resizable=True)
예제 #20
0
        _, ext = splitext(filename)
        file_factory = self._file_factory[ext]
        demo_file = file_factory(parent=self, name=filename)
        return demo_file


# -------------------------------------------------------------------------
#  Defines the demo tree editor:
# -------------------------------------------------------------------------

demo_path_view = View(
    UItem(
        "description",
        style="readonly",
        editor=HTMLEditor(
            format_text=True,
            base_url_name='base_url',
        ),
    ),
    id="demo_path_view",
    kind='subpanel',
)

demo_file_view = View(
    HSplit(
        UItem(
            "description",
            style="readonly",
            editor=HTMLEditor(
                format_text=True,
                base_url_name='base_url',
            ),
예제 #21
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")
예제 #22
0
        return len(self.resources) > 0

    def get_children(self):
        return self.resources


# -------------------------------------------------------------------------
#  Defines the demo tree editor:
# -------------------------------------------------------------------------

demo_path_view = View(
    UItem(
        "description",
        style="readonly",
        editor=HTMLEditor(
            format_text=True,
            base_url_name='base_url',
        ),
    ),
    id="demo_path_view",
    kind='subpanel',
)

demo_file_view = View(
    HSplit(
        UItem(
            "description",
            style="readonly",
            editor=HTMLEditor(
                format_text=True,
                base_url_name='base_url',
                open_externally=True,
예제 #23
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"
예제 #24
0
                if ext == '.py':
                    return True

        return False

#-------------------------------------------------------------------------------
#  Defines the demo tree editor:
#-------------------------------------------------------------------------------

path_view = View(
    Tabbed(
        Item( 'description',
              label      = 'Description',
              show_label = False,
              style      = 'readonly',
              editor=HTMLEditor(format_text=True)
        ),
        Item( 'source',
              label      = 'Source',
              show_label = False,
              style      = 'custom'
        ),
        export = 'DockWindowShell',
        id     = 'tabbed'
    ),
    id      = 'traitsui.demos.demo.path_view',
    #dock    = 'horizontal'
)

demo_view = View(
     #VSplit(
예제 #25
0
def html_editor():
    """ Factory function for an "editor" that displays a multi-line string as
    interpreted HTML.
    """
    from traitsui.api import HTMLEditor
    return HTMLEditor()