:mod:`porcupine.settings` --- Porcupine's setting manager
- NOTE:
- This documentation was originally meant to be converted to HTML with Sphinx, so it doesn't show up perfectly on GitHub. Please create an issue if you have trouble figuring out how the settings work. I'm planning to rewrite some of the setting stuff and then document it properly, see issue #1481.
This module manages Porcupine's settings and the Porcupine Settings dialog in the Edit menu.
.. data:: global_settings You often need only this variable from the ``porcupine.settings`` module. It's a :class:`Settings` object that contains all settings that are the same everywhere in Porcupine. For example, it doesn't contain settings like "indentation is 4 spaces", which can be different in each tab.
Each instance of :class:`Settings` is basically a dict of option names and values, but it also ensures that the values have the correct type, converts them to the correct type when loaded from a setting file, and so on.
There are two kinds of instances of this class that you will likely come across:
- The :data:`global_settings` object.
- Each :class:`~porcupine.tabs.FileTab` has its own :class:`Settings` object, known as :attr:`porcupine.tabs.FileTab.settings`.
Porcupine has these options in global_settings
by default (but you can add your own options too, see below):
font_family
: :class:`str`
font_size
: :class:`int`The font family and size used in Porcupine's main text widget.
Porcupine also keeps these settings in a special font named
TkFixedFont
, so if you want to use this font in a widget just pass it afont='TkFixedFont'
option.Text
widgets useTkFixedFont
by default.Do this if you need a
Font
object instead of the font name as a string:import tkinter.font fixedfont = tkinter.font.Font(name='TkFixedFont', exists=True)pygments_style
: :class:`str`Name of the current Pygments style used for syntax highlighting. Use this option like this:
from porcupine import settings from pygments import styles the_style = styles.get_style_by_name(settings.get('pygments_style', str))
disabled_plugins
: list[:class:`str`]Names of plugins that the user has disabled. See :mod:`porcupine.pluginloader`.
default_line_end
: :class:`LineEnding`The default line ending for new files.
.. autoclass:: LineEnding
Use these methods on global_settings
(or other :class:`Settings` objects) for setting and getting values:
.. automethod:: Settings.set
.. method:: Settings.get(option_name, type) Return the current value of an option. The *type* should be e.g. ``str`` or ``int`` depending on what type the option is. You can also specify ``object`` to allow any type. I have tried to tell mypy that this method returns a value of the given *type*. Mypy understands that in many cases, but not always:: foo = settings.get('foo', str) reveal_type(foo) # str from pathlib import Path shitty_bar = settings.get('bar', Optional[Path]) reveal_type(shitty_bar) # Any Use a type annotation to work around this (and make sure to write the same type two times):: good_bar: Path | None = settings.get('bar', Optional[Path]) reveal_type(good_bar) # Optional[Path] Before Python 3.10, you can't use the new ``|`` syntax as an argument to ``settings.get()``, even though it otherwise works with ``from __future__ import annotations``. The same goes for built-in generics, such as ``list[str]`` with lower-case ``list``. Options of mutable types are returned as copies, so things like ``settings.get('something', List[str])`` always return a new list. If you want to change a setting like that, you need to first get a copy of the current value, then modify the copy, and finally :meth:`set` it back. This is an easy way to make sure that change events run every time the value changes.
.. automethod:: Settings.debug_dump
.. automethod:: Settings.get_options_by_tag
Use :data:`global_settings` to store things that shouldn't reset every time Porcupine is restarted. For example:
import logging from porcupine.settings import global_settings log = logging.getLogger(__name__) def connect_to_http_server() -> None: host = global_settings.get('http_server_host', str) port = global_settings.get('http_server_port', int) log.info(f"Connecting to {host}:{port}...") ... def setup() -> None: # 80 and '127.0.0.1' are default values global_settings.add_option('http_server_host', '127.0.0.1') global_settings.add_option('http_server_port', 80)
.. automethod:: Settings.add_option
As the name suggests, the global settings are shared by all plugins,
so don't make the option names too short.
For example, if two plugins define an option named port
,
then one of them will get an error from :func:`add_option` when the plugins get set up.
If the plugins had instead named their options e.g. langserver_port
and http_server_port
,
then there would be no problem.
Note
Porcupine stores the global settings in a JSON file, but that file isn't meant to be edited directly by users. Instead, please provide some way to change the settings with a GUI, such as a handle that can be dragged to change the size settings of a widget, or add something to the setting dialog (see below).
Click Porcupine Settings in the Edit menu to open the dialog. It allows the user to edit :data:`global_settings`.
.. autofunction:: get_dialog_content
You can add widgets to the content frame, but it's usually easiest to use these functions. All of these return the added widget. A label that displays text is added to column 0 (see the ascii art above). When setting the values, the converter passed to :func:`add_option` is used.
Note
The option_name argument of the functions below must be an existing option on :data:`global_settings`. You can create it just before calling the functions below:
from porcupine import settings from porcupine.settings import global_settings def validate_host(host: str) -> bool: ... ... global_settings.add_option('http_server_host', '127.0.0.1') settings.add_entry('http_server_host', "HTTP server host:", validate_host)
.. autofunction:: add_entry
.. autofunction:: add_checkbutton
.. autofunction:: add_combobox
.. autofunction:: add_spinbox
.. autofunction:: add_label
.. virtualevent:: GlobalSettingChanged:foo When any global setting is changed, all widgets in the main window (see :func:`porcupine.get_main_window`) receive a virtual event named ``<<GlobalSettingChanged:option_name>>``, where ``option_name`` is the name of the changed setting. For example, ``<<GlobalSettingChanged:pygments_style>>`` runs when the user changes the Pygments style.
All widget bindings go away automatically when the corresponding widget is destroyed.
For example, if you want to update the configuration of a :class:`tkinter.Text` widget when pygments_style
changes,
you should use the .bind()
method of that text widget.
This way your callback won't be called when the text widget doesn't exist anymore.
If you want your binding to work as long as Porcupine is running, use the bind method of :func:`porcupine.get_tab_manager`.
Note
Do not use :func:`porcupine.get_main_window` for binding to <<GlobalSettingChanged:foo>>
.
The main window is a :class:`tkinter.Tk` widget,
so it gets notified of its child widgets' events too.
(The same applies to :class:`tkinter.Toplevel`.)
For example, let's say that there are 100 widgets currently being shown in the main window,
and you unwisely use the main window's bind()
to bind <<GlobalSettingChanged:foo>>
.
When the foo
setting changes, your bind callback runs
100 times with event.widget
set to something else than the main window, and
once with event.widget
set to the main window.
This can cause annoying slowness.
Other :class:`Settings` objects use a different name for the change events,
and unlike :class:`global_settings`, don't emit the change events on all widgets.
For example, the change event of tab-specific settings is named
:virtevt:`~porcupine.tabs.FileTab.TabSettingChanged:foo`,
where foo
is the name of the setting.
.. autofunction:: use_pygments_fg_and_bg
These functions work with global settings only. They are not available for other :class:`Settings` instances.
.. autofunction:: show_dialog
.. autofunction:: save