API Reference

Package

Entry point

argparse_qt

argparse helpers for passing Qt command-line options.

pbpicat.argparse_qt.add_qt_arguments(parser)[source]

Add Qt command-line options to parser, collected in args.qt_args.

Each --option [value] is stored as ["-option"] or ["-option", "value"] and appended to args.qt_args, ready to be passed directly to QApplication.

Qt options are hidden from the usage: line to reduce noise; they still appear in the “Qt options” section of the full --help output.

Parameters:

parser (ArgumentParser)

Return type:

None

Internationalisation

Internationalisation bootstrap for PBPicat.

Call setup(app) once before creating any window.

pbpicat.i18n.available_languages()[source]

Return [(lang_code, lang_name_in_that_language), …] sorted by code.

Return type:

list[tuple[str, str]]

pbpicat.i18n.current_language()[source]

Return the language code currently in effect (same resolution as setup()).

Return type:

str

pbpicat.i18n.setup(app)[source]

Install translations for app. Safe to call multiple times.

Parameters:

app (PySide6.QtWidgets.QApplication)

Return type:

None

config

pbpicat.config.set_base_dir(path)[source]

Override the base config directory. Must be called before init_catalogs().

Parameters:

path (Path)

Return type:

None

pbpicat.config.current_catalog()[source]
Return type:

str

pbpicat.config.load_current_catalog_name()[source]

Read catalog.conf; validate that the named directory exists; fall back to first available.

Return type:

str

pbpicat.config.set_current_catalog(name)[source]

Switch the in-memory current catalog. Hidden catalogs are not persisted to catalog.conf, so the next startup falls back to the last non-hidden catalog.

Parameters:

name (str)

Return type:

None

pbpicat.config.list_catalogs(*, include_hidden=False)[source]

List catalog names. Names starting with “.” or “-” are hidden by default.

Parameters:

include_hidden (bool)

Return type:

list[str]

pbpicat.config.create_catalog(name, initial_config=None)[source]
Parameters:
  • name (str)

  • initial_config (dict | None)

Return type:

None

pbpicat.config.delete_catalog(name)[source]
Parameters:

name (str)

Return type:

None

pbpicat.config.duplicate_catalog(source_name, dest_name)[source]

Copy all files from source_name catalog into a new dest_name catalog.

Parameters:
  • source_name (str)

  • dest_name (str)

Return type:

None

pbpicat.config.init_catalogs()[source]

Ensure at least one catalog exists, migrate legacy files if needed, load active catalog.

Return type:

None

pbpicat.config.qsettings()[source]

Return a QSettings instance pointing to the active catalog’s ui.conf.

Return type:

PySide6.QtCore.QSettings

pbpicat.config.app_qsettings()[source]

Return a QSettings instance for application-level state (window geometry, etc.).

Stored in <config_dir>/app.conf, independent of the active catalog.

Return type:

PySide6.QtCore.QSettings

pbpicat.config.load_config()[source]
Return type:

dict

pbpicat.config.save_config(config)[source]
Parameters:

config (dict)

Return type:

None

pbpicat.config.load_global_config()[source]
Return type:

dict

pbpicat.config.save_global_config(config)[source]
Parameters:

config (dict)

Return type:

None

pbpicat.config.load_history(key)[source]
Parameters:

key (str)

Return type:

list[str]

pbpicat.config.save_history(key, values)[source]
Parameters:
Return type:

None

pbpicat.config.load_all_history()[source]
Return type:

dict[str, list[str]]

pbpicat.config.save_all_history(data)[source]
Parameters:

data (dict[str, list[str]])

Return type:

None

pbpicat.config.load_last_dest()[source]
Return type:

str

pbpicat.config.save_last_dest(path)[source]
Parameters:

path (str)

Return type:

None

pbpicat.config.load_last_source_dir()[source]

Return the last selected source directory, falling back to $HOME if missing.

Return type:

str

pbpicat.config.save_last_source_dir(path)[source]
Parameters:

