MoleditPy 1.16.0a1__tar.gz → 1.16.0a2__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-1.16.0a1 → moleditpy-1.16.0a2}/PKG-INFO +1 -1
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/pyproject.toml +1 -1
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/MoleditPy.egg-info/PKG-INFO +1 -1
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/MoleditPy.egg-info/SOURCES.txt +2 -1
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/main.py +1 -4
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/constants.py +1 -1
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window.py +10 -7
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_app_state.py +44 -6
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_main_init.py +86 -5
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_project_io.py +15 -2
- moleditpy-1.16.0a2/tests/test_project_io_wrappers.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/README.md +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/setup.cfg +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/MoleditPy.egg-info/entry_points.txt +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/MoleditPy.egg-info/requires.txt +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/MoleditPy.egg-info/top_level.txt +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/__init__.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/__main__.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/__init__.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/about_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/align_plane_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/alignment_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/analysis_window.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/angle_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/assets/icon.icns +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/assets/icon.ico +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/assets/icon.png +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/atom_item.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/bond_item.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/bond_length_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/calculation_worker.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/color_settings_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/constrained_optimization_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/custom_interactor_style.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/custom_qt_interactor.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/dialog3_d_picking_mixin.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/dihedral_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_compute.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_dialog_manager.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_edit_3d.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_edit_actions.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_export.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_molecular_parsers.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_string_importers.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_ui_manager.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_view_3d.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_view_loaders.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/mirror_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/molecular_data.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/molecule_scene.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/move_group_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/periodic_table_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/planarize_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/settings_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/template_preview_item.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/template_preview_view.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/translation_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/user_template_dialog.py +0 -0
- {moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/zoomable_view.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.0a2
|
|
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
|
Project-URL: Homepage, https://github.com/HiroYokoyama/python_molecular_editor
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.0a2
|
|
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
|
Project-URL: Homepage, https://github.com/HiroYokoyama/python_molecular_editor
|
|
@@ -60,4 +60,5 @@ src/moleditpy/modules/user_template_dialog.py
|
|
|
60
60
|
src/moleditpy/modules/zoomable_view.py
|
|
61
61
|
src/moleditpy/modules/assets/icon.icns
|
|
62
62
|
src/moleditpy/modules/assets/icon.ico
|
|
63
|
-
src/moleditpy/modules/assets/icon.png
|
|
63
|
+
src/moleditpy/modules/assets/icon.png
|
|
64
|
+
tests/test_project_io_wrappers.py
|
|
@@ -22,10 +22,7 @@ try:
|
|
|
22
22
|
except Exception:
|
|
23
23
|
# When executed as a standalone script (python main.py) the package-relative
|
|
24
24
|
# import won't work; fall back to absolute import that works with sys.path
|
|
25
|
-
|
|
26
|
-
from .modules.main_window import MainWindow
|
|
27
|
-
except Exception:
|
|
28
|
-
from modules.main_window import MainWindow
|
|
25
|
+
from modules.main_window import MainWindow
|
|
29
26
|
|
|
30
27
|
def main():
|
|
31
28
|
# --- Windows タスクバーアイコンのための追加処理 ---
|
|
@@ -109,6 +109,9 @@ class MainWindow(QMainWindow):
|
|
|
109
109
|
# create a small proxy (BoundFeature) that will forward call to
|
|
110
110
|
# the helper class with the MainWindow instance as the first
|
|
111
111
|
# argument.
|
|
112
|
+
# Undo/Redo操作中に状態復元中であることを示すフラグ
|
|
113
|
+
# 他のモジュールが呼び出される前に初期化する
|
|
114
|
+
self._is_restoring_state = False
|
|
112
115
|
|
|
113
116
|
class BoundFeature:
|
|
114
117
|
"""Bind a feature-class method calls to the MainWindow.
|
|
@@ -395,7 +398,7 @@ class MainWindow(QMainWindow):
|
|
|
395
398
|
|
|
396
399
|
def load_mol_file(self, file_path=None):
|
|
397
400
|
# --- MOVED TO main_window_molecular_parsers.py ---
|
|
398
|
-
return self.main_window_molecular_parsers.load_mol_file(file_path
|
|
401
|
+
return self.main_window_molecular_parsers.load_mol_file(file_path)
|
|
399
402
|
|
|
400
403
|
def load_mol_for_3d_viewing(self):
|
|
401
404
|
# --- MOVED TO main_window_view_loaders.py ---
|
|
@@ -403,9 +406,9 @@ class MainWindow(QMainWindow):
|
|
|
403
406
|
|
|
404
407
|
def load_xyz_for_3d_viewing(self, file_path=None):
|
|
405
408
|
# --- MOVED TO main_window_view_loaders.py ---
|
|
406
|
-
return self.main_window_view_loaders.load_xyz_for_3d_viewing(file_path
|
|
409
|
+
return self.main_window_view_loaders.load_xyz_for_3d_viewing(file_path)
|
|
407
410
|
|
|
408
|
-
def load_xyz_file(self, file_path):
|
|
411
|
+
def load_xyz_file(self, file_path=None):
|
|
409
412
|
# --- MOVED TO main_window_molecular_parsers.py ---
|
|
410
413
|
return self.main_window_molecular_parsers.load_xyz_file(file_path)
|
|
411
414
|
|
|
@@ -427,7 +430,7 @@ class MainWindow(QMainWindow):
|
|
|
427
430
|
|
|
428
431
|
def load_raw_data(self, file_path=None):
|
|
429
432
|
# --- MOVED TO main_window_project_io.py ---
|
|
430
|
-
return self.main_window_project_io.load_raw_data(file_path
|
|
433
|
+
return self.main_window_project_io.load_raw_data(file_path)
|
|
431
434
|
|
|
432
435
|
def save_as_json(self):
|
|
433
436
|
# --- MOVED TO main_window_project_io.py ---
|
|
@@ -439,11 +442,11 @@ class MainWindow(QMainWindow):
|
|
|
439
442
|
|
|
440
443
|
def load_json_data(self, file_path=None):
|
|
441
444
|
# --- MOVED TO main_window_project_io.py ---
|
|
442
|
-
return self.main_window_project_io.load_json_data(file_path
|
|
445
|
+
return self.main_window_project_io.load_json_data(file_path)
|
|
443
446
|
|
|
444
447
|
def open_project_file(self, file_path=None):
|
|
445
448
|
# --- MOVED TO main_window_project_io.py ---
|
|
446
|
-
return self.main_window_project_io.open_project_file(file_path
|
|
449
|
+
return self.main_window_project_io.open_project_file(file_path)
|
|
447
450
|
|
|
448
451
|
def load_from_json_data(self, json_data):
|
|
449
452
|
# --- MOVED TO main_window_app_state.py ---
|
|
@@ -611,7 +614,7 @@ class MainWindow(QMainWindow):
|
|
|
611
614
|
|
|
612
615
|
def load_mol_file_for_3d_viewing(self, file_path=None):
|
|
613
616
|
# --- MOVED TO main_window_view_loaders.py ---
|
|
614
|
-
return self.main_window_view_loaders.load_mol_file_for_3d_viewing(file_path
|
|
617
|
+
return self.main_window_view_loaders.load_mol_file_for_3d_viewing(file_path)
|
|
615
618
|
|
|
616
619
|
def load_command_line_file(self, file_path):
|
|
617
620
|
# --- MOVED TO main_window_main_init.py ---
|
|
@@ -80,9 +80,13 @@ except Exception:
|
|
|
80
80
|
class MainWindowAppState(object):
|
|
81
81
|
""" main_window.py から分離された機能クラス """
|
|
82
82
|
|
|
83
|
-
def __init__(self
|
|
84
|
-
"""
|
|
85
|
-
|
|
83
|
+
def __init__(self):
|
|
84
|
+
"""
|
|
85
|
+
クラスの初期化
|
|
86
|
+
BoundFeature経由で呼ばれるため、'self' には MainWindow インスタンスが渡されます。
|
|
87
|
+
"""
|
|
88
|
+
self.DEBUG_UNDO = False
|
|
89
|
+
|
|
86
90
|
|
|
87
91
|
|
|
88
92
|
def get_current_state(self):
|
|
@@ -234,6 +238,9 @@ class MainWindowAppState(object):
|
|
|
234
238
|
|
|
235
239
|
|
|
236
240
|
def push_undo_state(self):
|
|
241
|
+
if self._is_restoring_state:
|
|
242
|
+
return
|
|
243
|
+
|
|
237
244
|
current_state_for_comparison = {
|
|
238
245
|
'atoms': {k: (v['symbol'], v['item'].pos().x(), v['item'].pos().y(), v.get('charge', 0), v.get('radical', 0)) for k, v in self.data.atoms.items()},
|
|
239
246
|
'bonds': {k: (v['order'], v.get('stereo', 0)) for k, v in self.data.bonds.items()},
|
|
@@ -254,8 +261,15 @@ class MainWindowAppState(object):
|
|
|
254
261
|
}
|
|
255
262
|
|
|
256
263
|
if not last_state_for_comparison or current_state_for_comparison != last_state_for_comparison:
|
|
257
|
-
state
|
|
264
|
+
# Deepcopy state to ensure saved states are immutable and not affected
|
|
265
|
+
# by later modifications to objects referenced from the state.
|
|
266
|
+
state = copy.deepcopy(self.get_current_state())
|
|
258
267
|
self.undo_stack.append(state)
|
|
268
|
+
if getattr(self, 'DEBUG_UNDO', False):
|
|
269
|
+
try:
|
|
270
|
+
print(f"DEBUG_UNDO: push_undo_state -> new stack size: {len(self.undo_stack)}")
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
259
273
|
self.redo_stack.clear()
|
|
260
274
|
# 初期化完了後のみ変更があったことを記録
|
|
261
275
|
if self.initialization_complete:
|
|
@@ -320,6 +334,11 @@ class MainWindowAppState(object):
|
|
|
320
334
|
self.undo_stack.clear()
|
|
321
335
|
self.redo_stack.clear()
|
|
322
336
|
self.push_undo_state()
|
|
337
|
+
if getattr(self, 'DEBUG_UNDO', False):
|
|
338
|
+
try:
|
|
339
|
+
print(f"DEBUG_UNDO: reset_undo_stack -> undo={len(self.undo_stack)} redo={len(self.redo_stack)}")
|
|
340
|
+
except Exception:
|
|
341
|
+
pass
|
|
323
342
|
|
|
324
343
|
|
|
325
344
|
|
|
@@ -327,7 +346,12 @@ class MainWindowAppState(object):
|
|
|
327
346
|
if len(self.undo_stack) > 1:
|
|
328
347
|
self.redo_stack.append(self.undo_stack.pop())
|
|
329
348
|
state = self.undo_stack[-1]
|
|
330
|
-
self.
|
|
349
|
+
self._is_restoring_state = True
|
|
350
|
+
try:
|
|
351
|
+
self.set_state_from_data(state)
|
|
352
|
+
finally:
|
|
353
|
+
self._is_restoring_state = False
|
|
354
|
+
|
|
331
355
|
|
|
332
356
|
# Undo後に3D構造の状態に基づいてメニューを再評価
|
|
333
357
|
if self.current_mol and self.current_mol.GetNumAtoms() > 0:
|
|
@@ -337,6 +361,11 @@ class MainWindowAppState(object):
|
|
|
337
361
|
# 3D構造がない場合は3D編集機能を無効化
|
|
338
362
|
self._enable_3d_edit_actions(False)
|
|
339
363
|
|
|
364
|
+
if getattr(self, 'DEBUG_UNDO', False):
|
|
365
|
+
try:
|
|
366
|
+
print(f"DEBUG_UNDO: undo -> undo_stack size: {len(self.undo_stack)}, redo_stack size: {len(self.redo_stack)}")
|
|
367
|
+
except Exception:
|
|
368
|
+
pass
|
|
340
369
|
self.update_undo_redo_actions()
|
|
341
370
|
self.update_realtime_info()
|
|
342
371
|
self.view_2d.setFocus()
|
|
@@ -347,7 +376,11 @@ class MainWindowAppState(object):
|
|
|
347
376
|
if self.redo_stack:
|
|
348
377
|
state = self.redo_stack.pop()
|
|
349
378
|
self.undo_stack.append(state)
|
|
350
|
-
self.
|
|
379
|
+
self._is_restoring_state = True
|
|
380
|
+
try:
|
|
381
|
+
self.set_state_from_data(state)
|
|
382
|
+
finally:
|
|
383
|
+
self._is_restoring_state = False
|
|
351
384
|
|
|
352
385
|
# Redo後に3D構造の状態に基づいてメニューを再評価
|
|
353
386
|
if self.current_mol and self.current_mol.GetNumAtoms() > 0:
|
|
@@ -357,6 +390,11 @@ class MainWindowAppState(object):
|
|
|
357
390
|
# 3D構造がない場合は3D編集機能を無効化
|
|
358
391
|
self._enable_3d_edit_actions(False)
|
|
359
392
|
|
|
393
|
+
if getattr(self, 'DEBUG_UNDO', False):
|
|
394
|
+
try:
|
|
395
|
+
print(f"DEBUG_UNDO: redo -> undo_stack size: {len(self.undo_stack)}, redo_stack size: {len(self.redo_stack)}")
|
|
396
|
+
except Exception:
|
|
397
|
+
pass
|
|
360
398
|
self.update_undo_redo_actions()
|
|
361
399
|
self.update_realtime_info()
|
|
362
400
|
self.view_2d.setFocus()
|
|
@@ -36,6 +36,65 @@ from PyQt6.QtGui import (
|
|
|
36
36
|
from PyQt6.QtCore import (
|
|
37
37
|
Qt, QPointF, QRectF, QLineF, QUrl, QTimer
|
|
38
38
|
)
|
|
39
|
+
import platform
|
|
40
|
+
import subprocess
|
|
41
|
+
try:
|
|
42
|
+
import winreg
|
|
43
|
+
except Exception:
|
|
44
|
+
winreg = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def detect_system_dark_mode():
|
|
48
|
+
"""Return True if the OS prefers dark app theme, False if light, or None if unknown.
|
|
49
|
+
|
|
50
|
+
This is a best-effort, cross-platform check supporting Windows (registry),
|
|
51
|
+
macOS (defaults read), and GNOME/GTK-based Linux (gsettings). Return
|
|
52
|
+
None if no reliable information is available.
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
# Windows: read registry AppsUseLightTheme (0 = dark, 1 = light)
|
|
56
|
+
if platform.system() == 'Windows' and winreg is not None:
|
|
57
|
+
try:
|
|
58
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
|
59
|
+
r'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize') as k:
|
|
60
|
+
val, _ = winreg.QueryValueEx(k, 'AppsUseLightTheme')
|
|
61
|
+
return False if int(val) == 0 else True
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
# macOS: 'defaults read -g AppleInterfaceStyle' returns 'Dark' in dark mode
|
|
66
|
+
if platform.system() == 'Darwin':
|
|
67
|
+
try:
|
|
68
|
+
p = subprocess.run(['defaults', 'read', '-g', 'AppleInterfaceStyle'], capture_output=True, text=True)
|
|
69
|
+
if p.returncode == 0 and p.stdout.strip().lower() == 'dark':
|
|
70
|
+
return True
|
|
71
|
+
# Key absence implies light mode
|
|
72
|
+
return False
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
# Linux / GNOME: try color-scheme gsetting; fallback to gtk-theme detection
|
|
77
|
+
if platform.system() == 'Linux':
|
|
78
|
+
try:
|
|
79
|
+
p = subprocess.run(['gsettings', 'get', 'org.gnome.desktop.interface', 'color-scheme'], capture_output=True, text=True)
|
|
80
|
+
if p.returncode == 0:
|
|
81
|
+
out = p.stdout.strip().strip("'\n ")
|
|
82
|
+
if 'dark' in out.lower():
|
|
83
|
+
return True
|
|
84
|
+
if 'light' in out.lower():
|
|
85
|
+
return False
|
|
86
|
+
except Exception:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
p = subprocess.run(['gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-theme'], capture_output=True, text=True)
|
|
91
|
+
if p.returncode == 0 and '-dark' in p.stdout.lower():
|
|
92
|
+
return True
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
return None
|
|
39
98
|
|
|
40
99
|
|
|
41
100
|
# Use centralized Open Babel availability from package-level __init__
|
|
@@ -406,10 +465,19 @@ class MainWindowMainInit(object):
|
|
|
406
465
|
toolbar.addSeparator()
|
|
407
466
|
|
|
408
467
|
# --- アイコン前景色を決めるヘルパー(ダーク/ライトモード対応) ---
|
|
409
|
-
|
|
410
|
-
|
|
468
|
+
# Use module-level detector `detect_system_dark_mode()` so tests and other
|
|
469
|
+
# modules can reuse the logic.
|
|
470
|
+
|
|
411
471
|
|
|
412
|
-
|
|
472
|
+
def _icon_foreground_color():
|
|
473
|
+
"""Return a QColor for icon foreground.
|
|
474
|
+
|
|
475
|
+
NOTE: this application inverts the usual foreground mapping so icons
|
|
476
|
+
will be the *opposite* color to the background (i.e., black on dark
|
|
477
|
+
backgrounds, white on light backgrounds). This intentionally reverses
|
|
478
|
+
the previous behavior so button icons don't blend into the 3D-view
|
|
479
|
+
background. Priority: explicit setting in 'icon_foreground' -> OS
|
|
480
|
+
theme preference -> configured 3D background -> application palette.
|
|
413
481
|
"""
|
|
414
482
|
try:
|
|
415
483
|
fg_hex = self.settings.get('icon_foreground')
|
|
@@ -420,13 +488,25 @@ class MainWindowMainInit(object):
|
|
|
420
488
|
except Exception:
|
|
421
489
|
pass
|
|
422
490
|
|
|
491
|
+
# 1) Prefer the system/OS dark-mode preference if available.
|
|
492
|
+
try:
|
|
493
|
+
os_pref = detect_system_dark_mode()
|
|
494
|
+
# Invert the color so in dark-pref OS we return black, in light we return white
|
|
495
|
+
if os_pref is not None:
|
|
496
|
+
return QColor('#000000') if os_pref else QColor('#FFFFFF')
|
|
497
|
+
except Exception:
|
|
498
|
+
pass
|
|
499
|
+
|
|
423
500
|
try:
|
|
501
|
+
# Keep background_color as a fallback: if system preference isn't
|
|
502
|
+
# available we'll use the configured 3D view background from settings.
|
|
424
503
|
bg_hex = self.settings.get('background_color')
|
|
425
504
|
if bg_hex:
|
|
426
505
|
bg = QColor(bg_hex)
|
|
427
506
|
if bg.isValid():
|
|
428
507
|
lum = 0.2126 * bg.redF() + 0.7152 * bg.greenF() + 0.0722 * bg.blueF()
|
|
429
|
-
return
|
|
508
|
+
# Inverted: return black on dark (lum<0.5), white on light
|
|
509
|
+
return QColor('#000000') if lum < 0.5 else QColor('#FFFFFF')
|
|
430
510
|
except Exception:
|
|
431
511
|
pass
|
|
432
512
|
|
|
@@ -435,7 +515,8 @@ class MainWindowMainInit(object):
|
|
|
435
515
|
# palette.window() returns a QBrush; call color()
|
|
436
516
|
window_bg = pal.window().color()
|
|
437
517
|
lum = 0.2126 * window_bg.redF() + 0.7152 * window_bg.greenF() + 0.0722 * window_bg.blueF()
|
|
438
|
-
|
|
518
|
+
# Inverted mapping for palette fallback
|
|
519
|
+
return QColor('#000000') if lum < 0.5 else QColor('#FFFFFF')
|
|
439
520
|
except Exception:
|
|
440
521
|
return QColor('#000000')
|
|
441
522
|
|
|
@@ -164,6 +164,11 @@ class MainWindowProjectIo(object):
|
|
|
164
164
|
# Replace current file with the newly saved file so subsequent saves go to this path
|
|
165
165
|
self.current_file_path = file_path
|
|
166
166
|
self.update_window_title()
|
|
167
|
+
# Mark this state as the last saved state for undo tracking
|
|
168
|
+
try:
|
|
169
|
+
self._saved_state = copy.deepcopy(self.get_current_state())
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
167
172
|
|
|
168
173
|
self.statusBar().showMessage(f"Project saved to {file_path}")
|
|
169
174
|
|
|
@@ -217,6 +222,10 @@ class MainWindowProjectIo(object):
|
|
|
217
222
|
# Update current file to the newly saved raw file
|
|
218
223
|
self.current_file_path = file_path
|
|
219
224
|
self.update_window_title()
|
|
225
|
+
try:
|
|
226
|
+
self._saved_state = copy.deepcopy(self.get_current_state())
|
|
227
|
+
except Exception:
|
|
228
|
+
pass
|
|
220
229
|
|
|
221
230
|
self.statusBar().showMessage(f"Project saved to {file_path}")
|
|
222
231
|
|
|
@@ -249,8 +258,12 @@ class MainWindowProjectIo(object):
|
|
|
249
258
|
self.has_unsaved_changes = False
|
|
250
259
|
self.current_file_path = file_path
|
|
251
260
|
self.update_window_title()
|
|
252
|
-
|
|
253
|
-
|
|
261
|
+
try:
|
|
262
|
+
self._saved_state= copy.deepcopy(self.et_current_state())
|
|
263
|
+
except Exception:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
self.statusBar.showMessage(f"Project loaded from {file_path}")
|
|
254
267
|
|
|
255
268
|
QTimer.singleShot(0, self.fit_to_view)
|
|
256
269
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/constrained_optimization_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_dialog_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_molecular_parsers.py
RENAMED
|
File without changes
|
{moleditpy-1.16.0a1 → moleditpy-1.16.0a2}/src/moleditpy/modules/main_window_string_importers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|