コード例 #1
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        settings.get_section('General').connect('pygments_style',
                                                self._set_style,
                                                run_now=True)

        def on_destroy(event):
            settings.get_section('General').disconnect('pygments_style',
                                                       self._set_style)

        self.bind('<Destroy>', on_destroy, add=True)
コード例 #2
0
ファイル: tabs.py プロジェクト: carlbordum/porcupine
    def save(self):
        """Save the file to the current :attr:`path`.

        This calls :meth:`save_as` if :attr:`path` is None, and returns
        False if the user cancels the save as dialog. None is returned
        on errors, and True is returned in all other cases. In other
        words, this returns True if saving succeeded.

        .. seealso:: The :virtevt:`Save` event.
        """
        if self.path is None:
            return self.save_as()

        self.event_generate('<<Save>>')

        encoding = settings.get_section('General')['encoding']
        try:
            with utils.backup_open(self.path, 'w', encoding=encoding) as f:
                for chunk in self.textwidget.iter_chunks():
                    f.write(chunk)
        except (OSError, UnicodeError) as e:
            log.exception("saving '%s' failed", self.path)
            utils.errordialog(
                type(e).__name__, "Saving failed!", traceback.format_exc())
            return None

        self.mark_saved()
        return True
コード例 #3
0
def setup():
    # TODO: loading the styles takes a long time on startup... try to
    # make it asynchronous without writing too complicated code?
    config = settings.get_section('General')
    actions.add_choice("Color Styles",
                       sorted(pygments.styles.get_all_styles()),
                       var=config.get_var('pygments_style'))
コード例 #4
0
 def change_font_size(how):
     config = settings.get_section('General')
     if how == 'reset':
         config.reset('font_size')
     else:
         try:
             config['font_size'] += (1 if how == 'bigger' else -1)
         except settings.InvalidValue:
             pass
コード例 #5
0
 def _guess_filetype(self):
     if self.path is None:
         name = settings.get_section('File Types')['default_filetype']
         self.filetype = filetypes.get_filetype_by_name(name)
     else:
         # FIXME: this may read the shebang from the file, but the file
         #        might not be saved yet because save_as() sets self.path
         #        before saving, and that's when this runs
         self.filetype = filetypes.guess_filetype(self.path)
コード例 #6
0
    def on_wheel(self, direction):
        config = settings.get_section('General')
        if direction == 'reset':
            config.reset('font_size')
            return

        try:
            config['font_size'] += (1 if direction == 'up' else -1)
        except settings.InvalidValue:
            pass
コード例 #7
0
ファイル: _run.py プロジェクト: rscales02/porcupine
 def change_font_size(how):
     # TODO: i think there is similar code in a couple other places too
     config = settings.get_section('General')
     if how == 'reset':
         config.reset('font_size')
     else:
         try:
             config['font_size'] += (1 if how == 'bigger' else -1)
         except settings.InvalidValue:
             pass
コード例 #8
0
ファイル: tabs.py プロジェクト: carlbordum/porcupine
    def _get_hash(self):
        # superstitious omg-optimization
        config = settings.get_section('General')
        encoding = config['encoding']

        result = hashlib.md5()
        for chunk in self.textwidget.iter_chunks():
            chunk = chunk.encode(encoding, errors='replace')
            result.update(chunk)

        # hash objects don't define an __eq__ so we need to use a string
        # representation of the hash
        return result.hexdigest()
コード例 #9
0
ファイル: tabs.py プロジェクト: carlbordum/porcupine
    def open_file(cls, manager, path):
        """Read a file and return a new FileTab object.

        Use this constructor if you want to open an existing file from a
        path and let the user edit it.

        :exc:`UnicodeError` or :exc:`OSError` is raised if reading the
        file fails.
        """
        config = settings.get_section('General')
        with open(path, 'r', encoding=config['encoding']) as file:
            content = file.read()
        return cls(manager, content, path)
コード例 #10
0
def setup():
    config = settings.get_section('General')
    styles = []
    thread = threading.Thread(target=load_styles_to_list, args=[styles])
    thread.daemon = True  # i don't care wtf happens to this
    thread.start()

    def check_if_it_finished():
        if thread.is_alive():
            get_main_window().after(200, check_if_it_finished)
        else:
            actions.add_choice("Color Styles",
                               styles,
                               var=config.get_var('pygments_style'))

    get_main_window().after(200, check_if_it_finished)
コード例 #11
0
# see also: https://github.com/RedFantom/ttkthemes
# TODO: capitalize theme names in menu items?
import functools
import tkinter

from porcupine import actions, settings
import ttkthemes

config = settings.get_section('General')


def on_theme_changed(style, theme_name):
    try:
        style.set_theme(theme_name)
    except tkinter.TclError as e:
        raise settings.InvalidValue(str(e)) from None


def setup():
    style = ttkthemes.ThemedStyle()

    # https://github.com/RedFantom/ttkthemes/issues/6
    # this does what style.theme_use() should do
    default_theme = style.tk.eval('return $ttk::currentTheme')

    config.add_option('ttk_theme', default_theme, reset=False)
    config.connect('ttk_theme', functools.partial(on_theme_changed, style),
                   run_now=True)
    actions.add_choice("Ttk Themes", sorted(style.get_themes()),
                       var=config.get_var('ttk_theme'))