path (str)

Return type:

None

pbpicat.config.load_video_marker_pos()[source]
Return type:

int

pbpicat.config.save_video_marker_pos(pos)[source]
Parameters:

pos (int)

Return type:

None

renamer

pbpicat.renamer.validate_schema(fields)[source]

Returns (dirs, parts, numeric_spec) or raises ValueError.

dirs — consecutive non-empty non-numeric fields from start (directory path) parts — all non-empty non-numeric fields (joined with ‘_’ for filename) numeric_spec — the ‘#…’ field if present, else None

Parameters:

fields (list[str])

Return type:

tuple[list[str], list[str], str | None]

pbpicat.renamer.find_max_number(dest_dir, basename, extensions=None)[source]

Return the maximum numeric suffix used in dest_dir for filenames containing basename_NNN.

If extensions is given, only files whose suffix (lowercased) is in that set are considered.

Parameters:
Return type:

int

pbpicat.renamer.build_rename_plan(dest_root, schema_fields, source_paths, sidecar_extensions, image_extensions, video_extensions=None, video_marker='', video_marker_pos=0)[source]

Build (src, dst) pairs for source_paths and their sidecars. Raises ValueError for schema errors, FileExistsError if a destination already exists.

For video files, video_marker is inserted at video_marker_pos within the schema parts.

Parameters:
Return type:

list[tuple[Path, Path]]

pbpicat.renamer.build_renumber_plan(schema_fields, source_paths, sidecar_extensions, image_extensions, video_extensions=None, video_marker='', video_marker_pos=0)[source]

Build (src, dst) pairs for in-place renumbering starting from 1. Images and videos have separate counters. Only includes pairs where src != dst. Raises ValueError if the schema has no numeric field.

Parameters:
Return type:

list[tuple[Path, Path]]

pbpicat.renamer.execute_renumber(pairs)[source]

Execute in-place renaming via a two-phase temp rename to avoid conflicts. Rolls back on error.

Parameters:

pairs (list[tuple[Path, Path]])

Return type:

None

pbpicat.renamer.undo_renumber(pairs)[source]

Reverse a renumber plan: new_dst → original_src via two-phase rename.

Parameters:

pairs (list[tuple[Path, Path]])

Return type:

None

pbpicat.renamer.undo_rename(pairs)[source]

Reverse a rename plan: move each dst back to its original src. Raises FileNotFoundError if a dst no longer exists, FileExistsError if a src already exists. Rolls back on error. Removes empty destination directories afterwards.

Parameters:

pairs (list[tuple[Path, Path]])

Return type:

None

pbpicat.renamer.execute_rename(pairs)[source]

Execute rename pairs atomically: pre-check all destinations, then move. Rolls back on error. Removes empty source directories afterwards.

Parameters:

pairs (list[tuple[Path, Path]])

Return type:

None

image_io

Image loading helpers with Pillow fallback for formats Qt cannot handle.

pbpicat.image_io.load_qimage(path, max_w=0, max_h=0, auto_rotate=True)[source]

Load an image as QImage, with optional scaling. Thread-safe.

Parameters:
Return type:

PySide6.QtGui.QImage

pbpicat.image_io.load_pixmap(path, auto_rotate=True)[source]

Load an image as QPixmap. Must be called from the main thread.

Parameters:
Return type:

PySide6.QtGui.QPixmap

image_ops

Lossless image rotation. jpegtran (libjpeg-turbo) required for JPEG files.

pbpicat.image_ops.is_jpeg(path)[source]
Parameters:

path (Path)

Return type:

bool

pbpicat.image_ops.repair_jpeg_sos(data)[source]

Fix a malformed SOS header found in some camera firmware (e.g. certain Samsung front-camera modules): baseline (non-progressive) JPEGs must have Se=63 in the SOS segment, but these encoders write Se=0. Strict decoders (jpegtran/libjpeg-turbo) reject this with “Invalid SOS parameters for sequential JPEG”; lenient decoders (Qt, Pillow) just warn and decode anyway. Patching the header byte is lossless: no entropy-coded data is touched. Returns the input unchanged if no such defect is found.

