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)
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
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
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()
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)
from rbtk import get_main_window, settings config = settings.get_section('General') config.add_option('default_geometry', '650x500', reset=False) def save_geometry(event): config['default_geometry'] = event.widget.geometry() def setup(): get_main_window().geometry(config['default_geometry']) get_main_window().bind('<<RBTKQuit>>', save_geometry, add=True)
def on_destroy(event): settings.get_section('General').disconnect('pygments_style', self._set_style)
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, rbtk.get_main_window()), "Edit/RBTK Settings...") # 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 ## FIX LINKS 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