コード例 #12
0
def _setup_actions():
    def new_file():
        _tab_manager.add_tab(tabs.FileTab(_tab_manager))

    def open_files():
        for path in _dialogs.open_files():
            try:
                tab = tabs.FileTab.open_file(_tab_manager, path)
            except (UnicodeError, OSError) as e:
                log.exception("opening '%s' failed", path)
                utils.errordialog(
                    type(e).__name__, "Opening failed!",
                    traceback.format_exc())
                continue

            _tab_manager.add_tab(tab)

    def close_current_tab():
        if _tab_manager.current_tab.can_be_closed():
            _tab_manager.close_tab(_tab_manager.current_tab)

    add_action(new_file, "File/New File", ("Ctrl+N", '<Control-n>'))
    add_action(open_files, "File/Open", ("Ctrl+O", '<Control-o>'))
    add_action((lambda: _tab_manager.current_tab.save()),
               "File/Save", ("Ctrl+S", '<Control-s>'),
               tabtypes=[tabs.FileTab])
    add_action((lambda: _tab_manager.current_tab.save_as()),
               "File/Save As...", ("Ctrl+Shift+S", '<Control-S>'),
               tabtypes=[tabs.FileTab])
    menubar.get_menu("File").add_separator()

    # TODO: disable File/Quit when there are tabs, it's too easy to hit
    # Ctrl+Q accidentally
    add_action(close_current_tab,
               "File/Close", ("Ctrl+W", '<Control-w>'),
               tabtypes=[tabs.Tab])
    add_action(quit, "File/Quit", ("Ctrl+Q", '<Control-q>'))

    def textmethod(attribute):
        def result():
            textwidget = _tab_manager.current_tab.textwidget
            method = getattr(textwidget, attribute)
            method()

        return result

    # FIXME: bind these in a text widget only, not globally
    add_action(textmethod('undo'),
               "Edit/Undo", ("Ctrl+Z", '<Control-z>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('redo'),
               "Edit/Redo", ("Ctrl+Y", '<Control-y>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('cut'),
               "Edit/Cut", ("Ctrl+X", '<Control-x>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('copy'),
               "Edit/Copy", ("Ctrl+C", '<Control-c>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('paste'),
               "Edit/Paste", ("Ctrl+V", '<Control-v>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('select_all'),
               "Edit/Select All", ("Ctrl+A", '<Control-a>'),
               tabtypes=[tabs.FileTab])
    menubar.get_menu("Edit").add_separator()

    # FIXME: this separator thing is a mess :(
    menubar.get_menu("Edit").add_separator()
    add_action(
        functools.partial(settings.show_dialog, porcupine.get_main_window()),
        "Edit/Porcupine Settings...")

    menubar.get_menu("Color Themes")  # this goes between Edit and View

    # the font size stuff are bound by the textwidget itself, that's why
    # there are Nones everywhere
    add_action((lambda: _tab_manager.current_tab.textwidget.on_wheel('up')),
               "View/Bigger Font", ("Ctrl+Plus", None),
               tabtypes=[tabs.FileTab])
    add_action((lambda: _tab_manager.current_tab.textwidget.on_wheel('down')),
               "View/Smaller Font", ("Ctrl+Minus", None),
               tabtypes=[tabs.FileTab])
    add_action((lambda: _tab_manager.current_tab.textwidget.on_wheel('reset')),
               "View/Reset Font Size", ("Ctrl+Zero", None),
               tabtypes=[tabs.FileTab])
    menubar.get_menu("View").add_separator()

    def add_link(menupath, url):
        add_action(functools.partial(webbrowser.open, url), menupath)

    # TODO: an about dialog that shows porcupine version, Python version
    #       and where porcupine is installed
    # TODO: porcupine starring button
    add_link("Help/Free help chat",
             "http://webchat.freenode.net/?channels=%23%23learnpython")
    add_link("Help/My Python tutorial",
             "https://github.com/Akuli/python-tutorial/blob/master/README.md")
    add_link("Help/Official Python documentation", "https://docs.python.org/")
    menubar.get_menu("Help").add_separator()
    add_link("Help/Porcupine Wiki", "https://github.com/Akuli/porcupine/wiki")
    add_link("Help/Report a problem or request a feature",
             "https://github.com/Akuli/porcupine/issues/new")
    add_link("Help/Read Porcupine's code on GitHub",
             "https://github.com/Akuli/porcupine/tree/master/porcupine")

    # TODO: loading the styles takes a long time on startup... try to
    # make it asynchronous without writing too complicated code?
    config = settings.get_section('General')
    for name in sorted(pygments.styles.get_all_styles()):
        if name.islower():
            label = name.replace('-', ' ').replace('_', ' ').title()
        else:
            label = name

        options = {
            'label': label,
            'value': name,
            'variable': config.get_var('pygments_style'),
        }

        style = pygments.styles.get_style_by_name(name)
        bg = style.background_color

        # styles have a style_for_token() method, but only iterating
        # is documented :( http://pygments.org/docs/formatterdevelopment/
        # i'm using iter() to make sure that dict() really treats
        # the style as an iterable of pairs instead of some other
        # metaprogramming fanciness
        fg = None
        style_infos = dict(iter(style))
        for token in [pygments.token.String, pygments.token.Text]:
            if style_infos[token]['color'] is not None:
                fg = '#' + style_infos[token]['color']
                break
        if fg is None:
            # do like textwidget.ThemedText._set_style does
            fg = (getattr(style, 'default_style', '')
                  or utils.invert_color(bg))

        options['foreground'] = options['activebackground'] = fg
        options['background'] = options['activeforeground'] = bg

        menubar.get_menu("Color Themes").add_radiobutton(**options)
コード例 #13
0
 def on_destroy(event):
     settings.get_section('General').disconnect('pygments_style',
                                                self._set_style)