MoleditPy 2.3.3__py3-none-any.whl → 2.4.1__py3-none-any.whl
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/modules/atom_item.py +61 -14
- moleditpy/modules/bond_item.py +78 -20
- moleditpy/modules/constants.py +1 -1
- moleditpy/modules/main_window.py +8 -0
- moleditpy/modules/main_window_edit_actions.py +105 -2
- moleditpy/modules/main_window_export.py +119 -8
- moleditpy/modules/main_window_main_init.py +38 -3
- moleditpy/modules/settings_dialog.py +361 -7
- {moleditpy-2.3.3.dist-info → moleditpy-2.4.1.dist-info}/METADATA +1 -1
- {moleditpy-2.3.3.dist-info → moleditpy-2.4.1.dist-info}/RECORD +14 -14
- {moleditpy-2.3.3.dist-info → moleditpy-2.4.1.dist-info}/WHEEL +1 -1
- {moleditpy-2.3.3.dist-info → moleditpy-2.4.1.dist-info}/entry_points.txt +0 -0
- {moleditpy-2.3.3.dist-info → moleditpy-2.4.1.dist-info}/licenses/LICENSE +0 -0
- {moleditpy-2.3.3.dist-info → moleditpy-2.4.1.dist-info}/top_level.txt +0 -0
moleditpy/modules/atom_item.py
CHANGED
|
@@ -13,7 +13,7 @@ DOI: 10.5281/zenodo.17268532
|
|
|
13
13
|
from PyQt6.QtWidgets import QGraphicsItem
|
|
14
14
|
|
|
15
15
|
from PyQt6.QtGui import (
|
|
16
|
-
QPen, QBrush, QColor, QFont, QPainterPath, QFontMetricsF
|
|
16
|
+
QPen, QBrush, QColor, QFont, QPainterPath, QFontMetricsF, QPainter
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from PyQt6.QtCore import (
|
|
@@ -45,14 +45,41 @@ class AtomItem(QGraphicsItem):
|
|
|
45
45
|
self.setPos(pos)
|
|
46
46
|
self.implicit_h_count = 0
|
|
47
47
|
self.setFlags(QGraphicsItem.GraphicsItemFlag.ItemIsMovable | QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
|
|
48
|
-
self.setZValue(1)
|
|
48
|
+
self.setZValue(1)
|
|
49
|
+
self.update_style()
|
|
49
50
|
self.setAcceptHoverEvents(True)
|
|
50
51
|
self.hovered = False
|
|
51
52
|
self.has_problem = False
|
|
52
53
|
|
|
54
|
+
|
|
55
|
+
def update_style(self):
|
|
56
|
+
# Allow updating font preference dynamically
|
|
57
|
+
font_size = 20
|
|
58
|
+
try:
|
|
59
|
+
if self.scene() and self.scene().views():
|
|
60
|
+
win = self.scene().views()[0].window()
|
|
61
|
+
if win and hasattr(win, 'settings'):
|
|
62
|
+
font_size = win.settings.get('atom_font_size_2d', 20)
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
self.font = QFont(FONT_FAMILY, font_size, FONT_WEIGHT_BOLD)
|
|
66
|
+
self.prepareGeometryChange()
|
|
67
|
+
|
|
68
|
+
self.is_visible = not (self.symbol == 'C' and len(self.bonds) > 0 and self.charge == 0 and self.radical == 0)
|
|
69
|
+
self.update()
|
|
70
|
+
|
|
53
71
|
def boundingRect(self):
|
|
54
72
|
# --- paint()メソッドと完全に同じロジックでテキストの位置とサイズを計算 ---
|
|
55
|
-
|
|
73
|
+
# Get dynamic font size
|
|
74
|
+
font_size = 20
|
|
75
|
+
try:
|
|
76
|
+
if self.scene() and self.scene().views():
|
|
77
|
+
win = self.scene().views()[0].window()
|
|
78
|
+
if win and hasattr(win, 'settings'):
|
|
79
|
+
font_size = win.settings.get('atom_font_size_2d', 20)
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
|
+
font = QFont(FONT_FAMILY, font_size, FONT_WEIGHT_BOLD)
|
|
56
83
|
fm = QFontMetricsF(font)
|
|
57
84
|
|
|
58
85
|
hydrogen_part = ""
|
|
@@ -164,9 +191,21 @@ class AtomItem(QGraphicsItem):
|
|
|
164
191
|
return path
|
|
165
192
|
|
|
166
193
|
def paint(self, painter, option, widget):
|
|
194
|
+
# Color logic: check if we should use bond color (uniform) or CPK (element-specific)
|
|
167
195
|
color = CPK_COLORS.get(self.symbol, CPK_COLORS['DEFAULT'])
|
|
196
|
+
try:
|
|
197
|
+
if self.scene() and self.scene().views():
|
|
198
|
+
win = self.scene().views()[0].window()
|
|
199
|
+
if win and hasattr(win, 'settings'):
|
|
200
|
+
if win.settings.get('atom_use_bond_color_2d', False):
|
|
201
|
+
bond_col = win.settings.get('bond_color_2d', '#222222')
|
|
202
|
+
color = QColor(bond_col)
|
|
203
|
+
except Exception:
|
|
204
|
+
pass
|
|
205
|
+
|
|
168
206
|
if self.is_visible:
|
|
169
207
|
# 1. 描画の準備
|
|
208
|
+
# Ensure correct font is used (self.font should be updated by update_style)
|
|
170
209
|
painter.setFont(self.font)
|
|
171
210
|
fm = painter.fontMetrics()
|
|
172
211
|
|
|
@@ -253,19 +292,29 @@ class AtomItem(QGraphicsItem):
|
|
|
253
292
|
offset_x = -symbol_rect.width() // 2
|
|
254
293
|
text_rect.moveTo(offset_x, -text_rect.height() // 2)
|
|
255
294
|
|
|
256
|
-
# 2.
|
|
295
|
+
# 2. 原子記号の背景を処理(白で塗りつぶす か 透明なら切り抜く)
|
|
257
296
|
if self.scene():
|
|
258
297
|
bg_brush = self.scene().backgroundBrush()
|
|
259
298
|
bg_rect = text_rect.adjusted(-5, -8, 5, 8)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
299
|
+
|
|
300
|
+
if bg_brush.style() == Qt.BrushStyle.NoBrush:
|
|
301
|
+
# 背景が透明の場合は、CompositionMode_Clearを使って
|
|
302
|
+
# 重なっている結合の線を「消しゴム」のように消す
|
|
303
|
+
painter.save()
|
|
304
|
+
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Clear)
|
|
305
|
+
painter.setBrush(QColor(0, 0, 0, 255)) # 色は何でも良い(アルファが重要)
|
|
306
|
+
painter.setPen(Qt.PenStyle.NoPen)
|
|
307
|
+
painter.drawEllipse(bg_rect)
|
|
308
|
+
painter.restore()
|
|
309
|
+
else:
|
|
310
|
+
# 背景がある場合は、その背景色で塗りつぶす(従来通り)
|
|
311
|
+
painter.setBrush(bg_brush)
|
|
312
|
+
painter.setPen(Qt.PenStyle.NoPen)
|
|
313
|
+
painter.drawEllipse(bg_rect)
|
|
263
314
|
|
|
264
315
|
# 3. 原子記号自体を描画
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
else:
|
|
268
|
-
painter.setPen(QPen(color))
|
|
316
|
+
# Color is already determined above
|
|
317
|
+
painter.setPen(QPen(color))
|
|
269
318
|
painter.drawText(text_rect, int(alignment_flag), display_text)
|
|
270
319
|
|
|
271
320
|
# --- 電荷とラジカルの描画 ---
|
|
@@ -316,9 +365,7 @@ class AtomItem(QGraphicsItem):
|
|
|
316
365
|
painter.setPen(pen)
|
|
317
366
|
painter.drawRect(self.boundingRect())
|
|
318
367
|
|
|
319
|
-
|
|
320
|
-
self.is_visible = not (self.symbol == 'C' and len(self.bonds) > 0 and self.charge == 0 and self.radical == 0)
|
|
321
|
-
self.update()
|
|
368
|
+
|
|
322
369
|
|
|
323
370
|
|
|
324
371
|
# 約203行目 AtomItem クラス内
|
moleditpy/modules/bond_item.py
CHANGED
|
@@ -105,7 +105,21 @@ class BondItem(QGraphicsItem):
|
|
|
105
105
|
line = self.get_line_in_local_coords()
|
|
106
106
|
except Exception:
|
|
107
107
|
line = QLineF(0, 0, 0, 0)
|
|
108
|
-
|
|
108
|
+
|
|
109
|
+
# Get dynamic bond offset (spacing)
|
|
110
|
+
bond_offset = 3.5
|
|
111
|
+
try:
|
|
112
|
+
if self.scene() and hasattr(self.scene(), 'views') and self.scene().views():
|
|
113
|
+
win = self.scene().views()[0].window()
|
|
114
|
+
if win and hasattr(win, 'settings'):
|
|
115
|
+
# Use specific spacing based on bond order
|
|
116
|
+
if getattr(self, 'order', 1) == 3:
|
|
117
|
+
bond_offset = win.settings.get('bond_spacing_triple_2d', 3.5)
|
|
118
|
+
else:
|
|
119
|
+
bond_offset = win.settings.get('bond_spacing_double_2d', 3.5)
|
|
120
|
+
except Exception:
|
|
121
|
+
bond_offset = globals().get('BOND_OFFSET', 3.5)
|
|
122
|
+
|
|
109
123
|
extra = (getattr(self, 'order', 1) - 1) * bond_offset + 20
|
|
110
124
|
rect = QRectF(line.p1(), line.p2()).normalized().adjusted(-extra, -extra, extra, extra)
|
|
111
125
|
|
|
@@ -142,7 +156,18 @@ class BondItem(QGraphicsItem):
|
|
|
142
156
|
view = scene.views()[0]
|
|
143
157
|
scale = view.transform().m11()
|
|
144
158
|
|
|
145
|
-
|
|
159
|
+
# Dynamic bond width
|
|
160
|
+
width_2d = 2.0
|
|
161
|
+
try:
|
|
162
|
+
if view.window() and hasattr(view.window(), 'settings'):
|
|
163
|
+
width_2d = view.window().settings.get('bond_width_2d', 2.0)
|
|
164
|
+
except Exception:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
# Hit area should be roughly closely matched or slightly larger than visual
|
|
168
|
+
# Ensure minimum hit width for usability
|
|
169
|
+
scene_width = max(DESIRED_BOND_PIXEL_WIDTH, width_2d * 10) / scale
|
|
170
|
+
|
|
146
171
|
|
|
147
172
|
stroker = QPainterPathStroker()
|
|
148
173
|
stroker.setWidth(scene_width)
|
|
@@ -160,24 +185,44 @@ class BondItem(QGraphicsItem):
|
|
|
160
185
|
line = self.get_line_in_local_coords()
|
|
161
186
|
if line.length() == 0: return
|
|
162
187
|
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
188
|
+
# Allow bond color override from app settings (2D color)
|
|
189
|
+
width_2d = 2.0
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
sc = self.scene()
|
|
193
|
+
if sc is not None and hasattr(sc, 'window') and sc.window is not None:
|
|
194
|
+
# Get settings
|
|
195
|
+
settings = sc.window.settings
|
|
196
|
+
|
|
197
|
+
# Width
|
|
198
|
+
width_2d = settings.get('bond_width_2d', 2.0)
|
|
199
|
+
|
|
200
|
+
# Cap Style logic
|
|
201
|
+
cap_style_str = settings.get('bond_cap_style_2d', 'Round')
|
|
202
|
+
cap_style = Qt.PenCapStyle.RoundCap # Default
|
|
203
|
+
|
|
204
|
+
if cap_style_str == 'Flat':
|
|
205
|
+
cap_style = Qt.PenCapStyle.FlatCap
|
|
206
|
+
elif cap_style_str == 'Square':
|
|
207
|
+
cap_style = Qt.PenCapStyle.SquareCap
|
|
208
|
+
|
|
209
|
+
# Color
|
|
210
|
+
if self.isSelected():
|
|
211
|
+
bond_color = QColor("blue") # Selection color
|
|
176
212
|
else:
|
|
177
|
-
|
|
178
|
-
|
|
213
|
+
bond_hex = settings.get('bond_color_2d', '#222222')
|
|
214
|
+
bond_color = QColor(bond_hex)
|
|
215
|
+
|
|
216
|
+
pen = QPen(bond_color, width_2d)
|
|
217
|
+
pen.setCapStyle(cap_style)
|
|
218
|
+
painter.setPen(pen)
|
|
219
|
+
|
|
220
|
+
else:
|
|
179
221
|
painter.setPen(self.pen)
|
|
180
|
-
|
|
222
|
+
except Exception:
|
|
223
|
+
painter.setPen(self.pen)
|
|
224
|
+
|
|
225
|
+
painter.setBrush(QBrush(Qt.GlobalColor.black))
|
|
181
226
|
|
|
182
227
|
# --- 立体化学 (Wedge/Dash) の描画 ---
|
|
183
228
|
if self.order == 1 and self.stereo in [1, 2]:
|
|
@@ -213,7 +258,19 @@ class BondItem(QGraphicsItem):
|
|
|
213
258
|
painter.drawLine(line)
|
|
214
259
|
else:
|
|
215
260
|
v = line.unitVector().normalVector()
|
|
216
|
-
|
|
261
|
+
# Use dynamic offset
|
|
262
|
+
bond_offset = 3.5
|
|
263
|
+
try:
|
|
264
|
+
sc = self.scene()
|
|
265
|
+
if sc and sc.views() and hasattr(sc.views()[0].window(), 'settings'):
|
|
266
|
+
if self.order == 3:
|
|
267
|
+
bond_offset = sc.views()[0].window().settings.get('bond_spacing_triple_2d', 3.5)
|
|
268
|
+
else:
|
|
269
|
+
bond_offset = sc.views()[0].window().settings.get('bond_spacing_double_2d', 3.5)
|
|
270
|
+
except Exception:
|
|
271
|
+
bond_offset = globals().get('BOND_OFFSET', 3.5)
|
|
272
|
+
|
|
273
|
+
offset = QPointF(v.dx(), v.dy()) * bond_offset
|
|
217
274
|
|
|
218
275
|
if self.order == 2:
|
|
219
276
|
# 環構造かどうかを判定し、描画方法を変更
|
|
@@ -274,7 +331,8 @@ class BondItem(QGraphicsItem):
|
|
|
274
331
|
is_in_ring = False
|
|
275
332
|
|
|
276
333
|
v = line.unitVector().normalVector()
|
|
277
|
-
offset
|
|
334
|
+
# Re-calculate offset in case loop variable scope issue, though strictly not needed if offset defined above works
|
|
335
|
+
offset = QPointF(v.dx(), v.dy()) * bond_offset
|
|
278
336
|
|
|
279
337
|
if is_in_ring and ring_center:
|
|
280
338
|
# 環構造: 1本の中心線(単結合位置) + 1本の短い内側線
|
moleditpy/modules/constants.py
CHANGED
moleditpy/modules/main_window.py
CHANGED
|
@@ -500,6 +500,10 @@ class MainWindow(QMainWindow):
|
|
|
500
500
|
# --- MOVED TO main_window_export.py ---
|
|
501
501
|
return self.main_window_export.export_2d_png()
|
|
502
502
|
|
|
503
|
+
def export_2d_svg(self):
|
|
504
|
+
# --- MOVED TO main_window_export.py ---
|
|
505
|
+
return self.main_window_export.export_2d_svg()
|
|
506
|
+
|
|
503
507
|
def export_3d_png(self):
|
|
504
508
|
# --- MOVED TO main_window_export.py ---
|
|
505
509
|
return self.main_window_export.export_3d_png()
|
|
@@ -524,6 +528,10 @@ class MainWindow(QMainWindow):
|
|
|
524
528
|
# --- MOVED TO main_window_edit_actions.py ---
|
|
525
529
|
return self.main_window_edit_actions.adjust_molecule_positions_to_avoid_collisions(mol, frags)
|
|
526
530
|
|
|
531
|
+
def open_rotate_2d_dialog(self):
|
|
532
|
+
# --- MOVED TO main_window_edit_actions.py ---
|
|
533
|
+
return self.main_window_edit_actions.open_rotate_2d_dialog()
|
|
534
|
+
|
|
527
535
|
def draw_molecule_3d(self, mol):
|
|
528
536
|
# --- MOVED TO main_window_view_3d.py ---
|
|
529
537
|
return self.main_window_view_3d.draw_molecule_3d(mol)
|
|
@@ -34,7 +34,7 @@ from rdkit.Chem import AllChem
|
|
|
34
34
|
|
|
35
35
|
# PyQt6 Modules
|
|
36
36
|
from PyQt6.QtWidgets import (
|
|
37
|
-
QApplication
|
|
37
|
+
QApplication, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QSpinBox, QSlider, QPushButton
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
from PyQt6.QtGui import (
|
|
@@ -43,9 +43,51 @@ from PyQt6.QtGui import (
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
from PyQt6.QtCore import (
|
|
46
|
-
QPointF, QLineF, QMimeData, QByteArray, QTimer
|
|
46
|
+
QPointF, QLineF, QMimeData, QByteArray, QTimer, Qt
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
+
class Rotate2DDialog(QDialog):
|
|
50
|
+
def __init__(self, parent=None):
|
|
51
|
+
super().__init__(parent)
|
|
52
|
+
self.setWindowTitle("Rotate 2D")
|
|
53
|
+
self.setFixedWidth(300)
|
|
54
|
+
|
|
55
|
+
layout = QVBoxLayout(self)
|
|
56
|
+
|
|
57
|
+
# Angle input
|
|
58
|
+
input_layout = QHBoxLayout()
|
|
59
|
+
input_layout.addWidget(QLabel("Angle (degrees):"))
|
|
60
|
+
self.angle_spin = QSpinBox()
|
|
61
|
+
self.angle_spin.setRange(-360, 360)
|
|
62
|
+
self.angle_spin.setValue(45)
|
|
63
|
+
input_layout.addWidget(self.angle_spin)
|
|
64
|
+
layout.addLayout(input_layout)
|
|
65
|
+
|
|
66
|
+
# Slider
|
|
67
|
+
self.slider = QSlider(Qt.Orientation.Horizontal)
|
|
68
|
+
self.slider.setRange(-180, 180)
|
|
69
|
+
self.slider.setValue(45)
|
|
70
|
+
self.slider.setTickPosition(QSlider.TickPosition.TicksBelow)
|
|
71
|
+
self.slider.setTickInterval(15)
|
|
72
|
+
layout.addWidget(self.slider)
|
|
73
|
+
|
|
74
|
+
# Sync slider and spinbox
|
|
75
|
+
self.angle_spin.valueChanged.connect(self.slider.setValue)
|
|
76
|
+
self.slider.valueChanged.connect(self.angle_spin.setValue)
|
|
77
|
+
|
|
78
|
+
# Buttons
|
|
79
|
+
btn_layout = QHBoxLayout()
|
|
80
|
+
ok_btn = QPushButton("Rotate")
|
|
81
|
+
ok_btn.clicked.connect(self.accept)
|
|
82
|
+
cancel_btn = QPushButton("Cancel")
|
|
83
|
+
cancel_btn.clicked.connect(self.reject)
|
|
84
|
+
btn_layout.addWidget(ok_btn)
|
|
85
|
+
btn_layout.addWidget(cancel_btn)
|
|
86
|
+
layout.addLayout(btn_layout)
|
|
87
|
+
|
|
88
|
+
def get_angle(self):
|
|
89
|
+
return self.angle_spin.value()
|
|
90
|
+
|
|
49
91
|
|
|
50
92
|
# Use centralized Open Babel availability from package-level __init__
|
|
51
93
|
# Use per-package modules availability (local __init__).
|
|
@@ -568,6 +610,67 @@ class MainWindowEditActions(object):
|
|
|
568
610
|
|
|
569
611
|
|
|
570
612
|
|
|
613
|
+
def open_rotate_2d_dialog(self):
|
|
614
|
+
"""2D回転ダイアログを開く"""
|
|
615
|
+
dialog = Rotate2DDialog(self)
|
|
616
|
+
if dialog.exec() == QDialog.DialogCode.Accepted:
|
|
617
|
+
angle = dialog.get_angle()
|
|
618
|
+
self.rotate_molecule_2d(angle)
|
|
619
|
+
|
|
620
|
+
def rotate_molecule_2d(self, angle_degrees):
|
|
621
|
+
"""2D分子を指定角度回転させる(選択範囲があればそれのみ、なければ全体)"""
|
|
622
|
+
try:
|
|
623
|
+
# Determine target atoms
|
|
624
|
+
selected_items = self.scene.selectedItems()
|
|
625
|
+
target_atoms = [item for item in selected_items if isinstance(item, AtomItem)]
|
|
626
|
+
|
|
627
|
+
# If no selection, rotate everything
|
|
628
|
+
if not target_atoms:
|
|
629
|
+
target_atoms = [data['item'] for data in self.data.atoms.values() if data.get('item') and not sip_isdeleted_safe(data['item'])]
|
|
630
|
+
|
|
631
|
+
if not target_atoms:
|
|
632
|
+
self.statusBar().showMessage("No atoms to rotate.")
|
|
633
|
+
return
|
|
634
|
+
|
|
635
|
+
# Calculate Center
|
|
636
|
+
xs = [atom.pos().x() for atom in target_atoms]
|
|
637
|
+
ys = [atom.pos().y() for atom in target_atoms]
|
|
638
|
+
if not xs: return
|
|
639
|
+
|
|
640
|
+
center_x = sum(xs) / len(xs)
|
|
641
|
+
center_y = sum(ys) / len(ys)
|
|
642
|
+
center = QPointF(center_x, center_y)
|
|
643
|
+
|
|
644
|
+
rad = math.radians(angle_degrees)
|
|
645
|
+
cos_a = math.cos(rad)
|
|
646
|
+
sin_a = math.sin(rad)
|
|
647
|
+
|
|
648
|
+
for atom in target_atoms:
|
|
649
|
+
# Relative pos
|
|
650
|
+
dx = atom.pos().x() - center_x
|
|
651
|
+
dy = atom.pos().y() - center_y
|
|
652
|
+
|
|
653
|
+
# Rotate
|
|
654
|
+
new_dx = dx * cos_a - dy * sin_a
|
|
655
|
+
new_dy = dx * sin_a + dy * cos_a
|
|
656
|
+
|
|
657
|
+
new_pos = QPointF(center_x + new_dx, center_y + new_dy)
|
|
658
|
+
atom.setPos(new_pos)
|
|
659
|
+
|
|
660
|
+
# Update bonds
|
|
661
|
+
self.scene.update_connected_bonds(target_atoms)
|
|
662
|
+
|
|
663
|
+
self.push_undo_state()
|
|
664
|
+
self.statusBar().showMessage(f"Rotated {len(target_atoms)} atoms by {angle_degrees} degrees.")
|
|
665
|
+
self.scene.update()
|
|
666
|
+
|
|
667
|
+
except Exception as e:
|
|
668
|
+
print(f"Error rotating molecule: {e}")
|
|
669
|
+
traceback.print_exc()
|
|
670
|
+
self.statusBar().showMessage(f"Error rotating: {e}")
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
|
|
571
674
|
|
|
572
675
|
def select_all(self):
|
|
573
676
|
for item in self.scene.items():
|
|
@@ -37,9 +37,11 @@ from PyQt6.QtGui import (
|
|
|
37
37
|
QBrush, QColor, QPainter, QImage
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
+
from PyQt6.QtSvg import QSvgGenerator
|
|
41
|
+
|
|
40
42
|
|
|
41
43
|
from PyQt6.QtCore import (
|
|
42
|
-
Qt, QRectF
|
|
44
|
+
Qt, QRectF, QSize
|
|
43
45
|
)
|
|
44
46
|
|
|
45
47
|
import pyvista as pv
|
|
@@ -672,7 +674,7 @@ class MainWindowExport(object):
|
|
|
672
674
|
filePath += ".png"
|
|
673
675
|
|
|
674
676
|
reply = QMessageBox.question(self, 'Choose Background',
|
|
675
|
-
'Do you want a transparent background?\n(Choose "No"
|
|
677
|
+
'Do you want a transparent background?\n(Choose "No" to use the current background color)',
|
|
676
678
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel,
|
|
677
679
|
QMessageBox.StandardButton.Yes)
|
|
678
680
|
|
|
@@ -706,8 +708,7 @@ class MainWindowExport(object):
|
|
|
706
708
|
|
|
707
709
|
if is_transparent:
|
|
708
710
|
self.scene.setBackgroundBrush(QBrush(Qt.BrushStyle.NoBrush))
|
|
709
|
-
|
|
710
|
-
self.scene.setBackgroundBrush(QBrush(QColor("#FFFFFF")))
|
|
711
|
+
# Else: keep original_background (current 2D background)
|
|
711
712
|
|
|
712
713
|
rect_to_render = molecule_bounds.adjusted(-20, -20, 20, 20)
|
|
713
714
|
|
|
@@ -719,10 +720,8 @@ class MainWindowExport(object):
|
|
|
719
720
|
return
|
|
720
721
|
|
|
721
722
|
image = QImage(w, h, QImage.Format.Format_ARGB32_Premultiplied)
|
|
722
|
-
if
|
|
723
|
-
|
|
724
|
-
else:
|
|
725
|
-
image.fill(Qt.GlobalColor.white)
|
|
723
|
+
# Always fill with transparent; render will paint opaque background if present
|
|
724
|
+
image.fill(Qt.GlobalColor.transparent)
|
|
726
725
|
|
|
727
726
|
painter = QPainter()
|
|
728
727
|
ok = painter.begin(image)
|
|
@@ -756,6 +755,118 @@ class MainWindowExport(object):
|
|
|
756
755
|
|
|
757
756
|
|
|
758
757
|
|
|
758
|
+
def export_2d_svg(self):
|
|
759
|
+
"""2D drawingをSVGとしてエクスポート"""
|
|
760
|
+
if not self.data.atoms:
|
|
761
|
+
self.statusBar().showMessage("Nothing to export.")
|
|
762
|
+
return
|
|
763
|
+
|
|
764
|
+
# default filename
|
|
765
|
+
default_name = "untitled-2d"
|
|
766
|
+
try:
|
|
767
|
+
if self.current_file_path:
|
|
768
|
+
base = os.path.basename(self.current_file_path)
|
|
769
|
+
name = os.path.splitext(base)[0]
|
|
770
|
+
default_name = f"{name}-2d"
|
|
771
|
+
except Exception:
|
|
772
|
+
default_name = "untitled-2d"
|
|
773
|
+
|
|
774
|
+
# prefer same directory
|
|
775
|
+
default_path = default_name
|
|
776
|
+
try:
|
|
777
|
+
if self.current_file_path:
|
|
778
|
+
default_path = os.path.join(os.path.dirname(self.current_file_path), default_name)
|
|
779
|
+
except Exception:
|
|
780
|
+
default_path = default_name
|
|
781
|
+
|
|
782
|
+
filePath, _ = QFileDialog.getSaveFileName(self, "Export 2D as SVG", default_path, "SVG Files (*.svg)")
|
|
783
|
+
if not filePath:
|
|
784
|
+
return
|
|
785
|
+
|
|
786
|
+
if not (filePath.lower().endswith(".svg")):
|
|
787
|
+
filePath += ".svg"
|
|
788
|
+
|
|
789
|
+
# Ask about transparency
|
|
790
|
+
reply = QMessageBox.question(self, 'Choose Background',
|
|
791
|
+
'Do you want a transparent background?\n(Choose "No" to use the current background color)',
|
|
792
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel,
|
|
793
|
+
QMessageBox.StandardButton.Yes)
|
|
794
|
+
|
|
795
|
+
if reply == QMessageBox.StandardButton.Cancel:
|
|
796
|
+
self.statusBar().showMessage("Export cancelled.", 2000)
|
|
797
|
+
return
|
|
798
|
+
|
|
799
|
+
is_transparent = (reply == QMessageBox.StandardButton.Yes)
|
|
800
|
+
|
|
801
|
+
try:
|
|
802
|
+
# 1. Hide non-molecular items if needed (optional, keeping consistent with PNG export)
|
|
803
|
+
items_to_restore = {}
|
|
804
|
+
original_background = self.scene.backgroundBrush()
|
|
805
|
+
|
|
806
|
+
all_items = list(self.scene.items())
|
|
807
|
+
for item in all_items:
|
|
808
|
+
is_mol_part = isinstance(item, (AtomItem, BondItem))
|
|
809
|
+
if not (is_mol_part and item.isVisible()):
|
|
810
|
+
# Keep measurement items visible if they are part of the scene?
|
|
811
|
+
# For now, let's stick to hiding everything that isn't atom/bond,
|
|
812
|
+
# similar to png export logic, or we can decide to export everything visible.
|
|
813
|
+
# The PNG export hides non-atom/bond items. Let's follow that for consistency.
|
|
814
|
+
items_to_restore[item] = item.isVisible()
|
|
815
|
+
item.hide()
|
|
816
|
+
|
|
817
|
+
# 2. Calculate bounds
|
|
818
|
+
molecule_bounds = QRectF()
|
|
819
|
+
for item in self.scene.items():
|
|
820
|
+
if isinstance(item, (AtomItem, BondItem)) and item.isVisible():
|
|
821
|
+
molecule_bounds = molecule_bounds.united(item.sceneBoundingRect())
|
|
822
|
+
|
|
823
|
+
if molecule_bounds.isEmpty() or not molecule_bounds.isValid():
|
|
824
|
+
self.statusBar().showMessage("Error: Could not determine molecule bounds for export.")
|
|
825
|
+
# Restore
|
|
826
|
+
for item, was_visible in items_to_restore.items():
|
|
827
|
+
item.setVisible(was_visible)
|
|
828
|
+
return
|
|
829
|
+
|
|
830
|
+
if is_transparent:
|
|
831
|
+
self.scene.setBackgroundBrush(QBrush(Qt.BrushStyle.NoBrush))
|
|
832
|
+
|
|
833
|
+
# Margin
|
|
834
|
+
rect_to_render = molecule_bounds.adjusted(-20, -20, 20, 20)
|
|
835
|
+
|
|
836
|
+
width = int(rect_to_render.width())
|
|
837
|
+
height = int(rect_to_render.height())
|
|
838
|
+
|
|
839
|
+
# 3. Setup QSvgGenerator
|
|
840
|
+
generator = QSvgGenerator()
|
|
841
|
+
generator.setFileName(filePath)
|
|
842
|
+
generator.setSize(QSize(width, height))
|
|
843
|
+
generator.setViewBox(rect_to_render)
|
|
844
|
+
generator.setTitle("MoleditPy Molecule")
|
|
845
|
+
|
|
846
|
+
# 4. Render
|
|
847
|
+
painter = QPainter()
|
|
848
|
+
painter.begin(generator)
|
|
849
|
+
try:
|
|
850
|
+
self.scene.render(painter, rect_to_render, rect_to_render)
|
|
851
|
+
finally:
|
|
852
|
+
painter.end()
|
|
853
|
+
|
|
854
|
+
self.statusBar().showMessage(f"2D view exported to {filePath}")
|
|
855
|
+
|
|
856
|
+
except Exception as e:
|
|
857
|
+
self.statusBar().showMessage(f"An unexpected error occurred during SVG export: {e}")
|
|
858
|
+
|
|
859
|
+
finally:
|
|
860
|
+
# Restore
|
|
861
|
+
for item, was_visible in items_to_restore.items():
|
|
862
|
+
item.setVisible(was_visible)
|
|
863
|
+
if 'original_background' in locals():
|
|
864
|
+
self.scene.setBackgroundBrush(original_background)
|
|
865
|
+
if self.view_2d:
|
|
866
|
+
self.view_2d.viewport().update()
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
|
|
759
870
|
def export_3d_png(self):
|
|
760
871
|
if not self.current_mol:
|
|
761
872
|
self.statusBar().showMessage("No 3D molecule to export.", 2000)
|
|
@@ -372,7 +372,7 @@ class MainWindowMainInit(object):
|
|
|
372
372
|
|
|
373
373
|
# --- 左パネルのボタンレイアウト ---
|
|
374
374
|
left_buttons_layout = QHBoxLayout()
|
|
375
|
-
self.cleanup_button = QPushButton("
|
|
375
|
+
self.cleanup_button = QPushButton("Clean Up 2D")
|
|
376
376
|
self.cleanup_button.clicked.connect(self.clean_up_2d_structure)
|
|
377
377
|
left_buttons_layout.addWidget(self.cleanup_button)
|
|
378
378
|
|
|
@@ -920,6 +920,10 @@ class MainWindowMainInit(object):
|
|
|
920
920
|
export_2d_png_action = QAction("PNG Image...", self)
|
|
921
921
|
export_2d_png_action.triggered.connect(self.export_2d_png)
|
|
922
922
|
export_2d_menu.addAction(export_2d_png_action)
|
|
923
|
+
|
|
924
|
+
export_2d_svg_action = QAction("SVG Image...", self)
|
|
925
|
+
export_2d_svg_action.triggered.connect(self.export_2d_svg)
|
|
926
|
+
export_2d_menu.addAction(export_2d_svg_action)
|
|
923
927
|
|
|
924
928
|
# 3D エクスポート
|
|
925
929
|
export_3d_menu = export_menu.addMenu("3D Formats")
|
|
@@ -987,7 +991,13 @@ class MainWindowMainInit(object):
|
|
|
987
991
|
|
|
988
992
|
edit_menu.addSeparator()
|
|
989
993
|
|
|
990
|
-
|
|
994
|
+
rotate_2d_action = QAction("Rotate 2D...", self)
|
|
995
|
+
rotate_2d_action.triggered.connect(self.open_rotate_2d_dialog)
|
|
996
|
+
edit_menu.addAction(rotate_2d_action)
|
|
997
|
+
|
|
998
|
+
edit_menu.addSeparator()
|
|
999
|
+
|
|
1000
|
+
optimize_2d_action = QAction("Clean Up 2D", self)
|
|
991
1001
|
optimize_2d_action.setShortcut(QKeySequence("Ctrl+J"))
|
|
992
1002
|
optimize_2d_action.triggered.connect(self.clean_up_2d_structure)
|
|
993
1003
|
edit_menu.addAction(optimize_2d_action)
|
|
@@ -1247,7 +1257,7 @@ class MainWindowMainInit(object):
|
|
|
1247
1257
|
|
|
1248
1258
|
settings_menu = menu_bar.addMenu("&Settings")
|
|
1249
1259
|
# 1) 3D View settings (existing)
|
|
1250
|
-
view_settings_action = QAction("
|
|
1260
|
+
view_settings_action = QAction("Settings...", self)
|
|
1251
1261
|
view_settings_action.triggered.connect(self.open_settings_dialog)
|
|
1252
1262
|
settings_menu.addAction(view_settings_action)
|
|
1253
1263
|
|
|
@@ -1495,6 +1505,10 @@ class MainWindowMainInit(object):
|
|
|
1495
1505
|
|
|
1496
1506
|
try:
|
|
1497
1507
|
if hasattr(self, 'scene') and self.scene:
|
|
1508
|
+
# Apply 2D background color
|
|
1509
|
+
bg_color_2d = self.settings.get('background_color_2d', '#FFFFFF')
|
|
1510
|
+
self.scene.setBackgroundBrush(QBrush(QColor(bg_color_2d)))
|
|
1511
|
+
|
|
1498
1512
|
for it in list(self.scene.items()):
|
|
1499
1513
|
if hasattr(it, 'update_style'):
|
|
1500
1514
|
it.update_style()
|
|
@@ -1587,6 +1601,15 @@ class MainWindowMainInit(object):
|
|
|
1587
1601
|
except Exception:
|
|
1588
1602
|
pass
|
|
1589
1603
|
# Update 2D scene styling to reflect default CPK colors
|
|
1604
|
+
try:
|
|
1605
|
+
# Reset 2D background specifically
|
|
1606
|
+
if hasattr(self, 'scene') and self.scene:
|
|
1607
|
+
bg_c = self.settings.get('background_color_2d', '#FFFFFF')
|
|
1608
|
+
self.scene.setBackgroundBrush(QBrush(QColor(bg_c)))
|
|
1609
|
+
|
|
1610
|
+
self.update_cpk_colors_from_settings()
|
|
1611
|
+
except Exception:
|
|
1612
|
+
pass
|
|
1590
1613
|
try:
|
|
1591
1614
|
if hasattr(self, 'scene') and self.scene:
|
|
1592
1615
|
for it in list(self.scene.items()):
|
|
@@ -1674,6 +1697,18 @@ class MainWindowMainInit(object):
|
|
|
1674
1697
|
# Whether to kekulize aromatic systems for 3D display
|
|
1675
1698
|
'display_kekule_3d': False,
|
|
1676
1699
|
'always_ask_charge': False,
|
|
1700
|
+
'display_aromatic_circles_3d': False,
|
|
1701
|
+
'ball_stick_use_cpk_bond_color': False,
|
|
1702
|
+
|
|
1703
|
+
# --- 2D Settings Defaults ---
|
|
1704
|
+
'bond_width_2d': 2.0,
|
|
1705
|
+
'bond_spacing_double_2d': 3.5,
|
|
1706
|
+
'bond_spacing_triple_2d': 3.5,
|
|
1707
|
+
'atom_font_size_2d': 20,
|
|
1708
|
+
'background_color_2d': '#FFFFFF',
|
|
1709
|
+
'bond_color_2d': '#222222', # Almost black
|
|
1710
|
+
'atom_use_bond_color_2d': False,
|
|
1711
|
+
'bond_cap_style_2d': 'Round',
|
|
1677
1712
|
}
|
|
1678
1713
|
|
|
1679
1714
|
try:
|
|
@@ -26,8 +26,8 @@ except Exception:
|
|
|
26
26
|
class SettingsDialog(QDialog):
|
|
27
27
|
def __init__(self, current_settings, parent=None):
|
|
28
28
|
super().__init__(parent)
|
|
29
|
-
self.setWindowTitle("
|
|
30
|
-
self.setMinimumSize(
|
|
29
|
+
self.setWindowTitle("Settings")
|
|
30
|
+
self.setMinimumSize(700, 800)
|
|
31
31
|
|
|
32
32
|
# 親ウィンドウの参照を保存(Apply機能のため)
|
|
33
33
|
self.parent_window = parent
|
|
@@ -89,6 +89,16 @@ class SettingsDialog(QDialog):
|
|
|
89
89
|
# (shows alternating single/double bonds rather than aromatic circles)
|
|
90
90
|
'display_kekule_3d': False,
|
|
91
91
|
'ball_stick_use_cpk_bond_color': False,
|
|
92
|
+
|
|
93
|
+
# --- 2D Settings Defaults ---
|
|
94
|
+
'bond_width_2d': 2.0,
|
|
95
|
+
'bond_spacing_double_2d': 3.5,
|
|
96
|
+
'bond_spacing_triple_2d': 3.5,
|
|
97
|
+
'atom_font_size_2d': 20,
|
|
98
|
+
'background_color_2d': '#FFFFFF',
|
|
99
|
+
'bond_color_2d': '#222222', # Almost black
|
|
100
|
+
'atom_use_bond_color_2d': False,
|
|
101
|
+
'bond_cap_style_2d': 'Round',
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
# --- 選択された色を管理する専用のインスタンス変数 ---
|
|
@@ -101,6 +111,9 @@ class SettingsDialog(QDialog):
|
|
|
101
111
|
self.tab_widget = QTabWidget()
|
|
102
112
|
layout.addWidget(self.tab_widget)
|
|
103
113
|
|
|
114
|
+
# 2D Settings Tab (First)
|
|
115
|
+
self.create_2d_settings_tab()
|
|
116
|
+
|
|
104
117
|
# Scene設定タブ
|
|
105
118
|
self.create_scene_tab()
|
|
106
119
|
|
|
@@ -129,6 +142,13 @@ class SettingsDialog(QDialog):
|
|
|
129
142
|
self.aromatic_torus_thickness_slider.setValue(int(thickness_factor * 100))
|
|
130
143
|
self.aromatic_torus_thickness_label.setText(f"{thickness_factor:.1f}")
|
|
131
144
|
|
|
145
|
+
# Initialize internal 2D colors
|
|
146
|
+
self.current_bg_color_2d = current_settings.get('background_color_2d', self.default_settings['background_color_2d'])
|
|
147
|
+
self.current_bond_color_2d = current_settings.get('bond_color_2d', self.default_settings['bond_color_2d'])
|
|
148
|
+
|
|
149
|
+
# Apply initial 2D button styles
|
|
150
|
+
self.update_2d_color_buttons()
|
|
151
|
+
|
|
132
152
|
# --- ボタンの配置 ---
|
|
133
153
|
buttons = QHBoxLayout()
|
|
134
154
|
|
|
@@ -160,6 +180,121 @@ class SettingsDialog(QDialog):
|
|
|
160
180
|
buttons.addWidget(ok_button)
|
|
161
181
|
buttons.addWidget(cancel_button)
|
|
162
182
|
layout.addLayout(buttons)
|
|
183
|
+
|
|
184
|
+
def create_2d_settings_tab(self):
|
|
185
|
+
"""2D Settings Tab"""
|
|
186
|
+
widget = QWidget()
|
|
187
|
+
form_layout = QFormLayout(widget)
|
|
188
|
+
|
|
189
|
+
# --- View Settings ---
|
|
190
|
+
form_layout.addRow(QLabel("<b>View Appearance</b>"))
|
|
191
|
+
|
|
192
|
+
# Background Color
|
|
193
|
+
self.bg_color_2d_button = QPushButton()
|
|
194
|
+
self.bg_color_2d_button.setFixedSize(60, 24)
|
|
195
|
+
self.bg_color_2d_button.clicked.connect(self.pick_bg_color_2d)
|
|
196
|
+
form_layout.addRow("Background Color:", self.bg_color_2d_button)
|
|
197
|
+
|
|
198
|
+
line = QFrame()
|
|
199
|
+
line.setFrameShape(QFrame.Shape.HLine)
|
|
200
|
+
line.setFrameShadow(QFrame.Shadow.Sunken)
|
|
201
|
+
form_layout.addRow(line)
|
|
202
|
+
|
|
203
|
+
# --- Bond Settings ---
|
|
204
|
+
form_layout.addRow(QLabel("<b>Bond Settings</b>"))
|
|
205
|
+
|
|
206
|
+
# Bond Color
|
|
207
|
+
self.bond_color_2d_button = QPushButton()
|
|
208
|
+
self.bond_color_2d_button.setFixedSize(60, 24)
|
|
209
|
+
self.bond_color_2d_button.clicked.connect(self.pick_bond_color_2d)
|
|
210
|
+
form_layout.addRow("Bond Color:", self.bond_color_2d_button)
|
|
211
|
+
|
|
212
|
+
# Bond Width
|
|
213
|
+
self.bond_width_2d_slider = QSlider(Qt.Orientation.Horizontal)
|
|
214
|
+
self.bond_width_2d_slider.setRange(10, 200) # 1.0 - 20.0
|
|
215
|
+
self.bond_width_2d_label = QLabel("2.0")
|
|
216
|
+
self.bond_width_2d_slider.valueChanged.connect(lambda v: self.bond_width_2d_label.setText(f"{v/10:.1f}"))
|
|
217
|
+
bw_layout = QHBoxLayout()
|
|
218
|
+
bw_layout.addWidget(self.bond_width_2d_slider)
|
|
219
|
+
bw_layout.addWidget(self.bond_width_2d_label)
|
|
220
|
+
form_layout.addRow("Bond Width:", bw_layout)
|
|
221
|
+
|
|
222
|
+
# Double Bond Spacing
|
|
223
|
+
self.bond_spacing_double_2d_slider = QSlider(Qt.Orientation.Horizontal)
|
|
224
|
+
self.bond_spacing_double_2d_slider.setRange(10, 200) # 1.0 - 20.0
|
|
225
|
+
self.bond_spacing_double_2d_label = QLabel("3.5")
|
|
226
|
+
self.bond_spacing_double_2d_slider.valueChanged.connect(lambda v: self.bond_spacing_double_2d_label.setText(f"{v/10:.1f}"))
|
|
227
|
+
bsd_layout = QHBoxLayout()
|
|
228
|
+
bsd_layout.addWidget(self.bond_spacing_double_2d_slider)
|
|
229
|
+
bsd_layout.addWidget(self.bond_spacing_double_2d_label)
|
|
230
|
+
form_layout.addRow("Double Bond Spacing:", bsd_layout)
|
|
231
|
+
|
|
232
|
+
# Triple Bond Spacing
|
|
233
|
+
self.bond_spacing_triple_2d_slider = QSlider(Qt.Orientation.Horizontal)
|
|
234
|
+
self.bond_spacing_triple_2d_slider.setRange(10, 200) # 1.0 - 20.0
|
|
235
|
+
self.bond_spacing_triple_2d_label = QLabel("3.5")
|
|
236
|
+
self.bond_spacing_triple_2d_slider.valueChanged.connect(lambda v: self.bond_spacing_triple_2d_label.setText(f"{v/10:.1f}"))
|
|
237
|
+
bst_layout = QHBoxLayout()
|
|
238
|
+
bst_layout.addWidget(self.bond_spacing_triple_2d_slider)
|
|
239
|
+
bst_layout.addWidget(self.bond_spacing_triple_2d_label)
|
|
240
|
+
form_layout.addRow("Triple Bond Spacing:", bst_layout)
|
|
241
|
+
|
|
242
|
+
# Bond Cap Style
|
|
243
|
+
self.bond_cap_style_2d_combo = QComboBox()
|
|
244
|
+
self.bond_cap_style_2d_combo.addItems(['Round', 'Flat', 'Square'])
|
|
245
|
+
form_layout.addRow("Bond Cap Style:", self.bond_cap_style_2d_combo)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
line2 = QFrame()
|
|
249
|
+
line2.setFrameShape(QFrame.Shape.HLine)
|
|
250
|
+
line2.setFrameShadow(QFrame.Shadow.Sunken)
|
|
251
|
+
form_layout.addRow(line2)
|
|
252
|
+
|
|
253
|
+
# --- Atom Settings ---
|
|
254
|
+
form_layout.addRow(QLabel("<b>Atom Settings</b>"))
|
|
255
|
+
|
|
256
|
+
# Font Size
|
|
257
|
+
self.atom_font_size_2d_slider = QSlider(Qt.Orientation.Horizontal)
|
|
258
|
+
self.atom_font_size_2d_slider.setRange(8, 72)
|
|
259
|
+
self.atom_font_size_2d_label = QLabel("20")
|
|
260
|
+
self.atom_font_size_2d_slider.valueChanged.connect(lambda v: self.atom_font_size_2d_label.setText(str(v)))
|
|
261
|
+
fs_layout = QHBoxLayout()
|
|
262
|
+
fs_layout.addWidget(self.atom_font_size_2d_slider)
|
|
263
|
+
fs_layout.addWidget(self.atom_font_size_2d_label)
|
|
264
|
+
form_layout.addRow("Atom Label Font Size:", fs_layout)
|
|
265
|
+
|
|
266
|
+
# Use Bond Color Checkbox
|
|
267
|
+
self.atom_use_bond_color_2d_checkbox = QCheckBox()
|
|
268
|
+
self.atom_use_bond_color_2d_checkbox.setToolTip("If checked, atoms will use the unified Bond Color instead of element-specific colors (CPK).")
|
|
269
|
+
form_layout.addRow("Use Bond Color for Atoms:", self.atom_use_bond_color_2d_checkbox)
|
|
270
|
+
|
|
271
|
+
self.tab_widget.addTab(widget, "2D Settings")
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Initialize internal variable for combo updates
|
|
276
|
+
# (Usually done in update_ui_from_settings, but we add direct access here for simple binding if needed)
|
|
277
|
+
# We rely on update_ui_from_settings to set current selection.
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def pick_bg_color_2d(self):
|
|
281
|
+
color = QColorDialog.getColor(QColor(self.current_bg_color_2d), self, "Select 2D Background Color")
|
|
282
|
+
if color.isValid():
|
|
283
|
+
self.current_bg_color_2d = color.name()
|
|
284
|
+
self.update_2d_color_buttons()
|
|
285
|
+
|
|
286
|
+
def pick_bond_color_2d(self):
|
|
287
|
+
color = QColorDialog.getColor(QColor(self.current_bond_color_2d), self, "Select 2D Bond Color")
|
|
288
|
+
if color.isValid():
|
|
289
|
+
self.current_bond_color_2d = color.name()
|
|
290
|
+
self.update_2d_color_buttons()
|
|
291
|
+
|
|
292
|
+
def update_2d_color_buttons(self):
|
|
293
|
+
try:
|
|
294
|
+
self.bg_color_2d_button.setStyleSheet(f"background-color: {self.current_bg_color_2d}; border: 1px solid #888;")
|
|
295
|
+
self.bond_color_2d_button.setStyleSheet(f"background-color: {self.current_bond_color_2d}; border: 1px solid #888;")
|
|
296
|
+
except Exception:
|
|
297
|
+
pass
|
|
163
298
|
|
|
164
299
|
def create_scene_tab(self):
|
|
165
300
|
"""基本設定タブを作成"""
|
|
@@ -217,7 +352,7 @@ class SettingsDialog(QDialog):
|
|
|
217
352
|
self.projection_combo.setToolTip("Choose camera projection mode: Perspective (default) or Orthographic")
|
|
218
353
|
form_layout.addRow("Projection Mode:", self.projection_combo)
|
|
219
354
|
|
|
220
|
-
self.tab_widget.addTab(scene_widget, "Scene")
|
|
355
|
+
self.tab_widget.addTab(scene_widget, "3D Scene")
|
|
221
356
|
|
|
222
357
|
def create_other_tab(self):
|
|
223
358
|
"""other設定タブを作成"""
|
|
@@ -643,11 +778,22 @@ class SettingsDialog(QDialog):
|
|
|
643
778
|
tab_name = self.tab_widget.tabText(current_tab_index)
|
|
644
779
|
|
|
645
780
|
# 各タブの設定項目を定義
|
|
646
|
-
#
|
|
781
|
+
# Each tab settings
|
|
782
|
+
# Note: tab labels must match those added to the QTabWidget ("2D Settings", "Scene", "Ball & Stick", ...
|
|
647
783
|
# "CPK (Space-filling)", "Wireframe", "Stick", "Other"). Use the per-model
|
|
648
784
|
# multi-bond keys present in self.default_settings.
|
|
649
785
|
tab_settings = {
|
|
650
|
-
"
|
|
786
|
+
"2D Settings": {
|
|
787
|
+
'bond_width_2d': self.default_settings['bond_width_2d'],
|
|
788
|
+
'bond_spacing_double_2d': self.default_settings['bond_spacing_double_2d'],
|
|
789
|
+
'bond_spacing_triple_2d': self.default_settings['bond_spacing_triple_2d'],
|
|
790
|
+
'atom_font_size_2d': self.default_settings['atom_font_size_2d'],
|
|
791
|
+
'background_color_2d': self.default_settings['background_color_2d'],
|
|
792
|
+
'bond_color_2d': self.default_settings['bond_color_2d'],
|
|
793
|
+
'atom_use_bond_color_2d': self.default_settings['atom_use_bond_color_2d'],
|
|
794
|
+
'bond_cap_style_2d': self.default_settings['bond_cap_style_2d']
|
|
795
|
+
},
|
|
796
|
+
"3D Scene": {
|
|
651
797
|
'background_color': self.default_settings['background_color'],
|
|
652
798
|
'projection_mode': self.default_settings['projection_mode'],
|
|
653
799
|
'show_3d_axes': self.default_settings['show_3d_axes'],
|
|
@@ -711,6 +857,13 @@ class SettingsDialog(QDialog):
|
|
|
711
857
|
|
|
712
858
|
# UIを更新
|
|
713
859
|
self.update_ui_from_settings(updated_settings)
|
|
860
|
+
|
|
861
|
+
# If 2D settings were reset, update internal color variables too
|
|
862
|
+
if tab_name == "2D Settings":
|
|
863
|
+
self.current_bg_color_2d = updated_settings.get('background_color_2d', self.default_settings['background_color_2d'])
|
|
864
|
+
self.current_bond_color_2d = updated_settings.get('bond_color_2d', self.default_settings['bond_color_2d'])
|
|
865
|
+
self.update_2d_color_buttons()
|
|
866
|
+
|
|
714
867
|
# CPK tab: do not change parent/settings immediately; let Apply/OK persist any changes
|
|
715
868
|
|
|
716
869
|
# ユーザーへのフィードバック
|
|
@@ -748,7 +901,9 @@ class SettingsDialog(QDialog):
|
|
|
748
901
|
if 'cpk_colors' in self.parent_window.settings:
|
|
749
902
|
# Reset to defaults (empty override dict)
|
|
750
903
|
self.parent_window.settings['cpk_colors'] = {}
|
|
751
|
-
|
|
904
|
+
|
|
905
|
+
# Reset 2D Settings (colors need manual refresh in parent scene/view usually handled by apply)
|
|
906
|
+
|
|
752
907
|
# Reset 3D Ball & Stick uniform bond color to default
|
|
753
908
|
self.parent_window.settings['ball_stick_bond_color'] = self.default_settings.get('ball_stick_bond_color', '#7F7F7F')
|
|
754
909
|
# Update global CPK colors and reapply 3D settings immediately
|
|
@@ -1063,9 +1218,17 @@ class SettingsDialog(QDialog):
|
|
|
1063
1218
|
'aromatic_torus_thickness_factor': self.aromatic_torus_thickness_slider.value() / 100.0,
|
|
1064
1219
|
'skip_chemistry_checks': self.skip_chem_checks_checkbox.isChecked(),
|
|
1065
1220
|
'always_ask_charge': self.always_ask_charge_checkbox.isChecked(),
|
|
1066
|
-
# Ball & Stick bond color (3D grey/uniform color)
|
|
1067
1221
|
'ball_stick_bond_color': getattr(self, 'bs_bond_color', self.default_settings.get('ball_stick_bond_color', '#7F7F7F')),
|
|
1068
1222
|
'ball_stick_use_cpk_bond_color': self.bs_use_cpk_bond_checkbox.isChecked(),
|
|
1223
|
+
# 2D Settings
|
|
1224
|
+
'bond_width_2d': self.bond_width_2d_slider.value() / 10.0,
|
|
1225
|
+
'bond_spacing_double_2d': self.bond_spacing_double_2d_slider.value() / 10.0,
|
|
1226
|
+
'bond_spacing_triple_2d': self.bond_spacing_triple_2d_slider.value() / 10.0,
|
|
1227
|
+
'atom_font_size_2d': self.atom_font_size_2d_slider.value(),
|
|
1228
|
+
'background_color_2d': self.current_bg_color_2d,
|
|
1229
|
+
'bond_color_2d': self.current_bond_color_2d,
|
|
1230
|
+
'atom_use_bond_color_2d': self.atom_use_bond_color_2d_checkbox.isChecked(),
|
|
1231
|
+
'bond_cap_style_2d': self.bond_cap_style_2d_combo.currentText(),
|
|
1069
1232
|
}
|
|
1070
1233
|
|
|
1071
1234
|
def pick_bs_bond_color(self):
|
|
@@ -1119,6 +1282,27 @@ class SettingsDialog(QDialog):
|
|
|
1119
1282
|
# 現在の分子を再描画(設定変更を反映)
|
|
1120
1283
|
if hasattr(self.parent_window, 'current_mol') and self.parent_window.current_mol:
|
|
1121
1284
|
self.parent_window.draw_molecule_3d(self.parent_window.current_mol)
|
|
1285
|
+
|
|
1286
|
+
# 2Dビューの設定適用 (背景色、結合スタイルなど)
|
|
1287
|
+
# update_style() will read the new settings from parent_window.settings
|
|
1288
|
+
try:
|
|
1289
|
+
if hasattr(self.parent_window, 'scene') and self.parent_window.scene:
|
|
1290
|
+
# Update Background
|
|
1291
|
+
bg_col_2d = self.parent_window.settings.get('background_color_2d', '#FFFFFF')
|
|
1292
|
+
self.parent_window.scene.setBackgroundBrush(QColor(bg_col_2d))
|
|
1293
|
+
|
|
1294
|
+
# Update Items
|
|
1295
|
+
for item in self.parent_window.scene.items():
|
|
1296
|
+
if hasattr(item, 'update_style'):
|
|
1297
|
+
item.update_style()
|
|
1298
|
+
elif hasattr(item, 'update'):
|
|
1299
|
+
item.update()
|
|
1300
|
+
|
|
1301
|
+
if hasattr(self.parent_window.view_2d, 'viewport'):
|
|
1302
|
+
self.parent_window.view_2d.viewport().update()
|
|
1303
|
+
except Exception:
|
|
1304
|
+
pass
|
|
1305
|
+
|
|
1122
1306
|
# ステータスバーに適用完了を表示
|
|
1123
1307
|
self.parent_window.statusBar().showMessage("Settings applied successfully")
|
|
1124
1308
|
|
|
@@ -1147,3 +1331,173 @@ class SettingsDialog(QDialog):
|
|
|
1147
1331
|
# apply_settingsを呼び出して設定を適用
|
|
1148
1332
|
self.apply_settings()
|
|
1149
1333
|
super().accept()
|
|
1334
|
+
|
|
1335
|
+
def update_ui_from_settings(self, settings_dict):
|
|
1336
|
+
"""設定辞書に基づいてUIを更新"""
|
|
1337
|
+
# 1. Scene settings
|
|
1338
|
+
self.current_bg_color = settings_dict.get('background_color', self.default_settings['background_color'])
|
|
1339
|
+
self.update_color_button(self.current_bg_color)
|
|
1340
|
+
|
|
1341
|
+
self.axes_checkbox.setChecked(settings_dict.get('show_3d_axes', self.default_settings['show_3d_axes']))
|
|
1342
|
+
self.light_checkbox.setChecked(settings_dict.get('lighting_enabled', self.default_settings['lighting_enabled']))
|
|
1343
|
+
|
|
1344
|
+
intensity = settings_dict.get('light_intensity', self.default_settings['light_intensity'])
|
|
1345
|
+
self.intensity_slider.setValue(int(intensity * 100))
|
|
1346
|
+
self.intensity_label.setText(f"{intensity:.2f}")
|
|
1347
|
+
|
|
1348
|
+
specular = settings_dict.get('specular', self.default_settings['specular'])
|
|
1349
|
+
self.specular_slider.setValue(int(specular * 100))
|
|
1350
|
+
self.specular_label.setText(f"{specular:.2f}")
|
|
1351
|
+
|
|
1352
|
+
spec_power = settings_dict.get('specular_power', self.default_settings['specular_power'])
|
|
1353
|
+
self.spec_power_slider.setValue(int(spec_power))
|
|
1354
|
+
self.spec_power_label.setText(str(spec_power))
|
|
1355
|
+
|
|
1356
|
+
proj_mode = settings_dict.get('projection_mode', self.default_settings['projection_mode'])
|
|
1357
|
+
idx = self.projection_combo.findText(proj_mode)
|
|
1358
|
+
if idx >= 0:
|
|
1359
|
+
self.projection_combo.setCurrentIndex(idx)
|
|
1360
|
+
|
|
1361
|
+
# 2. Ball & Stick settings
|
|
1362
|
+
bs_atom_scale = settings_dict.get('ball_stick_atom_scale', self.default_settings['ball_stick_atom_scale'])
|
|
1363
|
+
self.bs_atom_scale_slider.setValue(int(bs_atom_scale * 100))
|
|
1364
|
+
self.bs_atom_scale_label.setText(f"{bs_atom_scale:.2f}")
|
|
1365
|
+
|
|
1366
|
+
bs_bond_radius = settings_dict.get('ball_stick_bond_radius', self.default_settings['ball_stick_bond_radius'])
|
|
1367
|
+
self.bs_bond_radius_slider.setValue(int(bs_bond_radius * 100))
|
|
1368
|
+
self.bs_bond_radius_label.setText(f"{bs_bond_radius:.2f}")
|
|
1369
|
+
|
|
1370
|
+
# Multi-bond offsets (Ball & Stick)
|
|
1371
|
+
bs_db_offset = settings_dict.get('ball_stick_double_bond_offset_factor', self.default_settings.get('ball_stick_double_bond_offset_factor', 2.0))
|
|
1372
|
+
self.bs_double_offset_slider.setValue(int(bs_db_offset * 100))
|
|
1373
|
+
self.bs_double_offset_label.setText(f"{bs_db_offset:.2f}")
|
|
1374
|
+
|
|
1375
|
+
bs_tr_offset = settings_dict.get('ball_stick_triple_bond_offset_factor', self.default_settings.get('ball_stick_triple_bond_offset_factor', 2.0))
|
|
1376
|
+
self.bs_triple_offset_slider.setValue(int(bs_tr_offset * 100))
|
|
1377
|
+
self.bs_triple_offset_label.setText(f"{bs_tr_offset:.2f}")
|
|
1378
|
+
|
|
1379
|
+
bs_db_rad = settings_dict.get('ball_stick_double_bond_radius_factor', self.default_settings.get('ball_stick_double_bond_radius_factor', 0.8))
|
|
1380
|
+
self.bs_double_radius_slider.setValue(int(bs_db_rad * 100))
|
|
1381
|
+
self.bs_double_radius_label.setText(f"{bs_db_rad:.2f}")
|
|
1382
|
+
|
|
1383
|
+
bs_tr_rad = settings_dict.get('ball_stick_triple_bond_radius_factor', self.default_settings.get('ball_stick_triple_bond_radius_factor', 0.75))
|
|
1384
|
+
self.bs_triple_radius_slider.setValue(int(bs_tr_rad * 100))
|
|
1385
|
+
self.bs_triple_radius_label.setText(f"{bs_tr_rad:.2f}")
|
|
1386
|
+
|
|
1387
|
+
bs_res = settings_dict.get('ball_stick_resolution', self.default_settings['ball_stick_resolution'])
|
|
1388
|
+
self.bs_resolution_slider.setValue(int(bs_res))
|
|
1389
|
+
self.bs_resolution_label.setText(str(bs_res))
|
|
1390
|
+
|
|
1391
|
+
# Ball & Stick bond color
|
|
1392
|
+
self.bs_bond_color = settings_dict.get('ball_stick_bond_color', self.default_settings.get('ball_stick_bond_color', '#7F7F7F'))
|
|
1393
|
+
try:
|
|
1394
|
+
self.bs_bond_color_button.setStyleSheet(f"background-color: {self.bs_bond_color}; border: 1px solid #888;")
|
|
1395
|
+
self.bs_bond_color_button.setToolTip(self.bs_bond_color)
|
|
1396
|
+
except Exception:
|
|
1397
|
+
pass
|
|
1398
|
+
|
|
1399
|
+
self.bs_use_cpk_bond_checkbox.setChecked(settings_dict.get('ball_stick_use_cpk_bond_color', self.default_settings.get('ball_stick_use_cpk_bond_color', False)))
|
|
1400
|
+
|
|
1401
|
+
# 3. CPK settings
|
|
1402
|
+
cpk_atom_scale = settings_dict.get('cpk_atom_scale', self.default_settings['cpk_atom_scale'])
|
|
1403
|
+
self.cpk_atom_scale_slider.setValue(int(cpk_atom_scale * 100))
|
|
1404
|
+
self.cpk_atom_scale_label.setText(f"{cpk_atom_scale:.2f}")
|
|
1405
|
+
|
|
1406
|
+
cpk_res = settings_dict.get('cpk_resolution', self.default_settings['cpk_resolution'])
|
|
1407
|
+
self.cpk_resolution_slider.setValue(int(cpk_res))
|
|
1408
|
+
self.cpk_resolution_label.setText(str(cpk_res))
|
|
1409
|
+
|
|
1410
|
+
# 4. Wireframe settings
|
|
1411
|
+
wf_bond_radius = settings_dict.get('wireframe_bond_radius', self.default_settings['wireframe_bond_radius'])
|
|
1412
|
+
self.wf_bond_radius_slider.setValue(int(wf_bond_radius * 100))
|
|
1413
|
+
self.wf_bond_radius_label.setText(f"{wf_bond_radius:.2f}")
|
|
1414
|
+
|
|
1415
|
+
wf_res = settings_dict.get('wireframe_resolution', self.default_settings['wireframe_resolution'])
|
|
1416
|
+
self.wf_resolution_slider.setValue(int(wf_res))
|
|
1417
|
+
self.wf_resolution_label.setText(str(wf_res))
|
|
1418
|
+
|
|
1419
|
+
# Multi-bond offsets (Wireframe)
|
|
1420
|
+
wf_db_offset = settings_dict.get('wireframe_double_bond_offset_factor', self.default_settings.get('wireframe_double_bond_offset_factor', 3.0))
|
|
1421
|
+
self.wf_double_offset_slider.setValue(int(wf_db_offset * 100))
|
|
1422
|
+
self.wf_double_offset_label.setText(f"{wf_db_offset:.2f}")
|
|
1423
|
+
|
|
1424
|
+
wf_tr_offset = settings_dict.get('wireframe_triple_bond_offset_factor', self.default_settings.get('wireframe_triple_bond_offset_factor', 3.0))
|
|
1425
|
+
self.wf_triple_offset_slider.setValue(int(wf_tr_offset * 100))
|
|
1426
|
+
self.wf_triple_offset_label.setText(f"{wf_tr_offset:.2f}")
|
|
1427
|
+
|
|
1428
|
+
wf_db_rad = settings_dict.get('wireframe_double_bond_radius_factor', self.default_settings.get('wireframe_double_bond_radius_factor', 0.8))
|
|
1429
|
+
self.wf_double_radius_slider.setValue(int(wf_db_rad * 100))
|
|
1430
|
+
self.wf_double_radius_label.setText(f"{wf_db_rad:.2f}")
|
|
1431
|
+
|
|
1432
|
+
wf_tr_rad = settings_dict.get('wireframe_triple_bond_radius_factor', self.default_settings.get('wireframe_triple_bond_radius_factor', 0.75))
|
|
1433
|
+
self.wf_triple_radius_slider.setValue(int(wf_tr_rad * 100))
|
|
1434
|
+
self.wf_triple_radius_label.setText(f"{wf_tr_rad:.2f}")
|
|
1435
|
+
|
|
1436
|
+
# 5. Stick settings
|
|
1437
|
+
stick_bond_radius = settings_dict.get('stick_bond_radius', self.default_settings['stick_bond_radius'])
|
|
1438
|
+
self.stick_bond_radius_slider.setValue(int(stick_bond_radius * 100))
|
|
1439
|
+
self.stick_bond_radius_label.setText(f"{stick_bond_radius:.2f}")
|
|
1440
|
+
|
|
1441
|
+
stick_res = settings_dict.get('stick_resolution', self.default_settings['stick_resolution'])
|
|
1442
|
+
self.stick_resolution_slider.setValue(int(stick_res))
|
|
1443
|
+
self.stick_resolution_label.setText(str(stick_res))
|
|
1444
|
+
|
|
1445
|
+
# Multi-bond offsets (Stick)
|
|
1446
|
+
stick_db_offset = settings_dict.get('stick_double_bond_offset_factor', self.default_settings.get('stick_double_bond_offset_factor', 1.5))
|
|
1447
|
+
self.stick_double_offset_slider.setValue(int(stick_db_offset * 100))
|
|
1448
|
+
self.stick_double_offset_label.setText(f"{stick_db_offset:.2f}")
|
|
1449
|
+
|
|
1450
|
+
stick_tr_offset = settings_dict.get('stick_triple_bond_offset_factor', self.default_settings.get('stick_triple_bond_offset_factor', 1.0))
|
|
1451
|
+
self.stick_triple_offset_slider.setValue(int(stick_tr_offset * 100))
|
|
1452
|
+
self.stick_triple_offset_label.setText(f"{stick_tr_offset:.2f}")
|
|
1453
|
+
|
|
1454
|
+
stick_db_rad = settings_dict.get('stick_double_bond_radius_factor', self.default_settings.get('stick_double_bond_radius_factor', 0.6))
|
|
1455
|
+
self.stick_double_radius_slider.setValue(int(stick_db_rad * 100))
|
|
1456
|
+
self.stick_double_radius_label.setText(f"{stick_db_rad:.2f}")
|
|
1457
|
+
|
|
1458
|
+
stick_tr_rad = settings_dict.get('stick_triple_bond_radius_factor', self.default_settings.get('stick_triple_bond_radius_factor', 0.4))
|
|
1459
|
+
self.stick_triple_radius_slider.setValue(int(stick_tr_rad * 100))
|
|
1460
|
+
self.stick_triple_radius_label.setText(f"{stick_tr_rad:.2f}")
|
|
1461
|
+
|
|
1462
|
+
# 6. Other settings
|
|
1463
|
+
self.skip_chem_checks_checkbox.setChecked(settings_dict.get('skip_chemistry_checks', self.default_settings.get('skip_chemistry_checks', False)))
|
|
1464
|
+
self.kekule_3d_checkbox.setChecked(settings_dict.get('display_kekule_3d', self.default_settings.get('display_kekule_3d', False)))
|
|
1465
|
+
# always ask for charge on XYZ imports
|
|
1466
|
+
self.always_ask_charge_checkbox.setChecked(settings_dict.get('always_ask_charge', self.default_settings.get('always_ask_charge', False)))
|
|
1467
|
+
# Aromatic ring circle display and torus thickness factor
|
|
1468
|
+
self.aromatic_circle_checkbox.setChecked(settings_dict.get('display_aromatic_circles_3d', self.default_settings.get('display_aromatic_circles_3d', False)))
|
|
1469
|
+
thickness_factor = float(settings_dict.get('aromatic_torus_thickness_factor', self.default_settings.get('aromatic_torus_thickness_factor', 0.6)))
|
|
1470
|
+
try:
|
|
1471
|
+
self.aromatic_torus_thickness_slider.setValue(int(thickness_factor * 100))
|
|
1472
|
+
self.aromatic_torus_thickness_label.setText(f"{thickness_factor:.1f}")
|
|
1473
|
+
except Exception:
|
|
1474
|
+
pass
|
|
1475
|
+
|
|
1476
|
+
# 7. 2D Settings
|
|
1477
|
+
bw_2d = settings_dict.get('bond_width_2d', self.default_settings['bond_width_2d'])
|
|
1478
|
+
self.bond_width_2d_slider.setValue(int(bw_2d * 10))
|
|
1479
|
+
self.bond_width_2d_label.setText(f"{bw_2d:.1f}")
|
|
1480
|
+
|
|
1481
|
+
bsd_2d = settings_dict.get('bond_spacing_double_2d', self.default_settings['bond_spacing_double_2d'])
|
|
1482
|
+
self.bond_spacing_double_2d_slider.setValue(int(bsd_2d * 10))
|
|
1483
|
+
self.bond_spacing_double_2d_label.setText(f"{bsd_2d:.1f}")
|
|
1484
|
+
|
|
1485
|
+
bst_2d = settings_dict.get('bond_spacing_triple_2d', self.default_settings['bond_spacing_triple_2d'])
|
|
1486
|
+
self.bond_spacing_triple_2d_slider.setValue(int(bst_2d * 10))
|
|
1487
|
+
self.bond_spacing_triple_2d_label.setText(f"{bst_2d:.1f}")
|
|
1488
|
+
|
|
1489
|
+
fs_2d = settings_dict.get('atom_font_size_2d', self.default_settings['atom_font_size_2d'])
|
|
1490
|
+
self.atom_font_size_2d_slider.setValue(int(fs_2d))
|
|
1491
|
+
self.atom_font_size_2d_label.setText(str(fs_2d))
|
|
1492
|
+
|
|
1493
|
+
self.atom_use_bond_color_2d_checkbox.setChecked(settings_dict.get('atom_use_bond_color_2d', self.default_settings.get('atom_use_bond_color_2d', False)))
|
|
1494
|
+
|
|
1495
|
+
self.current_bg_color_2d = settings_dict.get('background_color_2d', self.default_settings['background_color_2d'])
|
|
1496
|
+
|
|
1497
|
+
# Load Bond Cap Style
|
|
1498
|
+
cap_style = settings_dict.get('bond_cap_style_2d', self.default_settings.get('bond_cap_style_2d', 'Round'))
|
|
1499
|
+
index = self.bond_cap_style_2d_combo.findText(cap_style)
|
|
1500
|
+
if index >= 0:
|
|
1501
|
+
self.bond_cap_style_2d_combo.setCurrentIndex(index)
|
|
1502
|
+
self.current_bond_color_2d = settings_dict.get('bond_color_2d', self.default_settings['bond_color_2d'])
|
|
1503
|
+
self.update_2d_color_buttons()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.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
|
|
@@ -7,25 +7,25 @@ moleditpy/modules/align_plane_dialog.py,sha256=SEC32l8z2x1W1Sf1Gu001OQEtinuvqGuC
|
|
|
7
7
|
moleditpy/modules/alignment_dialog.py,sha256=CPubSJI52mNSXmWt7oKQ2un5lXnN8_K4Z-LMM7wWTxQ,11309
|
|
8
8
|
moleditpy/modules/analysis_window.py,sha256=zjP5ipSTpKw8oLr1eKdoxW8Bk1SslGlPqsVucD-x_5w,9403
|
|
9
9
|
moleditpy/modules/angle_dialog.py,sha256=uc2WbvSfRe892xoEirqpZ78pf2Smwzkinkso6zLWr0Y,17751
|
|
10
|
-
moleditpy/modules/atom_item.py,sha256=
|
|
11
|
-
moleditpy/modules/bond_item.py,sha256=
|
|
10
|
+
moleditpy/modules/atom_item.py,sha256=zPV6AO0Je4rKgZekyYCGehucleKP21ALDsjVhGV8n-M,17738
|
|
11
|
+
moleditpy/modules/bond_item.py,sha256=hOwga7DcxZf8zxwZr8F7viTNgEi_9wftKFgSpROUJs8,21602
|
|
12
12
|
moleditpy/modules/bond_length_dialog.py,sha256=6bFPGssnqlgINuqpxLv-OhjMH3_hspnaH8QtorAyu2M,14782
|
|
13
13
|
moleditpy/modules/calculation_worker.py,sha256=KiGQY7i-QCQofEoE0r65KoQgpEGFcbhmxWv6egfkUdc,42324
|
|
14
14
|
moleditpy/modules/color_settings_dialog.py,sha256=Ow44BhCOLo0AFb6klO001k6B4drOgKX9DeNBQhZLp5o,15474
|
|
15
|
-
moleditpy/modules/constants.py,sha256=
|
|
15
|
+
moleditpy/modules/constants.py,sha256=_KThEyBuzjfnKMFRCmrcHetn0kEWmXmVyiLT2bFsrQ0,4702
|
|
16
16
|
moleditpy/modules/constrained_optimization_dialog.py,sha256=REsk4ePsqNmAGPMTS_jckeM7jexrU3krwun8sKqKUCs,30062
|
|
17
17
|
moleditpy/modules/custom_interactor_style.py,sha256=LDNODMJoNHGe1AUSrvqv6PdeJm-hpPmSpWINppnJLt0,38942
|
|
18
18
|
moleditpy/modules/custom_qt_interactor.py,sha256=vCZsDfRO-FtphD5cTP7Ps-5rpHZMIGloaoe6EaKzrsw,4139
|
|
19
19
|
moleditpy/modules/dialog3_d_picking_mixin.py,sha256=z4udbkiX9PYmIGazPXsbftkk_oRRwZhcvlCqbyJzr24,6493
|
|
20
20
|
moleditpy/modules/dihedral_dialog.py,sha256=bOTDO6-b74vEDn_z6OyuBr5cRz3RnRj83PiaEBUyWJA,18002
|
|
21
|
-
moleditpy/modules/main_window.py,sha256=
|
|
21
|
+
moleditpy/modules/main_window.py,sha256=bYioEvqRVZVfLSJeV_Sw0TyA0rVWR4unQ0C5OgaDN2s,36642
|
|
22
22
|
moleditpy/modules/main_window_app_state.py,sha256=8YDcGNCSpLTO1NGL9tEvNkXpUcS7JW-uK7TdUGvEqnk,35189
|
|
23
23
|
moleditpy/modules/main_window_compute.py,sha256=ipIkhH_DONXDnPzh7xeym9X-Yfx8EhsvXYOdyxsAj4c,53347
|
|
24
24
|
moleditpy/modules/main_window_dialog_manager.py,sha256=QR96LqHAPSOShXbc9cK-Ffq8a16JrXAoMKB0pHjESrQ,20072
|
|
25
25
|
moleditpy/modules/main_window_edit_3d.py,sha256=CUArB5wcsgq1C7LygAEC6URlbnn4RhRYDa5n-Y-etWI,19731
|
|
26
|
-
moleditpy/modules/main_window_edit_actions.py,sha256=
|
|
27
|
-
moleditpy/modules/main_window_export.py,sha256=
|
|
28
|
-
moleditpy/modules/main_window_main_init.py,sha256=
|
|
26
|
+
moleditpy/modules/main_window_edit_actions.py,sha256=jjMBwCzwflEHHvf_zh_RBcup9ceR_H4dDEvdRrb6wno,69009
|
|
27
|
+
moleditpy/modules/main_window_export.py,sha256=_Vd7MeP_xaLWDYDm3-ZIZiPuAXhP-AvrQbi7Yx3Jy3c,43020
|
|
28
|
+
moleditpy/modules/main_window_main_init.py,sha256=t-um1lHvXo_tPXSkjx5Y0mjZlrKUhZbmb529e-7ffNU,94781
|
|
29
29
|
moleditpy/modules/main_window_molecular_parsers.py,sha256=KR6vzuqc3nutOcorpYr0QOyX3MFBcxTwDhZX96VgJ9Q,48291
|
|
30
30
|
moleditpy/modules/main_window_project_io.py,sha256=TWwtuKDuvgcvPZ9IGmW8r1EJJOrgxrIJRnxe_f4C1oM,17149
|
|
31
31
|
moleditpy/modules/main_window_string_importers.py,sha256=v47wOd4RtjKYcF-aLP-mogGGdYTpTEo3dDyAu79_5MM,10782
|
|
@@ -41,7 +41,7 @@ moleditpy/modules/planarize_dialog.py,sha256=eaqI1MpF35e-VUMpJATt-EtGG5FhcSUlbAe
|
|
|
41
41
|
moleditpy/modules/plugin_interface.py,sha256=JfobFdAOLp5OnyJp3BNsWnpmZNJYypWQHNVNT0fP1N8,8171
|
|
42
42
|
moleditpy/modules/plugin_manager.py,sha256=ZFQz8VlCy1_IHX7DnMro7iRbouB_rxZQowfrKuZ76G8,21133
|
|
43
43
|
moleditpy/modules/plugin_manager_window.py,sha256=b4kEv0DaWHZG76ZaFTOxn6CVtA62_0MpPYYr10ehCtA,12544
|
|
44
|
-
moleditpy/modules/settings_dialog.py,sha256=
|
|
44
|
+
moleditpy/modules/settings_dialog.py,sha256=kT1c4INl0EOlazDRv1WZbpg2AHEPfworyq8BO-6z79k,85511
|
|
45
45
|
moleditpy/modules/template_preview_item.py,sha256=djdq3tz73d_fJGOvai3E-V9Hk9q9ZW7skx7BV59mooA,6556
|
|
46
46
|
moleditpy/modules/template_preview_view.py,sha256=4OCHZDO51BvJpKdfrBWJ4_4WfLfFSKxsVIyf7I-Kj2E,3350
|
|
47
47
|
moleditpy/modules/translation_dialog.py,sha256=x_GJsbVk-cj4aN2KgmYWDRUDInFlXezAoYoTvX-OT30,14553
|
|
@@ -51,9 +51,9 @@ moleditpy/modules/assets/file_icon.ico,sha256=yyVj084A7HuMNbV073cE_Ag3Ne405qgOP3
|
|
|
51
51
|
moleditpy/modules/assets/icon.icns,sha256=wD5R6-Vw7K662tVKhu2E1ImN0oUuyAP4youesEQsn9c,139863
|
|
52
52
|
moleditpy/modules/assets/icon.ico,sha256=RfgFcx7-dHY_2STdsOQCQziY5SNhDr3gPnjO6jzEDPI,147975
|
|
53
53
|
moleditpy/modules/assets/icon.png,sha256=kCFN1WacYIdy0GN6SFEbNA00ef39pCczBnFdkkBI8Bs,147110
|
|
54
|
-
moleditpy-2.
|
|
55
|
-
moleditpy-2.
|
|
56
|
-
moleditpy-2.
|
|
57
|
-
moleditpy-2.
|
|
58
|
-
moleditpy-2.
|
|
59
|
-
moleditpy-2.
|
|
54
|
+
moleditpy-2.4.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
55
|
+
moleditpy-2.4.1.dist-info/METADATA,sha256=Au-0u49T92vgtUFeMp_45D-KulYajiHuKYL0K0LsScw,60629
|
|
56
|
+
moleditpy-2.4.1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
57
|
+
moleditpy-2.4.1.dist-info/entry_points.txt,sha256=yH1h9JjALhok1foXT3-hYrC4ufoZt8b7oiBcsdnGNNM,54
|
|
58
|
+
moleditpy-2.4.1.dist-info/top_level.txt,sha256=ARICrS4ihlPXqywlKl6o-oJa3Qz3gZRWu_VZsQ3_c44,10
|
|
59
|
+
moleditpy-2.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|