Skip to content

File Formats and Import Export

Hiromichi Yokoyama edited this page Aug 4, 2026 · 3 revisions

File Formats and Import/Export

This page lists every file format MoleditPy can open, import, save, or export, grounded in ui/io_logic.py (IOManager), ui/export_logic.py (ExportManager), and ui/string_importers.py (StringImporterManager). "2D" means the format populates the 2D editor canvas; "3D" means it populates/reads the 3D viewer only.


1. Native project format

.pmeprj — PME Project (JSON)

The recommended, default save format (File ▸ Save Project / Ctrl+S, Save Project As... / Ctrl+Shift+S).

  • Written by IOManager.save_project_as / save_as_json as UTF-8 JSON (json.dump(..., indent=2, ensure_ascii=False)), with an internal "format": "PME Project" marker and "version" field.
  • create_json_data() (StateManager) serializes the full editing session — the exact contents depend on StateManager, but per the app's own README/manual this includes: the 2D drawing (atoms, bonds, positions, charges, radicals, stereo), the generated/loaded 3D structure, chiral-label state, and — because plugins can hook register_save_handler/register_load_handlerany plugin's own persisted state (e.g. PMEFF's geometry overrides, the Metadata Saver plugin's debug fields) gets written into the same file.
  • On load (Open Project... / Ctrl+O, or File ▸ Open Project...), load_json_data() checks the "format" marker (rejects anything else with "This file is not a valid PME Project format.") and warns — but still attempts to load — if "version" isn't "1.0".
  • Corrupt/foreign JSON, a missing file, or a data-shape mismatch each surface a specific dialog (Invalid Format / Project Load Error with the underlying json.JSONDecodeError/KeyError/etc. message) rather than a generic failure.
  • After loading, the 3D camera is reset to isometric and the view is re-fit on a short QTimer delay.

.pmeraw — PME Raw (legacy, Python pickle)

  • Written by Export ▸ PME Raw Format... (save_raw_data) via pickle.dump(get_current_state(), f); loaded via Open Project File (auto-detected by extension) or explicitly via load_raw_data.
  • Security note (from the project README): this format uses Python's pickle, which can execute arbitrary code when deserialized. Only open .pmeraw files you created yourself — for anything you intend to share, use .pmeprj instead. This is a documented, deliberate risk of the legacy format, not a bug.
  • Load failures are caught explicitly for pickle.UnpicklingError, EOFError, and ImportError (a .pmeraw referencing a class that no longer exists) and reported as "Invalid project file format."

2. 2D structural formats

MOL / SDF — import

File ▸ Import ▸ MOL/SDF File... (load_mol_file):

  • .mol files are read as text, then fix_mol_block() patches the counts line to guarantee a valid V2000 tag if the line doesn't already declare V2000/V3000 (defends against hand-edited or nonstandard MOL files with a malformed counts line).
  • .sdf files use Chem.SDMolSupplier(file_path, removeHs=False) and take only the first molecule in the file — there is no multi-molecule SDF browser in the main app (some plugins, e.g. OpenBabel Conversion Tool, add multi-molecule support).
  • Both paths parse with sanitize=True/RDKit defaults and Chem.Kekulize(); if the file has no conformer, 2D coordinates are computed (AllChem.Compute2DCoords). Stereochemistry (wedge/dash, E/Z) is reassigned and re-wedged from the parsed 3D/2D geometry (AssignStereochemistry + WedgeMolBonds) — stereochemistry is preserved even though 2D coordinates may be recalculated.
  • The imported structure is placed to the right of whatever is already on the canvas (offset by 80 px past the rightmost existing atom) rather than overwriting it — MOL/SDF import is additive, not a replace.
  • Any read/parse failure (Chem.MolFromMolBlock/SDMolSupplier returning None, or any OSError/ValueError/RuntimeError/AttributeError/KeyError) surfaces as a "MOL Import Error" dialog with the underlying message.

MOL — export (2D)

