MoleditPy 3.0.0a8__tar.gz → 3.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/PKG-INFO +1 -1
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/pyproject.toml +1 -1
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/PKG-INFO +1 -1
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/plugin_interface.py +33 -2
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/plugin_manager_window.py +56 -56
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/angle_dialog.py +404 -404
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/bond_length_dialog.py +351 -351
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/io_logic.py +2 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/main_window.py +7 -2
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/main_window_init.py +16 -20
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_dialog.py +5 -1
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_3d_tabs.py +2 -4
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/ui_manager.py +51 -8
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/view_3d_logic.py +2 -2
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/constants.py +1 -1
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/default_settings.py +85 -85
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/LICENSE +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/README.md +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/setup.cfg +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/SOURCES.txt +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/entry_points.txt +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/requires.txt +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/top_level.txt +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/__init__.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/__main__.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/file_icon.ico +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/icon.icns +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/icon.ico +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/icon.png +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/core/__init__.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/core/mol_geometry.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/core/molecular_data.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/main.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/__init__.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/plugin_manager.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/__init__.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/about_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/align_plane_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/alignment_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/analysis_window.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/app_state.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/atom_item.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/base_picking_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/bond_item.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/calculation_worker.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/color_settings_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/compute_logic.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/custom_interactor_style.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/custom_qt_interactor.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/dialog_3d_picking_mixin.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/dialog_logic.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/dihedral_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/edit_3d_logic.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/edit_actions_logic.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/export_logic.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/geometry_base_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/mirror_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/molecular_scene_handler.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/molecule_scene.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/move_group_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/periodic_table_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/planarize_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/__init__.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/sip_isdeleted_safe.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/string_importers.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/template_preview_item.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/template_preview_view.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/translation_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/user_template_dialog.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/zoomable_view.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/__init__.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/system_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
|
|
5
5
|
Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
|
|
5
5
|
Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -173,6 +173,14 @@ class PluginContext:
|
|
|
173
173
|
"""
|
|
174
174
|
return self._manager.get_main_window()
|
|
175
175
|
|
|
176
|
+
def show_status_message(self, message: str, timeout: int = 3000) -> None:
|
|
177
|
+
"""
|
|
178
|
+
Display a temporary message in the status bar of the main window.
|
|
179
|
+
"""
|
|
180
|
+
mw = self.get_main_window()
|
|
181
|
+
if mw and hasattr(mw, "ui_manager"):
|
|
182
|
+
mw.ui_manager.update_status_bar(message)
|
|
183
|
+
|
|
176
184
|
@property
|
|
177
185
|
def current_mol(self) -> Any:
|
|
178
186
|
"""
|
|
@@ -186,8 +194,7 @@ class PluginContext:
|
|
|
186
194
|
mw = self.get_main_window()
|
|
187
195
|
if mw and hasattr(mw, "view_3d_manager"):
|
|
188
196
|
mw.view_3d_manager.current_mol = mol
|
|
189
|
-
|
|
190
|
-
mw.view_3d_manager.draw_molecule_3d(mol)
|
|
197
|
+
mw.view_3d_manager.draw_molecule_3d(mol)
|
|
191
198
|
|
|
192
199
|
@property
|
|
193
200
|
def current_molecule(self) -> Any:
|
|
@@ -214,6 +221,30 @@ class PluginContext:
|
|
|
214
221
|
mw = self.get_main_window()
|
|
215
222
|
return mw.init_manager.scene if mw and hasattr(mw, "init_manager") else None
|
|
216
223
|
|
|
224
|
+
def draw_molecule_3d(self, mol: Any) -> None:
|
|
225
|
+
"""Draw a molecule in the 3D scene (Direct manager call)."""
|
|
226
|
+
mw = self.get_main_window()
|
|
227
|
+
if mw and hasattr(mw, "view_3d_manager"):
|
|
228
|
+
mw.view_3d_manager.draw_molecule_3d(mol)
|
|
229
|
+
|
|
230
|
+
def refresh_3d_view(self) -> None:
|
|
231
|
+
"""Force the 3D window to redraw using the current molecule."""
|
|
232
|
+
mw = self.get_main_window()
|
|
233
|
+
if mw and hasattr(mw, "view_3d_manager"):
|
|
234
|
+
mol = getattr(mw.view_3d_manager, "current_mol", None)
|
|
235
|
+
if mol:
|
|
236
|
+
mw.view_3d_manager.draw_molecule_3d(mol)
|
|
237
|
+
else:
|
|
238
|
+
# Also redraw/clear plotter if no molecule
|
|
239
|
+
if hasattr(mw.view_3d_manager, "plotter") and mw.view_3d_manager.plotter:
|
|
240
|
+
mw.view_3d_manager.plotter.render()
|
|
241
|
+
|
|
242
|
+
def reset_3d_camera(self) -> None:
|
|
243
|
+
"""Zoom in and re-center the 3D viewport to fit the current molecule."""
|
|
244
|
+
mw = self.get_main_window()
|
|
245
|
+
if mw and hasattr(mw, "view_3d_manager") and mw.view_3d_manager.plotter:
|
|
246
|
+
mw.view_3d_manager.plotter.reset_camera()
|
|
247
|
+
|
|
217
248
|
def add_export_action(self, label: str, callback: Callable):
|
|
218
249
|
"""
|
|
219
250
|
Register a custom export action.
|
|
@@ -10,9 +10,9 @@ Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
|
10
10
|
DOI: 10.5281/zenodo.17268532
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
import os
|
|
14
|
-
import shutil
|
|
15
|
-
import hashlib
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
15
|
+
import hashlib
|
|
16
16
|
|
|
17
17
|
from PyQt6.QtCore import Qt, QUrl
|
|
18
18
|
from PyQt6.QtGui import QDesktopServices, QDragEnterEvent, QDropEvent
|
|
@@ -258,10 +258,10 @@ class PluginManagerWindow(QDialog):
|
|
|
258
258
|
is_valid = True
|
|
259
259
|
is_folder = True
|
|
260
260
|
|
|
261
|
-
if is_valid:
|
|
262
|
-
sha256_value = self._compute_sha256(file_path)
|
|
263
|
-
# Extract info and confirm
|
|
264
|
-
info = {
|
|
261
|
+
if is_valid:
|
|
262
|
+
sha256_value = self._compute_sha256(file_path)
|
|
263
|
+
# Extract info and confirm
|
|
264
|
+
info = {
|
|
265
265
|
"name": os.path.basename(file_path),
|
|
266
266
|
"version": "Unknown",
|
|
267
267
|
"author": "Unknown",
|
|
@@ -281,15 +281,15 @@ class PluginManagerWindow(QDialog):
|
|
|
281
281
|
elif file_path.endswith(".py"):
|
|
282
282
|
info = self.plugin_manager.get_plugin_info_safe(file_path)
|
|
283
283
|
|
|
284
|
-
msg = (
|
|
285
|
-
f"Do you want to install this plugin?\n\n"
|
|
286
|
-
f"Name: {info['name']}\n"
|
|
287
|
-
f"Author: {info['author']}\n"
|
|
288
|
-
f"Version: {info['version']}\n"
|
|
289
|
-
f"Description: {info['description']}\n\n"
|
|
290
|
-
f"File: {os.path.basename(file_path)}\n"
|
|
291
|
-
f"SHA-256: {sha256_value}"
|
|
292
|
-
)
|
|
284
|
+
msg = (
|
|
285
|
+
f"Do you want to install this plugin?\n\n"
|
|
286
|
+
f"Name: {info['name']}\n"
|
|
287
|
+
f"Author: {info['author']}\n"
|
|
288
|
+
f"Version: {info['version']}\n"
|
|
289
|
+
f"Description: {info['description']}\n\n"
|
|
290
|
+
f"File: {os.path.basename(file_path)}\n"
|
|
291
|
+
f"SHA-256: {sha256_value}"
|
|
292
|
+
)
|
|
293
293
|
|
|
294
294
|
reply = QMessageBox.question(
|
|
295
295
|
self,
|
|
@@ -310,43 +310,43 @@ class PluginManagerWindow(QDialog):
|
|
|
310
310
|
summary = ""
|
|
311
311
|
if files_installed:
|
|
312
312
|
summary += "Installed:\n" + "\n".join(files_installed) + "\n\n"
|
|
313
|
-
if errors:
|
|
314
|
-
summary += "Errors:\n" + "\n".join(errors)
|
|
315
|
-
|
|
316
|
-
QMessageBox.information(self, "Plugin Installation", summary)
|
|
317
|
-
|
|
318
|
-
def _compute_sha256(self, path):
|
|
319
|
-
if os.path.isfile(path):
|
|
320
|
-
return self._sha256_for_file(path)
|
|
321
|
-
if os.path.isdir(path):
|
|
322
|
-
return self._sha256_for_directory(path)
|
|
323
|
-
return "N/A"
|
|
324
|
-
|
|
325
|
-
def _sha256_for_file(self, path):
|
|
326
|
-
hasher = hashlib.sha256()
|
|
327
|
-
try:
|
|
328
|
-
with open(path, "rb") as f:
|
|
329
|
-
for chunk in iter(lambda: f.read(8192), b""):
|
|
330
|
-
hasher.update(chunk)
|
|
331
|
-
return hasher.hexdigest()
|
|
332
|
-
except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
|
|
333
|
-
return "N/A"
|
|
334
|
-
|
|
335
|
-
def _sha256_for_directory(self, dir_path):
|
|
336
|
-
hasher = hashlib.sha256()
|
|
337
|
-
try:
|
|
338
|
-
root = os.path.abspath(dir_path)
|
|
339
|
-
for current_root, _dirs, files in os.walk(root):
|
|
340
|
-
rel_root = os.path.relpath(current_root, root)
|
|
341
|
-
for filename in sorted(files):
|
|
342
|
-
file_path = os.path.join(current_root, filename)
|
|
343
|
-
rel_path = os.path.normpath(os.path.join(rel_root, filename))
|
|
344
|
-
hasher.update(rel_path.encode("utf-8", errors="replace"))
|
|
345
|
-
hasher.update(b"\0")
|
|
346
|
-
with open(file_path, "rb") as f:
|
|
347
|
-
for chunk in iter(lambda: f.read(8192), b""):
|
|
348
|
-
hasher.update(chunk)
|
|
349
|
-
hasher.update(b"\0")
|
|
350
|
-
return hasher.hexdigest()
|
|
351
|
-
except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
|
|
352
|
-
return "N/A"
|
|
313
|
+
if errors:
|
|
314
|
+
summary += "Errors:\n" + "\n".join(errors)
|
|
315
|
+
|
|
316
|
+
QMessageBox.information(self, "Plugin Installation", summary)
|
|
317
|
+
|
|
318
|
+
def _compute_sha256(self, path):
|
|
319
|
+
if os.path.isfile(path):
|
|
320
|
+
return self._sha256_for_file(path)
|
|
321
|
+
if os.path.isdir(path):
|
|
322
|
+
return self._sha256_for_directory(path)
|
|
323
|
+
return "N/A"
|
|
324
|
+
|
|
325
|
+
def _sha256_for_file(self, path):
|
|
326
|
+
hasher = hashlib.sha256()
|
|
327
|
+
try:
|
|
328
|
+
with open(path, "rb") as f:
|
|
329
|
+
for chunk in iter(lambda: f.read(8192), b""):
|
|
330
|
+
hasher.update(chunk)
|
|
331
|
+
return hasher.hexdigest()
|
|
332
|
+
except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
|
|
333
|
+
return "N/A"
|
|
334
|
+
|
|
335
|
+
def _sha256_for_directory(self, dir_path):
|
|
336
|
+
hasher = hashlib.sha256()
|
|
337
|
+
try:
|
|
338
|
+
root = os.path.abspath(dir_path)
|
|
339
|
+
for current_root, _dirs, files in os.walk(root):
|
|
340
|
+
rel_root = os.path.relpath(current_root, root)
|
|
341
|
+
for filename in sorted(files):
|
|
342
|
+
file_path = os.path.join(current_root, filename)
|
|
343
|
+
rel_path = os.path.normpath(os.path.join(rel_root, filename))
|
|
344
|
+
hasher.update(rel_path.encode("utf-8", errors="replace"))
|
|
345
|
+
hasher.update(b"\0")
|
|
346
|
+
with open(file_path, "rb") as f:
|
|
347
|
+
for chunk in iter(lambda: f.read(8192), b""):
|
|
348
|
+
hasher.update(chunk)
|
|
349
|
+
hasher.update(b"\0")
|
|
350
|
+
return hasher.hexdigest()
|
|
351
|
+
except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
|
|
352
|
+
return "N/A"
|