API reference
dodo.app
- class Dodo
Bases:
QApplication
The main Dodo application
There is always one instance of this class, and it contains methods for all of the global (i.e. not view-specific) commands. This includes running global opening/closing panels, opening the help window, and synchronizing mail with the IMAP server.
- add_panel(p, focus=True)
Add a panel to the tab view
This method is used by the
search
,thread
, andcompose
methods to open new panels. In general, this method shouldn’t be called directly from key mappings.- Return type:
None
- close_panel(index=None)
Close the panel at index (if provided) or the current panel
If index is not provided, close the current panel. This will only close panels whose keep_open property is False.
- Return type:
None
- next_panel()
Go to the next panel
- Return type:
None
- num_panels()
Returns the number of panels (i.e. tabs) currently open
- Return type:
int
- open_compose(mode='', msg=None)
Open a compose panel
If reply_to is provided, set populate the ‘To’ and ‘In-Reply-To’ headers appropriately, and quote the text in this message.
- Parameters:
msg (
Optional
[dict
]) – A JSON message referenced in a reply or forwardmode (
str
) – Composition mode. Possible values are ‘’, ‘reply’, ‘replyall’, and ‘forward’
- Return type:
None
- open_search(query, keep_open=False)
Open a search panel with the given query
If a panel with this query is already open, switch to it rather than opening another copy.
- Return type:
None
- open_tags(keep_open=False)
Open tag panel
- Return type:
None
- open_thread(thread_id)
Open a thread panel with the given thread_id
If a panel with this thread_id is already open, switch to it rather than opening another copy.
- Return type:
None
- previous_panel()
Go to the previous panel
- Return type:
None
- prompt_quit()
A ‘soft’ quit function, which gives each open tab the opportunity to prompt the user and possible cancel closing.
- Return type:
None
- refresh_panels()
Refresh current panel and mark the others as out of date
This method gets called whenever tags have been changed or a new message has been sent. The refresh will happen the next time a panel is switched to.
- Return type:
None
- search_bar()
Open command bar for searching
- Return type:
None
- show_help()
Show help window
- Return type:
None
- sync_mail(quiet=True)
Sync mail with IMAP server
This method runs
sync_mail_command
, then ‘notmuch new’- Parameters:
quiet (
bool
) – If this is True, do not give any visual cues that email is being synced. This is less distracting if this is a periodic sync, rather than a manual sync by the user.- Return type:
None
- tag_bar(mode='tag')
Open command bar for tagging
- Return type:
None
- class SyncMailThread(parent=None)
Bases:
QThread
A QThread used for syncing local Maildir and notmuch with IMAP
Called by the
sync_mail
method.- run()
Run
sync_mail_command
then notmuch new- Return type:
None
- main()
Main entry point for Dodo
- Return type:
None
dodo.commandbar
- class CommandBar(a, label, parent)
Bases:
QLineEdit
A command bar that appears on the bottom of the screen when searching or tagging.
- accept()
Apply the command typed into the command bar and close
After the command has been applied, this method saves the command to the command history associated with the current mode, then calls
close_bar
to clear the command and close the command bar.- Return type:
None
- close_bar()
Clear the command and close
Call this method by itself to cancel the command and close the bar. Note we use close_bar to avoid a name clash with QWidget.close.
- Return type:
None
- handleCompletion(text)
Use the choosen tag.
- handleTextChanged(text)
Open suggestion dialog if a matching tag is present.
- history_next()
Cycle to the next command in the command history
Note a separate history is kept for each mode.
- Return type:
None
- history_previous()
Cycle to the previous command in the command history
Note a separate history is kept for each mode.
- Return type:
None
- keyPressEvent(e)
Process keyboard input while the command bar is in focus.
Translate the key event into a string with
key_string
and check if it is incommand_bar_keymap
. If it is, fire the associated function. Otherwise, pass the event on to the text box.Note: Key chords are NOT supported in the command bar.
- Return type:
None
- open(mode, callback)
Open the command bar and give it focus
This method sets the command_area QWidget (which contains the command bar and its label) to be visible, and sets the command_label to be equal to mode.
- Parameters:
mode (
str
) – a string used to set the label next to the command bar and keep a unique command history (e.g. ‘search’ history should be different from ‘tag’ history).callback (
Callable
[[str
],Any
]) – a function called with the user input to run the command
- Return type:
None
dodo.compose
- class ComposePanel(a, mode='', msg=None, parent=None)
Bases:
Panel
A panel for composing messages
- Parameters:
mode (
str
) – Composition mode. Possible values are ‘’, ‘mailto’, ‘reply’, ‘replyall’, and ‘forward’msg (
Optional
[dict
]) – A JSON message referenced in a reply or forward. If mode != ‘’, this cannot be None.
- account_name()
Return the name of the current SMTP account
- Return type:
str
- attach_file()
Attach a file
Opens a file browser for selecting a file to attach. If a file is selected, add it using the “A:” pseudo-header. This will be translated into a proper attachment when the message is sent.
This command can also use the optional setting file_picker_command to run an external file picker instead of Qt’s built-in one.
- Return type:
None
- edit()
Edit the email message with an external text editor
The editor is configured via
dodo.settings.editor_command
.- Return type:
None
- email_address()
Return email address that should be used in From: header
- Return type:
str
- next_account()
Cycle to the next SMTP account in
smtp_accounts
- Return type:
None
- previous_account()
Cycle to the previous SMTP account in
smtp_accounts
- Return type:
None
- refresh()
Refresh the message text
This gets called automatically after the external editor has closed.
- Return type:
None
- send()
Send the message
Sends asynchronously using
SendmailThread
. If one or more occurances of the “A:” pseudo-header are detected, these are converted into attachments.- Return type:
None
- title()
The title shown on this panel’s tab
- Return type:
str
- toggle_wrap()
Toggle message wrapping
Tell Dodo to apply hard wrapping to message text for viewing and sending. This maintains an unwrapped copy of the text for editing.
- Return type:
None
dodo.helpwindow
dodo.keymap
- command_bar_keymap = {'<down>': ('history next', <function <lambda>>), '<enter>': ('accept', <function <lambda>>), '<escape>': ('close', <function <lambda>>), '<up>': ('history previous', <function <lambda>>), 'C-n': ('history next', <function <lambda>>), 'C-p': ('history previous', <function <lambda>>)}
The keymap active when the command bar is visible
A dictionary from key strings to pairs consisting of a short docstring and a function taking
CommandBar
as input. Unlike the other keymaps, the command bar keymap doesn’t accept keychords. Also, you should avoid mapping alphanumeric keys to commands, as this will interfere with typing.
- compose_keymap = {'<enter>': ('edit message', <function <lambda>>), 'S': ('send', <function <lambda>>), '[': ('previous SMTP account', <function <lambda>>), ']': ('next SMTP account', <function <lambda>>), 'a': ('attach file', <function <lambda>>), 's': ('toggle PGP-sign', <function <lambda>>), 'w': ('toggle word wrap', <function <lambda>>)}
The local keymap for compose panels
A dictionary from key strings to pairs consisting of a short docstring and a function taking
ComposePanel
as input.
- global_keymap = {'/': ('search', <function <lambda>>), '?': ('show help', <function <lambda>>), 'F': ('show flagged', <function <lambda>>), 'I': ('show inbox', <function <lambda>>), 'Q': ('quit', <function <lambda>>), 'T': ('show tags', <function <lambda>>), 'U': ('show unread', <function <lambda>>), 'X': ('close all', <function <lambda>>), '`': ('sync mail', <function <lambda>>), 'c': ('compose', <function <lambda>>), 'h': ('previous panel', <function <lambda>>), 'l': ('next panel', <function <lambda>>), 't m': ('tag marked', <function <lambda>>), 't t': ('tag', <function <lambda>>), 'x': ('close panel', <function <lambda>>)}
The global keymap
A dictionary from key strings to pairs consisting of a short docstring and a function taking
Dodo
as input. This commands can be superseded by the various local keymaps.
- search_keymap = {'<down>': ('next thread', <function <lambda>>), '<enter>': ('open thread', <function <lambda>>), '<space>': ('toggle marked', <function <lambda>>), '<tab>': ('next unread', <function <lambda>>), '<up>': ('previous thread', <function <lambda>>), 'C-d': ('down 20', <function <lambda>>), 'C-u': ('up 20', <function <lambda>>), 'G': ('last thread', <function <lambda>>), 'S-<tab>': ('previous unread', <function <lambda>>), 'a': ('tag -inbox -unread', <function <lambda>>), 'f': ('toggle flagged', <function <lambda>>), 'g g': ('first thread', <function <lambda>>), 'j': ('next thread', <function <lambda>>), 'k': ('previous thread', <function <lambda>>), 'u': ('toggle unread', <function <lambda>>)}
The local keymap for search panels
A dictionary from key strings to pairs consisting of a short docstring and a function taking
SearchPanel
as input.
- tag_keymap = {'<down>': ('next tag', <function <lambda>>), '<enter>': ('search tag', <function <lambda>>), '<up>': ('previous tag', <function <lambda>>), 'C-d': ('down 20', <function <lambda>>), 'C-u': ('up 20', <function <lambda>>), 'G': ('last tag', <function <lambda>>), 'g g': ('first tag', <function <lambda>>), 'j': ('next tag', <function <lambda>>), 'k': ('previous tag', <function <lambda>>)}
The local keymap for the tag panel
A dictionary from key strings to pairs consisting of a short docstring and a function taking
TagPanel
as input.
- thread_keymap = {'-': ('page up', <function <lambda>>), '<space>': ('page down', <function <lambda>>), 'A': ('show attachments in file browser', <function <lambda>>), 'C-d': ('scroll down more', <function <lambda>>), 'C-f': ('forward', <function <lambda>>), 'C-u': ('scroll up more', <function <lambda>>), 'G': ('bottom of message', <function <lambda>>), 'H': ('toggle HTML', <function <lambda>>), 'J': ('next message', <function <lambda>>), 'K': ('previous message', <function <lambda>>), 'R': ('reply', <function <lambda>>), 'f': ('toggle flagged', <function <lambda>>), 'g g': ('top of message', <function <lambda>>), 'j': ('scroll down', <function <lambda>>), 'k': ('scroll up', <function <lambda>>), 'r': ('reply to all', <function <lambda>>), 'u': ('toggle unread', <function <lambda>>)}
The local keymap for thread panels
A dictionary from key strings to pairs consisting of a short docstring and a function taking
ThreadPanel
as input.
dodo.panel
- class Panel(a, keep_open=False, parent=None)
Bases:
QWidget
A container widget that can handle key events and be shown on a tab
This is the base class for
SearchPanel
,ThreadPanel
, andComposePanel
, which are the main top-level containers used by Dodo.- Parameters:
keep_open (
bool
) – If this is True, keep the panel open even when instructed to close. This is used to make sure the “Inbox”SearchPanel
always stays open.
- before_close()
Called before closing a panel
Cleans up temp dirs and returns True. Overriding methods should call this method after checking they are ready to close.
- Return type:
bool
- Returns:
True if the panel is ready to close, or False to cancel
- keyPressEvent(e)
Passes key events to the appropriate keymap
First a key press (possibly with modifiers) is translated into a string representation using
key_string
. Then, if that string is part of a keychord, start a timer to try to gather more input.Once we have the full keychord (or a timeout has occurred), check if the string of the keychord is in the keymap set via
set_keymap
. If so, fire the associated function. Otherwise, checkglobal_keymap
and fire the associated function. If it is not in either, swallow the input and do nothing.- Return type:
None
- set_keymap(mp)
Set the local keymap to be the given dictionary.
This needs to be called in the
__init__
method of each child class to ensure it handles keychords correctly.- Return type:
None
- title()
The title shown on this panel’s tab
- Return type:
str
dodo.search
- class SearchModel(q)
Bases:
QAbstractItemModel
A model containing the results of a search
- columnCount(index=<PyQt6.QtCore.QModelIndex object>)
The number of columns
- Return type:
int
- data(index, role=ItemDataRole.DisplayRole)
Overrides QAbstractItemModel.data to populate a view with search results
- Return type:
Any
- headerData(section, orientation, role=ItemDataRole.DisplayRole)
Overrides QAbstractItemModel.headerData to populate a view with column names
- Return type:
Any
- index(row, column, parent=<PyQt6.QtCore.QModelIndex object>)
Construct a QModelIndex for the given row and column
- Return type:
QModelIndex
- num_threads()
The number of threads returned by the search
- Return type:
int
- parent(child=None)
Always return an invalid index, since there are no nested indices
- Return type:
Any
- refresh()
Refresh the model by (re-) running “notmuch search”.
- Return type:
None
- rowCount(index=<PyQt6.QtCore.QModelIndex object>)
The number of rows
This is essentially an alias for
num_threads
, but it also returns 0 if an index is given to tell Qt not to add any child items.- Return type:
int
- thread_id(index)
Return the notmuch thread id associated with the thread at the given model index
- Return type:
Optional
[str
]
- thread_json(index)
Return a JSON object associated with the thread at the given model index
- Return type:
Optional
[dict
]
- class SearchPanel(a, q, keep_open=False, parent=None)
Bases:
Panel
A panel showing the results of a search
This is used as the main entry point for the GUI, i.e. a search for “tag:inbox”.
- first_thread()
Select the first thread in the search
- Return type:
None
- last_thread()
Select the last thread in the search
- Return type:
None
- next_thread(unread=False)
Select the next thread in the search
- Parameters:
unread (
bool
) – if True, this will jump to the next unread thread- Return type:
None
- open_current_thread()
Open the selected thread
- Return type:
None
- previous_thread(unread=False)
Select the previous thread in the search
- Parameters:
unread (
bool
) – if True, this will jump to the previous unread thread- Return type:
None
- refresh()
Refresh the search listing and restore the selection, if possible.
- Return type:
None
- tag_thread(tag_expr, mode='tag')
Apply the given tag expression to the selected thread
A tag expression is a string consisting of one more statements of the form “+TAG” or “-TAG” to add or remove TAG, respectively, separated by whitespace.
- Return type:
None
- title()
Give the query as the tab title
- Return type:
str
- toggle_thread_tag(tag)
Toggle the given thread tag
- Return type:
None
dodo.settings
This module holds settings and sets their default values. The values set here should be overridden by the user in ~/.config/dodo/config.py. This can be done as follows:
import dodo
dodo.settings.email_address = 'First Last <me@domain.com>''
dodo.settings.sent_dir = '~/mail/work/Sent'
The settings email_address
and
sent_dir
are required. Dodo may not work correctly
unless you set them properly. The rest of the settings have reasonable
defaults, as detailed below.
- default_to_html = False
Open messages in HTML mode by default, rather than plaintext
- editor_command = "xterm -e vim '{file}'"
Command used to launch external text editor
This is a shell command, which additionally takes the {file} placeholder, which is passed the name of a temp file being edited while composing an email.
- email_address = ''
Your email address (REQUIRED)
This is used both to populate the ‘From’ field of emails and to (mostly) avoid CC’ing yourself when replying to all. It can be given as ‘NAME <ADDRESS@DOMAIN>’ format. For just one email address, this can be given as a string. From multiple emails, use a dictionary mapping the account names in
smtp_accounts
to the associated email addresses.
- file_browser_command = "nautilus '{dir}'"
Command used to launch external file browser
This is a shell command, which additionally takes the {dir} placeholder. This command is used when viewing attachments, which first dumps the attachments to a temp directory given by {dir}, then opens that directory in a file browser.
- file_picker_command = None
Command used to launch external file picker
This is an optional shell command, which additionally takes the {tempfile} placeholder. This command is used when picking files to attach to an email. The command should write out the chosen files to {tempfile}, which will then be read and deleted, if it exists.
By default, this is set to None, in which case the built-in file picker will be used.
- gnupg_home = None
Directory containg GnuPG keys
If set to None, GnuPG will use whatever directory is the default (consult the GnuPG documentation for more information on what this might be).
- gnupg_keyid = None
The id of the key to be used for GnuPG-signing mail messages.
If set to the id of a valid GnuPG private signing key, sent messages will be cryptographically signed according to rfc3156 using the GnuPG sotware, which should be installed and configured. Requires python-gnupg (https://pypi.org/project/python-gnupg/)
- hide_tags = ['unread', 'sent']
Tags to hide in search panel
- html_block_remote_requests = True
Block remote requests for HTML messages
HTML messages, especially from dodgy senders, can display remote content or ‘call home’ from embedded image tags or iframes. If set to True, Dodo will not allow these requests.
- html_confirm_open_links = True
Display a confirmation dialog before opening a link in browser
If this is True, Dodo will display a confirmation dialog showing the actual URL that the web browser will request before opening. This is an extra measure against phishing or emails opening your web browser without your permission.
- init_queries = ['tag:inbox']
List of non closable queries open at startup
You can save query with notmuch config set query:inbox “tag:inbox and not tag:trash” and use query:inbox as a search term.
- message_css = '\npre {{\n font-family: {message_font};\n font-size: {message_font_size}pt;\n}}\n\npre .quoted {{\n color: {fg_dim};\n}}\n\npre .headername {{\n color: {fg_bright};\n font-weight: bold;\n}}\n\npre .headertext {{\n color: {fg_bright};\n}}\n\nbody {{\n background-color: {bg};\n color: {fg};\n}}\n\n::-webkit-scrollbar {{\n background: {bg};\n}}\n\n::-webkit-scrollbar-thumb {{\n background: {bg_button};\n}}\n\n::selection {{\n color: {bg};\n background: {fg};\n}}\n\na {{\n color: {fg_bright};\n}}\n'
CSS used in view and compose window
Placeholders may be included in curly brackets for any color named in the current theme, as well as {message_font} and {message_font_size}. Literal curly braces should be doubled, i.e. ‘{’ should be ‘{{’ and ‘}’ should be ‘}}’.
- message_font = 'DejaVu Sans Mono'
The font used for plaintext messages
- message_font_size = 12
The font size used for plaintext messages
- remove_temp_dirs = 'ask'
Set whether to remove temporary directories when closing a panel
Thread panels create temporary directories to open attachments. These can be cleaned up automatically when a panel (or Dodo) is closed. Possible values are: ‘always’, ‘never’, or ‘ask’.
- search_font = 'DejaVu Sans Mono'
The font used for search output and various other list-boxes
- search_font_size = 13
The font size used for search output and various other list-boxes
- search_view_padding = 1
A bit of spacing around each line in the search panel
- send_mail_command = 'msmtp -a "{account}" -t'
Command used to send mail via SMTP
This is a shell command that expects a (sendmail-compatible) email message to be written to STDIN. Note that it should read the destination from the From: header of the message and not a command-line argument. Use the {account} placeholder to read the currently selected account.
- sent_dir = ''
Where to store sent messages (REQUIRED)
This will usually be a subdirectory of the Maildir sync’ed with
sync_mail_command
. This setting can be given either as a string to use one global sent directory, or as a dictionary mapping account names insmtp_accounts
to their own sent dirs.
- smtp_accounts = ['default']
A list of SMTP account names recognised by send_mail_command
This setting allows switching SMTP accounts in the Compose panel. The first account in the list is selected by default.
- sync_mail_command = 'offlineimap'
Command used to sync IMAP with local Maildir
- sync_mail_interval = 300
Interval to run
sync_mail_command
automatically, in secondsSet this to -1 to disable automatic syncing.
- tag_font = 'DejaVu Sans Mono'
The font used for tags and tag icons
- tag_font_size = 13
The font size used for tags and tag icons
- tag_icons = {'attachment': '\uf565', 'flagged': '\uf73a', 'inbox': '\uf01c', 'marked': '\uf111', 'replied': '\uf4a8', 'sent': '>', 'signed': '\uf040', 'unread': '\uf0e0'}
Tag icons
This is a dictionary of substitutions used to abbreviate common tag names as unicode icons in the search and thread panels.
- theme = {'bg': '#2e3440', 'bg_alt': '#3b4252', 'bg_button': '#4c566a', 'bg_highlight': '#a3be8c', 'fg': '#d8dee9', 'fg_bad': '#bf616a', 'fg_bright': '#b48ead', 'fg_button': '#eceff4', 'fg_date': '#4c566a', 'fg_dim': '#4c566a', 'fg_from': '#5e81ac', 'fg_good': '#a3be8c', 'fg_highlight': '#2e3440', 'fg_link': '#81a1c1', 'fg_subject': '#d8dee9', 'fg_subject_flagged': '#ebcb8b', 'fg_subject_unread': '#b48ead', 'fg_tags': '#81a1c1'}
The GUI theme
A theme is a dictionary mapping a dozen or so named colors to HEX values. Several themes are defined in dodo.themes, based on the popular Nord and Solarized color palettes.
- web_browser_command = ''
Web browser to use when clicking links in emails
This should be a single command which expects a URL as its first argument. If this is an empty string, Dodo will attempt to use the default web browser supplied by the desktop environment, if it exists.
- wrap_column = 78
Wrap text to this column when composing emails
- wrap_message = True
Hard-wrap message text by default
You may wish to disable this if you don’t want hard wraps in your email messages or your text editor does hard wrapping already.
dodo.themes
- apply_theme(theme)
“Apply the given theme to GUI components
This is called when
Dodo
is initialised.- Return type:
None
- catppuccin_macchiato = {'bg': '#24273a', 'bg_alt': '#181926', 'bg_button': '#363a4f', 'bg_highlight': '#8aadf4', 'fg': '#cad3f5', 'fg_bad': '#ed8796', 'fg_bright': '#b7bdf8', 'fg_button': '#f4dbd6', 'fg_date': '#f0c6c6', 'fg_dim': '#8087a2', 'fg_from': '#8aadf4', 'fg_good': '#a6da95', 'fg_highlight': '#181926', 'fg_link': '#8aadf4', 'fg_subject': '#cad3f5', 'fg_subject_flagged': '#eed49f', 'fg_subject_unread': '#c6a0f6', 'fg_tags': '#f5a97f'}
Theme based on the Catppuchin palette (macchiatto version).
- nord = {'bg': '#2e3440', 'bg_alt': '#3b4252', 'bg_button': '#4c566a', 'bg_highlight': '#a3be8c', 'fg': '#d8dee9', 'fg_bad': '#bf616a', 'fg_bright': '#b48ead', 'fg_button': '#eceff4', 'fg_date': '#4c566a', 'fg_dim': '#4c566a', 'fg_from': '#5e81ac', 'fg_good': '#a3be8c', 'fg_highlight': '#2e3440', 'fg_link': '#81a1c1', 'fg_subject': '#d8dee9', 'fg_subject_flagged': '#ebcb8b', 'fg_subject_unread': '#b48ead', 'fg_tags': '#81a1c1'}
Theme based on the Nord palette
- solarized_dark = {'bg': '#073642', 'bg_alt': '#002b36', 'bg_button': '#002b36', 'bg_highlight': '#eee8d5', 'fg': '#93a1a1', 'fg_bad': '#dc322f', 'fg_bright': '#6c71c4', 'fg_button': '#586e75', 'fg_date': '#2aa198', 'fg_dim': '#586e75', 'fg_from': '#268bd2', 'fg_good': '#859900', 'fg_highlight': '#586e75', 'fg_link': '#6c71c4', 'fg_subject': '#839496', 'fg_subject_flagged': '#6c71c4', 'fg_subject_unread': '#eee8d5', 'fg_tags': '#6c71c4'}
Theme based on the Solarized palette (dark background).
- solarized_light = {'bg': '#fdf6e3', 'bg_alt': '#fdf6e3', 'bg_button': '#fdf6e3', 'bg_highlight': '#073642', 'fg': '#586e75', 'fg_bad': '#dc322f', 'fg_bright': '#6c71c4', 'fg_button': '#93a1a1', 'fg_date': '#2aa198', 'fg_dim': '#93a1a1', 'fg_from': '#268bd2', 'fg_good': '#859900', 'fg_highlight': '#93a1a1', 'fg_link': '#6c71c4', 'fg_subject': '#839496', 'fg_subject_flagged': '#6c71c4', 'fg_subject_unread': '#073642', 'fg_tags': '#6c71c4'}
Theme based on the Solarized palette (light background).
dodo.thread
- class EmbeddedImageHandler(parent=None)
Bases:
QWebEngineUrlSchemeHandler
- requestStarted(request)
- Return type:
None
- class MessageHandler(parent=None)
Bases:
QWebEngineUrlSchemeHandler
- requestStarted(request)
- Return type:
None
- class MessagePage(a, profile, parent=None)
Bases:
QWebEnginePage
- Return type:
bool
- class ThreadModel(thread_id)
Bases:
QAbstractItemModel
A model containing a thread, its messages, and some metadata
This extends QAbstractItemModel to enable a tree view to give a summary of the messages, but also contains more data that the tree view doesn’t care about (e.g. message bodies). Since this comes from calling “notmuch show –format=json”, it contains information about attachments (e.g. filename), but not attachments themselves.
- Parameters:
thread_id (
str
) – the unique thread identifier used by notmuch
- columnCount(index=<PyQt6.QtCore.QModelIndex object>)
Constant = 1
- Return type:
int
- data(index, role=ItemDataRole.DisplayRole)
Overrides QAbstractItemModel.data to populate a list view with short descriptions of messages in the thread.
Currently, this just returns the message sender and makes it bold if the message is unread. Adding an emoji to show attachments would be good.
- Return type:
Any
- default_message()
Return the index of either the oldest unread message or the last message in the thread.
- Return type:
int
- index(row, column, parent=<PyQt6.QtCore.QModelIndex object>)
Construct a QModelIndex for the given row and (irrelevant) column
- Return type:
QModelIndex
- message_at(i)
A JSON object describing the i-th message in the (flattened) thread
- Return type:
dict
- num_messages()
The number of messages in the thread
- Return type:
int
- parent(child=None)
Always return an invalid index, since there are no nested indices
- Return type:
Any
- refresh()
Refresh the model by calling “notmuch show”.
- Return type:
None
- rowCount(index=<PyQt6.QtCore.QModelIndex object>)
The number of rows
This is essentially an alias for
num_messages
, but it also returns 0 if an index is given to tell Qt not to add any child items.- Return type:
int
- class ThreadPanel(a, thread_id, parent=None)
Bases:
Panel
A panel showing an email thread
This is the panel used for email viewing.
- Parameters:
app – the unique instance of the
Dodo
app classthread_id (
str
) – the unique ID notmuch uses to identify this thread
- forward()
Open a
ComposePanel
populated with a forwarded message- Return type:
None
- layout_panel()
Method for laying out various components in the ThreadPanel
- next_message()
Show the next message in the thread
- Return type:
None
- open_attachments()
Write attachments out into temp directory and open with settings.file_browser_command
Currently, this exports a new copy of the attachments every time it is called. Maybe it should do something smarter?
- Return type:
None
- previous_message()
Show the previous message in the thread
- Return type:
None
- refresh()
Refresh the panel using the output of “notmuch show”
Note the view of the message body is not refreshed, as this would pop the user back to the top of the message every time it happens. To refresh the current message body, use
show_message
wihtout any arguments.- Return type:
None
- reply(to_all=True)
Open a
ComposePanel
populated with a replyThis uses the current message as the message to reply to. This should probably do something smarter if the current message is from the user (e.g. reply to the previous one instead).
- Parameters:
to_all (
bool
) – if True, do a reply to all instead (see ~dodo.compose.ComposePanel)- Return type:
None
- scroll_message(lines=None, pages=None, pos=None)
Scroll the message body
This operates in 3 different modes, depending on which arguments are given. Precisely one of the three arguments lines, pages, and pos should be provided.
- Parameters:
lines (
Optional
[int
]) – scroll up/down the given number of 20-pixel increments. Negative numbers scroll up.pages (
Union
[float
,int
,None
]) – scroll up/down the given number of pages. Negative numbers scroll up.pos (
Optional
[str
]) – scroll to the given position (possible values are ‘top’ and ‘bottom’)
- Return type:
None
- show_message(i=-1)
Show a message
If an index is provided, switch the current message to that index, otherwise refresh the view of the current message.
- Return type:
None
- tag_message(tag_expr)
Apply the given tag expression to the current message
A tag expression is a string consisting of one more statements of the form “+TAG” or “-TAG” to add or remove TAG, respectively, separated by whitespace.
- Return type:
None
- title()
The tab title
The title is given as the (shortened) subject of the currently visible message.
- Return type:
str
- toggle_html()
Toggle between HTML and plain text message view
- Return type:
None
- toggle_message_tag(tag)
Toggle the given tag on the current message
- Return type:
None
- flat_thread(d)
Return the thread as a flattened list of messages, sorted by date.
- Return type:
List
[dict
]
- short_string(m)
Return a short string describing the provided message
Currently, this just returns the contents of the “From” header, but something like a first name and short/relative date might be more useful.
- Parameters:
m (
dict
) – A JSON message object- Return type:
str
dodo.util
- add_header_line(s, h)
Add the given string to the headers, i.e. before the first blank line, in the provided string.
- Return type:
str
- body_html(m)
Get the body HTML of a message
Search a message recursively for the first part with content-type equal to “text/html” and return it.
- Parameters:
m (
dict
) – a JSON message- Return type:
str
- body_text(m)
Get the body text of a message
Search a message recursively for the first part with content-type equal to “text/plain” and return it.
- Parameters:
m (
dict
) – a JSON message- Return type:
str
- clean_html2html(s)
Sanitize the given HTML string
This cleans the input string using
Cleaner
with the default settings. Set the global util.html2html to this function to enable.- Parameters:
s (
str
) – an HTML input string- Return type:
str
- colorize_text(s, has_headers=False)
Add some colors to HTML-escaped plaintext, for use inside <pre> tag
- Return type:
str
- decode_header(s)
Decode any charset-encoded parts of an email header
- Return type:
str
- email_is_me(e)
Check whether the provided email is me
This compares settings.email_address with the provided email, after calling
strip_email_address
on both. This method is used e.g. bydodo.compose.Compose
to filter out the user’s own email when forming a “reply-to-all” message.- Return type:
bool
- email_smtp_account_index(e)
Index in settings.smtp_accounts of account having the provided email address
This method is used e.g. by
dodo.compose.Compose
to autmatically select the account to be used when replying to a mail. It returns the index of first matching account or None if provided email does not match any smtp account.- Return type:
Optional
[int
]
- find_content(m, content_type)
Return a flat list consisting of the ‘content’ field of each message part with the given content-type.
- Return type:
List
[str
]
- html2html(s)
Function used to process HTML messages
This is the identity by default, but can be set to another function to do HTML sanitization, (de)formatting, etc.
- html2text(s)
Function used to convert HTML to plain text
This is set to
w3m_html2text
by default, but can be changed by the user in “config.py”.- Return type:
str
- key_string(e)
Convert a Qt keycode plus modifiers into a human readable/writable string
- Parameters:
e (
QKeyEvent
) – a QKeyEvent- Return type:
str
- Returns:
a string representing e.key() and its modifiers
- linkify(s)
Link URLs and email addresses
- Parameters:
s (
str
) – a plaintext input string- Return type:
str
- Returns:
HTML with URLs and emails linked
- make_message_css()
Fill placeholders in settings.message_css using the current theme and font settings.
- Return type:
str
- message_parts(m)
Iterate over JSON message parts recursively, in depth-first order
This is method roughly emulates the behavior of
walk
, but for JSON representations of an email message rather thanMessage
objects.Note that if parts are nested, their data will be returned multiple times, first as a sub-object of their parent, then as the part itself.
- Parameters:
m (
dict
) – a JSON message- Return type:
Iterator
[dict
]- Returns:
an iterator which returns each JSON-subobject corresponding to a message part.
- quote_body_text(m)
Return the body text of the message, with ‘>’ prepended to each line
- Return type:
str
- replace_header(s, h, new_value)
Replace a single header without doing full message parsing
Note this ONLY works for short (i.e. unwrapped) headers.
- Return type:
str
- separate_headers(s)
Split a message into its header part and body part
- Return type:
Tuple
[str
,str
]
- simple_escape(s)
Provide (limited) HTML escaping
This function only escapes &, <, and >.
- Return type:
str
- strip_email_address(e)
Strip the display name, leaving just the email address
E.g. “First Last <me@domain.com>” -> “me@domain.com”
- Return type:
str
- w3m_html2text(s)
Convert HTML to plain text using “w3m -dump”
- Parameters:
s (
str
) – an HTML input string- Return type:
str
- Returns:
plain text representation of the HTML
- wrap_message(s)
Hard wrap message body using
wrap_column
Wrap the body part of the message. Headers and quoted text are not affected.
- Return type:
str
- write_attachments(m)
Write attachments out into temp directory and open with settings.file_browser_command
Currently, this exports a new copy of the attachments every time it is called. Maybe it should do something smarter?
- Parameters:
m (
dict
) – message JSON- Return type:
Tuple
[str
,List
[str
]]- Returns:
Return a tuple consisting of the temp dir and a list of files. If no attachments, returns an empty string and empty list.