File ▸ Export ▸ 2D Formats ▸ MOL File... (save_as_mol): writes state_manager.data.to_mol_block() (the current 2D structure) as a .mol file, stamping the header comment line with MoleditPy Ver. {VERSION} 2D in place of RDKit's default header if present.

SMILES / InChI — import only

File ▸ Import ▸ SMILES... / InChI... (string_importers.py): a QInputDialog text prompt feeds Chem.MolFromSmiles/Chem.MolFromInchi. On success, 2D coordinates are computed, the molecule is Kekulized, stereo is reassigned/rewedged, and it is added to the 2D canvas the same way MOL import is (offset to the right of existing content, or centered in the viewport if the canvas is empty). An empty or unparseable string reports "Invalid SMILES"/"Invalid InChI" on the status bar rather than throwing. There is no SMILES/InChI export in the main app itself — the Molecular Analysis window displays a computed SMILES/InChI string for the current molecule (read-only, copyable), but there's no dedicated "export as SMILES" file action; the PubChem plugins and MCP Server plugin add SMILES round-tripping capability on top.


3. 3D structural formats

XYZ — import (3D-viewer-only)

File ▸ Import ▸ 3D XYZ (3D View Only)... (load_xyz_for_3d_viewing_mol_from_xyz_lines):

  • Parses a standard headed XYZ (N atoms line, title/comment line, then N rows) or falls back to a headerless/irregular file (any line that isn't a valid atom-count integer causes the whole file to be treated as bare atom rows with comment lines stripped).
  • Robust to malformed rows: if the declared atom count exceeds the rows actually present, it loads what's there and warns rather than refusing the file; ghost/label columns like XX : x y z are tolerated by taking the first three float-parseable tokens as coordinates regardless of extra separator tokens.
  • Any atom symbol not in RDKit's valid element table, or a symbol containing :, or one of the recognized dummy tokens (DUMMY_XYZ_SYMBOLS) is loaded as a wildcard * (ghost atom), with the original text preserved in an xyz_original_symbol RDKit property for round-tripping.
  • Bond determination and charge prompting: unless skip_chemistry_checks is enabled in Settings or the file contains any ghost/dummy atoms (which always skip chemistry), the app first tries RDKit's rdDetermineBonds.DetermineBonds at charge 0; if that fails (or "Always ask for charge" is enabled in Settings), a modal "Import XYZ Charge" dialog prompts for the total molecular charge (default 0), with a "Skip chemistry" button that instead falls back to pure distance-based bond estimation (covalent-radius sum, 0.5×1.2×/1.3× tolerance window depending on whether either atom is H, nearest-pairs-first with a one-bond-per-H cap) and no sanitization at all.
  • Loading enters "3D Viewer Mode": the 2D editor is cleared, the molecule is shown only in the 3D view, and is_xyz_derived is set (used elsewhere to gate/adjust some 2D-oriented features).
  • If any atom row had a non-standard leading column, the status bar reports how many rows were affected.

XYZ — export (3D)

File ▸ Export ▸ 3D Formats ▸ XYZ File... (save_as_xyz): writes a standard headed XYZ with the comment/title line set to chrg = {charge} mult = {multiplicity} | Generated by MoleditPy Ver. {VERSION}, where charge comes from the _xyz_charge property if the molecule was itself XYZ-derived, else RDKit's computed formal charge; multiplicity is NumRadicalElectrons() + 1. Coordinates are written at 8 decimal places, symbol left-padded to 5 chars.

3D MOL/SDF — import (3D-viewer-only)

File ▸ Import ▸ 3D MOL/SDF (3D View Only)... (load_mol_file_for_3d_viewing): reads a MOL (with the same fix_mol_block counts-line patch) or the first molecule of an SDF, generating a conformer via AllChem.EmbedMolecule if the file has none. Also clears the 2D editor and enters 3D-viewer-only mode; is_xyz_derived is explicitly False for this path (unlike the XYZ import) since a MOL/SDF file has real bond-order/stereo data, not distance-estimated bonds.

MOL — export (3D)

File ▸ Export ▸ 3D Formats ▸ MOL File... (save_3d_as_mol): writes Chem.MolToMolBlock(current_mol, includeStereo=True) for the currently-displayed 3D structure, with the same MoleditPy Ver. {VERSION} 3D header stamp as the 2D MOL export.


4. Image / rendering export

PNG (2D)

File ▸ Export ▸ 2D Formats ▸ PNG Image... (export_2d_png): asks Yes/No/Cancel for a transparent background, temporarily hides every non-atom/non-bond scene item (labels, template previews, etc.), computes the tight bounding box of visible atoms/bonds with a 20 px margin on each side, renders via QPainter/QImage (Format_ARGB32_Premultiplied), then restores visibility/background afterward (in a finally block, so a mid-export exception still restores the canvas).

SVG (2D)

File ▸ Export ▸ 2D Formats ▸ SVG Image... (export_2d_svg): same background-choice/bounding-box/restore logic as PNG, but renders through QSvgGenerator at the screen's logical DPI (falls back to 96 if logicalDpiX() isn't available), titled "MoleditPy Molecule".