Parameters:

data (bytes)

Return type:

bytes

pbpicat.image_ops.get_exif_orientation(path)[source]

Return EXIF Orientation value 2–8 if present and non-trivial, else None.

Parameters:

path (Path)

Return type:

int | None

pbpicat.image_ops.rotate_lossless(path, op)[source]

Apply a lossless rotation/flip to path in-place.

op: 90, -90, 180 (degrees CW)

“auto” (apply + clear EXIF orientation tag) “hflip” / “vflip” / “transpose” / “transverse” (for undo of flip-type EXIF)

Returns the inverse op for undo.

Raises RuntimeError (user-friendly) if jpegtran is unavailable for a JPEG file. Raises ValueError if op == “auto” and no EXIF orientation found.

Parameters:
Return type:

int | str

pbpicat.image_ops.set_exif_orientation(path, value)[source]

Set EXIF Orientation tag to value in a JPEG file in-place (requires piexif + Pillow).

Parameters:
Return type:

None

platform

Platform-specific helpers. Loaded once at import time.

pbpicat.platform.config_dir()[source]
Return type:

Path

pbpicat.platform.open_default(path)[source]
Parameters:

path (Path)

Return type:

None

pbpicat.platform.open_with(path, parent=None)[source]
Parameters:

path (Path)

Return type:

None

ui

icons

pbpicat.ui.icons.get_icon(svg_name, theme_name='', text_fallback='')[source]

Return an icon: system theme → SVG file → text fallback (or null QIcon).

Parameters:
  • svg_name (str)

  • theme_name (str)

  • text_fallback (str)

Return type:

PySide6.QtGui.QIcon

dir_tree

class pbpicat.ui.dir_tree.DirTree(*args, **kwargs)[source]
directory_selected

alias of str

set_file_list(file_list)[source]
Return type:

None

current_path()[source]
Return type:

str

select_path(path)[source]
Parameters:

path (str)

Return type:

None

showEvent(event)[source]
Return type:

None

keyPressEvent(event)[source]
Return type:

None

file_list_widget

class pbpicat.ui.file_list_widget.FileListWidget(*args, **kwargs)[source]

Table displaying image and video files with thumbnail, name and sidecar indicator.

Columns: THUMB | NAME | SIDECAR

Signals:

rename_requested(list[Path])

Parameters:

config (dict)

rename_requested

alias of list

schema_proposed

alias of list

file_count_changed

alias of int

orphan_sidecar_count_changed

alias of int

resizeEvent(event)[source]
Return type:

None

set_dir_tree(dir_tree)[source]
Return type:

None

set_schema_getter(getter)[source]
Parameters:

getter (Callable[[], list[str]])

Return type:

None

set_video_marker_pos_getter(getter)[source]
Parameters:

getter (Callable[[], int])

Return type:

None

set_context_actions(open_act, open_with_act, template_act, delete_act)[source]
Return type:

None

set_rotation_actions(rotate_ccw, rotate_cw, rotate_180, rotate_auto, reset_exif)[source]
Return type:

None

set_rotate_callback(callback)[source]

callback(paths: list[Path], op) — called when the image viewer requests rotation.

Return type:

None

load_directory(dir_path)[source]
Parameters:

dir_path (str)

Return type:

None

refresh()[source]
Return type:

None

reconfigure(config)[source]

Apply new config (thumbnail size, extensions) and refresh.

Parameters:

config (dict)

Return type:

None

set_sidecar_filter(pattern)[source]
Parameters:

pattern (str)

Return type:

None

set_sort_by_date(value)[source]
Parameters:

value (bool)

Return type:

None

set_sort_reverse(value)[source]
Parameters:

value (bool)

Return type:

None

get_selected_files()[source]
Return type:

list[Path]

get_all_files()[source]
Return type:

list[Path]

next_row_after_files(paths)[source]

Return the row to select after paths are removed from the list.

Counts non-removed files up to and including the last removed file’s position — that is the index of the file that follows in the new list.

Parameters:

paths (list[Path])

Return type:

int

refresh_and_select(row)[source]

Refresh the table then select the given row (clamped to valid range).

Parameters:

row (int)

Return type:

None

refresh_and_select_paths(paths)[source]

Refresh the table then select all rows whose path is in paths.

Parameters:

paths (set[Path])

Return type:

None

refresh_thumbnails_for_paths(paths)[source]

Reload thumbnails only for the given paths without rebuilding the table.

Parameters:

paths (set[Path])

Return type:

None

get_orphan_sidecars()[source]
Return type:

list[Path]

viewportEvent(event)[source]
Parameters:

event (PySide6.QtCore.QEvent)

Return type:

bool

focusInEvent(event)[source]
Return type:

None

keyPressEvent(event)[source]
Return type:

None

contextMenuEvent(event)[source]
Return type:

None

mouseDoubleClickEvent(event)[source]
Return type:

None

startDrag(supported_actions)[source]
Parameters:

supported_actions (PySide6.QtCore.Qt.DropActions)

Return type:

None

open_selected()[source]
Return type:

None

open_with_selected()[source]
Return type:

None

template_selected()[source]
Return type:

None

delete_selected()[source]
Return type:

None

closeEvent(event)[source]
Return type:

None

file_panel

class pbpicat.ui.file_panel.FilePanel(*args, **kwargs)[source]
Parameters:

config (dict)

refresh()[source]
Return type:

None

reconfigure(config)[source]
Parameters:

config (dict)

Return type:

None

schema_frame

class pbpicat.ui.schema_frame.SchemaFrame(*args, **kwargs)[source]
Parameters:

config (dict)

rebuild(config)[source]
Parameters:

config (dict)

Return type:

None

get_fields()[source]
Return type:

list[str]

get_video_marker_pos()[source]
Return type:

int

push_history(fields)[source]
Parameters:

fields (list[str])

Return type:

None

set_fields(values)[source]
Parameters:

values (list[str])

Return type:

None

image_viewer

class pbpicat.ui.image_viewer.ImageViewer(*args, **kwargs)[source]

Non-modal independent image viewer with zoom toolbar.

Zoom modes (toolbar, left to right):

1:1 | Zoom in | Zoom out | Width | Height | Window (default)

Parameters:
  • image_path (Path)

  • zoom_step_percent (int)

  • zoom_max_percent (int)

  • auto_rotate (bool)

rotate_requested

alias of object

load_image(path)[source]
Parameters:

path (Path)

Return type:

None

set_auto_rotate(value)[source]
Parameters:

value (bool)

Return type:

None

show_message(text)[source]
Parameters:

text (str)

Return type:

None

eventFilter(obj, event)[source]
Return type:

bool

closeEvent(event)[source]
Return type:

None

resizeEvent(event)[source]
Return type:

None

showEvent(event)[source]
Return type:

None

history_dialog

class pbpicat.ui.history_dialog.HistoryDialog(*args, **kwargs)[source]

Edit the saved history for every schema field. Each field gets a tab with a reorderable, deletable list.

Parameters:

config (dict)

done(result)[source]
Return type:

None

settings_dialog

class pbpicat.ui.settings_dialog.SettingsDialog(*args, **kwargs)[source]
Parameters:

config (dict)

done(result)[source]
Return type:

None

updated_config()[source]
Return type:

dict

class pbpicat.ui.settings_dialog.GlobalSettingsDialog(*args, **kwargs)[source]

Program-level settings (not per-catalog).

done(result)[source]
Return type:

None

updated_global_config()[source]
Return type:

dict

main_window

class pbpicat.ui.main_window.MainWindow(*args, **kwargs)[source]
closeEvent(event)[source]
Return type:

None