PNG (3D)

File ▸ Export ▸ 3D Formats ▸ PNG Image... (export_3d_png): same transparency prompt, then plotter.screenshot(path, transparent_background=...) — a straight PyVista/VTK render-to-file of the current 3D view exactly as displayed (camera angle, style, lighting).

STL — 3D printing (no color)

File ▸ Export ▸ 3D Formats ▸ STL File... (export_stl): walks every actor in the PyVista renderer, extracts its underlying mesh (mapper.dataset/.input, or GetInput()/GetInputAsDataSet() for raw VTK actors), merges them into one PolyData, and writes a binary STL with no color information — appropriate for slicers/3D printing.

Color STL

export_color_stl (reachable in-code but not currently wired to a menu item in the reviewed source — the export menu exposes the two-file colored path below instead): same mesh-merge logic as plain STL, but stamps each mesh's actor color into point_data (red/green/blue, diffuse_red/green/blue, and a combined colors array) before saving, for tools that read vertex-color STL extensions.

OBJ/MTL (with colors)

File ▸ Export ▸ 3D Formats ▸ OBJ/MTL (with colors)... (export_obj_mtl): produces a companion pair of files — name.obj and name.mtl (same basename, .mtl derived via os.path.splitext so a .OBJ-cased path can't collide with itself). Each PyVista actor becomes one o object_N block with its own usemtl material_N_<actor> material (ambient 0.2, diffuse = the actor's RGB, specular 0.5, shininess 32, illum model 2). Where an actor carries per-vertex color data (e.g. a single merged-atom glyph mesh where each atom has a different applied color — see the Atom/Bond Colorizer plugins), the mesh is further split into per-unique-color sub-meshes (extract_points) so each color group gets its own material in the OBJ, rather than collapsing to one averaged color.


5. What plugins add on top

The formats above are everything the main app itself reads/writes. Several official plugins extend this considerably — see Official Plugins for the full catalogue, but notably:

  • OpenBabel Conversion Tool — imports many additional chemical formats via OpenBabel, with multi-molecule (multi-frame) support.
  • Paste from ChemDraw — pastes structures directly from the ChemDraw clipboard format.
  • Animated XYZ Giffer — opens multi-frame/trajectory XYZ files and exports GIF animations.
  • Blender Export / Blender Export Pro / POV-Ray Export — export the 3D scene as a Blender Python script or POV-Ray scene file for external rendering.
  • CIF Viewer — opens .cif crystal structure files (visualization only; see its dedicated page).
  • Cube File Viewer / Cube File Viewer Advanced / Mapped Cube Viewer / Orbital Comparator — open Gaussian .cube volumetric-data files.
  • Gaussian FCHK Loader / Gaussian Freq Analyzer / Gaussian MO Analyzer — open Gaussian .fchk/.fch/.fck files.
  • ORCA Result Analyzer / ORCA Freq Analyzer — open ORCA .out output files.
  • Encrypted Project — adds a password-protected .pmeenc save/load format (AES-128).
  • DECIMER Image Importer — imports structures from PNG/JPG images via deep-learning OCSR.

6. See also

Clone this wiki locally