MoleditPy-linux 2.4.5__py3-none-any.whl → 2.4.7__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.
@@ -60,9 +60,13 @@ class AtomItem(QGraphicsItem):
60
60
  win = self.scene().views()[0].window()
61
61
  if win and hasattr(win, 'settings'):
62
62
  font_size = win.settings.get('atom_font_size_2d', 20)
63
+ font_family = win.settings.get('atom_font_family_2d', FONT_FAMILY)
64
+ else:
65
+ font_family = FONT_FAMILY
63
66
  except Exception:
64
- pass
65
- self.font = QFont(FONT_FAMILY, font_size, FONT_WEIGHT_BOLD)
67
+ font_family = FONT_FAMILY
68
+
69
+ self.font = QFont(font_family, font_size, FONT_WEIGHT_BOLD)
66
70
  self.prepareGeometryChange()
67
71
 
68
72
  self.is_visible = not (self.symbol == 'C' and len(self.bonds) > 0 and self.charge == 0 and self.radical == 0)
@@ -70,16 +74,18 @@ class AtomItem(QGraphicsItem):
70
74
 
71
75
  def boundingRect(self):
72
76
  # --- paint()メソッドと完全に同じロジックでテキストの位置とサイズを計算 ---
73
- # Get dynamic font size
77
+ # Get dynamic font size and family
74
78
  font_size = 20
79
+ font_family = FONT_FAMILY
75
80
  try:
76
81
  if self.scene() and self.scene().views():
77
82
  win = self.scene().views()[0].window()
78
83
  if win and hasattr(win, 'settings'):
79
84
  font_size = win.settings.get('atom_font_size_2d', 20)
85
+ font_family = win.settings.get('atom_font_family_2d', FONT_FAMILY)
80
86
  except Exception:
81
87
  pass
82
- font = QFont(FONT_FAMILY, font_size, FONT_WEIGHT_BOLD)
88
+ font = QFont(font_family, font_size, FONT_WEIGHT_BOLD)
83
89
  fm = QFontMetricsF(font)
84
90
 
85
91
  hydrogen_part = ""
@@ -197,7 +203,9 @@ class AtomItem(QGraphicsItem):
197
203
  if self.scene() and self.scene().views():
198
204
  win = self.scene().views()[0].window()
199
205
  if win and hasattr(win, 'settings'):
200
- if win.settings.get('atom_use_bond_color_2d', False):
206
+ # Force Hydrogen to use bond color (invisible on white bg otherwise)
207
+ # OR if the global setting is checked
208
+ if self.symbol == 'H' or win.settings.get('atom_use_bond_color_2d', False):
201
209
  bond_col = win.settings.get('bond_color_2d', '#222222')
202
210
  color = QColor(bond_col)
203
211
  except Exception:
@@ -374,11 +382,9 @@ class AtomItem(QGraphicsItem):
374
382
  res = super().itemChange(change, value)
375
383
  if change == QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged:
376
384
  if self.flags() & QGraphicsItem.GraphicsItemFlag.ItemIsMovable:
377
- # Prevent cascading updates during batch operations
378
- if not getattr(self, '_updating_position', False):
379
- for bond in self.bonds:
380
- if bond.scene(): # Only update if bond is still in scene
381
- bond.update_position()
385
+ for bond in self.bonds:
386
+ if bond.scene():
387
+ bond.update_position()
382
388
 
383
389
  return res
384
390
 
@@ -94,10 +94,12 @@ class BondItem(QGraphicsItem):
94
94
  if self.atom1 is None or self.atom2 is None:
95
95
  return QLineF(0, 0, 0, 0)
96
96
  try:
97
- p2 = self.mapFromItem(self.atom2, 0, 0)
98
- return QLineF(QPointF(0, 0), p2)
99
- except (RuntimeError, TypeError):
100
- # Handle case where atoms are deleted from scene
97
+ # Use pos() directly - assuming items are in scene coords (no parent)
98
+ # This is robust and efficient.
99
+ p1 = self.atom1.pos()
100
+ p2 = self.atom2.pos()
101
+ return QLineF(QPointF(0, 0), p2 - p1)
102
+ except Exception:
101
103
  return QLineF(0, 0, 0, 0)
102
104
 
103
105
  def boundingRect(self):
@@ -120,12 +122,33 @@ class BondItem(QGraphicsItem):
120
122
  except Exception:
121
123
  bond_offset = globals().get('BOND_OFFSET', 3.5)
122
124
 
123
- extra = (getattr(self, 'order', 1) - 1) * bond_offset + 20
125
+ # Get dynamic wedge width
126
+ wedge_width = 6.0
127
+ try:
128
+ if self.scene() and self.scene().views():
129
+ win = self.scene().views()[0].window()
130
+ if win and hasattr(win, 'settings'):
131
+ wedge_width = win.settings.get('bond_wedge_width_2d', 6.0)
132
+ except Exception:
133
+ pass
134
+
135
+ extra = (getattr(self, 'order', 1) - 1) * bond_offset + 50 + wedge_width
124
136
  rect = QRectF(line.p1(), line.p2()).normalized().adjusted(-extra, -extra, extra, extra)
125
137
 
126
138
  # E/Zラベルの描画範囲も考慮して拡張(QFontMetricsFで正確に)
127
139
  if self.order == 2 and self.stereo in [3, 4]:
128
- font = QFont(FONT_FAMILY, FONT_SIZE_LARGE, FONT_WEIGHT_BOLD)
140
+ font_size = 20
141
+ font_family = FONT_FAMILY
142
+ try:
143
+ if self.scene() and self.scene().views():
144
+ win = self.scene().views()[0].window()
145
+ if win and hasattr(win, 'settings'):
146
+ font_size = win.settings.get('atom_font_size_2d', 20)
147
+ font_family = win.settings.get('atom_font_family_2d', FONT_FAMILY)
148
+ except Exception:
149
+ pass
150
+
151
+ font = QFont(font_family, font_size, FONT_WEIGHT_BOLD)
129
152
  font.setItalic(True)
130
153
  text = "Z" if self.stereo == 3 else "E"
131
154
  fm = QFontMetricsF(font)
@@ -141,43 +164,50 @@ class BondItem(QGraphicsItem):
141
164
  return rect
142
165
 
143
166
  def shape(self):
167
+ """Define the precise collision/selection area, separate from the drawing area (boundingRect)."""
144
168
  path = QPainterPath()
145
169
  try:
146
170
  line = self.get_line_in_local_coords()
171
+ # Create a simple path along the bond line
172
+ path.moveTo(line.p1())
173
+ path.lineTo(line.p2())
174
+
175
+ # Stroke it to give it some width (e.g., 10px or dynamic based on settings) generally easier to click
176
+ # even if the visual width is smaller.
177
+ stroker = QPainterPathStroker()
178
+ stroker.setWidth(DESIRED_BOND_PIXEL_WIDTH) # Use constant (20.0)
179
+ path = stroker.createStroke(path)
180
+
181
+ # If there's an E/Z label, add its rect to the selection shape
182
+ label_rect = self.get_ez_label_local_rect()
183
+ if label_rect:
184
+ path.addRect(label_rect)
185
+
147
186
  except Exception:
148
- return path
149
- if line.length() == 0:
150
- return path
151
-
152
- scene = self.scene()
153
- if not scene or not scene.views():
154
- return super().shape()
155
-
156
- view = scene.views()[0]
157
- scale = view.transform().m11()
187
+ # Fallback to a small rect around the origin if calculation fails
188
+ path.addRect(QRectF(-5, -5, 10, 10))
189
+
190
+ return path
158
191
 
159
- # Dynamic bond width
160
- width_2d = 2.0
192
+ def get_ez_label_local_rect(self):
193
+ """Helper to get E/Z label rect in local coordinates."""
194
+ if self.order != 2 or self.stereo not in [3, 4]:
195
+ return None
161
196
  try:
162
- if view.window() and hasattr(view.window(), 'settings'):
163
- width_2d = view.window().settings.get('bond_width_2d', 2.0)
197
+ line = self.get_line_in_local_coords()
198
+ center = line.center()
199
+
200
+ # Logic similar to boundingRect but returning just the label box
201
+ font_size = 20
202
+ # ... (Simpler logic: just return a box around center)
203
+ # Standard size estimate
204
+ box_size = 30
205
+ return QRectF(center.x() - box_size/2, center.y() - box_size/2, box_size, box_size)
164
206
  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
207
+ return None
170
208
 
171
209
 
172
- stroker = QPainterPathStroker()
173
- stroker.setWidth(scene_width)
174
- stroker.setCapStyle(Qt.PenCapStyle.RoundCap)
175
- stroker.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
176
210
 
177
- center_line_path = QPainterPath(line.p1())
178
- center_line_path.lineTo(line.p2())
179
-
180
- return stroker.createStroke(center_line_path)
181
211
 
182
212
  def paint(self, painter, option, widget):
183
213
  if self.atom1 is None or self.atom2 is None:
@@ -383,7 +413,18 @@ class BondItem(QGraphicsItem):
383
413
  painter.save() # 現在の描画設定を保存
384
414
 
385
415
  # --- ラベルの設定 ---
386
- font = QFont(FONT_FAMILY, FONT_SIZE_LARGE, FONT_WEIGHT_BOLD)
416
+ font_size = 20
417
+ font_family = FONT_FAMILY
418
+ try:
419
+ if self.scene() and self.scene().views():
420
+ win = self.scene().views()[0].window()
421
+ if win and hasattr(win, 'settings'):
422
+ font_size = win.settings.get('atom_font_size_2d', 20)
423
+ font_family = win.settings.get('atom_font_family_2d', FONT_FAMILY)
424
+ except Exception:
425
+ pass
426
+
427
+ font = QFont(font_family, font_size, FONT_WEIGHT_BOLD)
387
428
  font.setItalic(True)
388
429
  text_color = QColor("gray")
389
430
  # 輪郭の色を背景色と同じにする(scene()がNoneのときは安全なフォールバックを使う)
@@ -442,9 +483,10 @@ class BondItem(QGraphicsItem):
442
483
 
443
484
 
444
485
 
445
- def update_position(self):
486
+ def update_position(self, notify=True):
446
487
  try:
447
- self.prepareGeometryChange()
488
+ if notify:
489
+ self.prepareGeometryChange()
448
490
  if self.atom1:
449
491
  self.setPos(self.atom1.pos())
450
492
  self.update()
@@ -16,7 +16,7 @@ from PyQt6.QtGui import QFont, QColor
16
16
  from rdkit import Chem
17
17
 
18
18
  #Version
19
- VERSION = '2.4.5'
19
+ VERSION = '2.4.7'
20
20
 
21
21
  ATOM_RADIUS = 18
22
22
  BOND_OFFSET = 3.5
@@ -12,11 +12,11 @@ DOI: 10.5281/zenodo.17268532
12
12
 
13
13
  from PyQt6.QtWidgets import (
14
14
  QDialog, QVBoxLayout, QTabWidget, QWidget, QFormLayout, QPushButton, QHBoxLayout,
15
- QCheckBox, QComboBox, QLabel, QColorDialog, QSlider, QFrame, QMessageBox
15
+ QCheckBox, QComboBox, QLabel, QColorDialog, QSlider, QFrame, QMessageBox, QFontComboBox
16
16
  )
17
17
  from PyQt6.QtWidgets import QApplication
18
18
  from PyQt6.QtCore import Qt
19
- from PyQt6.QtGui import QColor
19
+ from PyQt6.QtGui import QColor, QFont
20
20
  try:
21
21
  from .constants import CPK_COLORS
22
22
  except Exception:
@@ -101,6 +101,7 @@ class SettingsDialog(QDialog):
101
101
  'bond_cap_style_2d': 'Round',
102
102
  'bond_wedge_width_2d': 6.0,
103
103
  'bond_dash_count_2d': 8,
104
+ 'atom_font_family_2d': 'Arial',
104
105
  }
105
106
 
106
107
  # --- 選択された色を管理する専用のインスタンス変数 ---
@@ -275,6 +276,13 @@ class SettingsDialog(QDialog):
275
276
  # --- Atom Settings ---
276
277
  form_layout.addRow(QLabel("<b>Atom Settings</b>"))
277
278
 
279
+ # Font Family
280
+ self.atom_font_family_2d_combo = QFontComboBox()
281
+ self.atom_font_family_2d_combo.setEditable(False) # Force selection from list
282
+ # Filter mostly for scalable fonts if desired, but default is usually fine
283
+ self.atom_font_family_2d_combo.setFontFilters(QFontComboBox.FontFilter.ScalableFonts)
284
+ form_layout.addRow("Atom Label Font Family:", self.atom_font_family_2d_combo)
285
+
278
286
  # Font Size
279
287
  self.atom_font_size_2d_slider = QSlider(Qt.Orientation.Horizontal)
280
288
  self.atom_font_size_2d_slider.setRange(8, 72)
@@ -813,10 +821,10 @@ class SettingsDialog(QDialog):
813
821
  'background_color_2d': self.default_settings['background_color_2d'],
814
822
  'bond_color_2d': self.default_settings['bond_color_2d'],
815
823
  'atom_use_bond_color_2d': self.default_settings['atom_use_bond_color_2d'],
816
- 'atom_use_bond_color_2d': self.default_settings['atom_use_bond_color_2d'],
817
824
  'bond_cap_style_2d': self.default_settings['bond_cap_style_2d'],
818
825
  'bond_wedge_width_2d': self.default_settings['bond_wedge_width_2d'],
819
- 'bond_dash_count_2d': self.default_settings['bond_dash_count_2d']
826
+ 'bond_dash_count_2d': self.default_settings['bond_dash_count_2d'],
827
+ 'atom_font_family_2d': self.default_settings['atom_font_family_2d']
820
828
  },
821
829
  "3D Scene": {
822
830
  'background_color': self.default_settings['background_color'],
@@ -1258,6 +1266,7 @@ class SettingsDialog(QDialog):
1258
1266
  'bond_cap_style_2d': self.bond_cap_style_2d_combo.currentText(),
1259
1267
  'bond_wedge_width_2d': self.bond_wedge_width_2d_slider.value() / 10.0,
1260
1268
  'bond_dash_count_2d': self.bond_dash_count_2d_slider.value(),
1269
+ 'atom_font_family_2d': self.atom_font_family_2d_combo.currentFont().family(),
1261
1270
  }
1262
1271
 
1263
1272
  def pick_bs_bond_color(self):
@@ -1519,6 +1528,10 @@ class SettingsDialog(QDialog):
1519
1528
  self.atom_font_size_2d_slider.setValue(int(fs_2d))
1520
1529
  self.atom_font_size_2d_label.setText(str(fs_2d))
1521
1530
 
1531
+ # Load Font Family
1532
+ font_family = settings_dict.get('atom_font_family_2d', self.default_settings['atom_font_family_2d'])
1533
+ self.atom_font_family_2d_combo.setCurrentFont(QFont(font_family))
1534
+
1522
1535
  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)))
1523
1536
 
1524
1537
  self.current_bg_color_2d = settings_dict.get('background_color_2d', self.default_settings['background_color_2d'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 2.4.5
3
+ Version: 2.4.7
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,12 +7,12 @@ moleditpy_linux/modules/align_plane_dialog.py,sha256=SEC32l8z2x1W1Sf1Gu001OQEtin
7
7
  moleditpy_linux/modules/alignment_dialog.py,sha256=CPubSJI52mNSXmWt7oKQ2un5lXnN8_K4Z-LMM7wWTxQ,11309
8
8
  moleditpy_linux/modules/analysis_window.py,sha256=zjP5ipSTpKw8oLr1eKdoxW8Bk1SslGlPqsVucD-x_5w,9403
9
9
  moleditpy_linux/modules/angle_dialog.py,sha256=uc2WbvSfRe892xoEirqpZ78pf2Smwzkinkso6zLWr0Y,17751
10
- moleditpy_linux/modules/atom_item.py,sha256=zPV6AO0Je4rKgZekyYCGehucleKP21ALDsjVhGV8n-M,17738
11
- moleditpy_linux/modules/bond_item.py,sha256=JlfXCSd9wN13M52bcbly4hbI6W83IhYt5wpRzUAjMEg,22020
10
+ moleditpy_linux/modules/atom_item.py,sha256=9x0xojz6eih-N6UFgGJHNdRshgOurx3F5ysMzcHup6Y,18034
11
+ moleditpy_linux/modules/bond_item.py,sha256=21oX1sR5kpY-MlfUsmekkV6XATVwIAhmsH1UMwV34rg,24225
12
12
  moleditpy_linux/modules/bond_length_dialog.py,sha256=6bFPGssnqlgINuqpxLv-OhjMH3_hspnaH8QtorAyu2M,14782
13
13
  moleditpy_linux/modules/calculation_worker.py,sha256=KiGQY7i-QCQofEoE0r65KoQgpEGFcbhmxWv6egfkUdc,42324
14
14
  moleditpy_linux/modules/color_settings_dialog.py,sha256=Ow44BhCOLo0AFb6klO001k6B4drOgKX9DeNBQhZLp5o,15474
15
- moleditpy_linux/modules/constants.py,sha256=xHvWc66dgQ0Z1YICny3WRGTL8F9-BC33j7UgzGSuNzw,4702
15
+ moleditpy_linux/modules/constants.py,sha256=cJBVJ8ESdrhJ3CVXNEWjf8jAUcw9bjzBIm80BxT3CFw,4702
16
16
  moleditpy_linux/modules/constrained_optimization_dialog.py,sha256=REsk4ePsqNmAGPMTS_jckeM7jexrU3krwun8sKqKUCs,30062
17
17
  moleditpy_linux/modules/custom_interactor_style.py,sha256=LDNODMJoNHGe1AUSrvqv6PdeJm-hpPmSpWINppnJLt0,38942
18
18
  moleditpy_linux/modules/custom_qt_interactor.py,sha256=vCZsDfRO-FtphD5cTP7Ps-5rpHZMIGloaoe6EaKzrsw,4139
@@ -41,7 +41,7 @@ moleditpy_linux/modules/planarize_dialog.py,sha256=eaqI1MpF35e-VUMpJATt-EtGG5Fhc
41
41
  moleditpy_linux/modules/plugin_interface.py,sha256=JfobFdAOLp5OnyJp3BNsWnpmZNJYypWQHNVNT0fP1N8,8171
42
42
  moleditpy_linux/modules/plugin_manager.py,sha256=ZFQz8VlCy1_IHX7DnMro7iRbouB_rxZQowfrKuZ76G8,21133
43
43
  moleditpy_linux/modules/plugin_manager_window.py,sha256=b4kEv0DaWHZG76ZaFTOxn6CVtA62_0MpPYYr10ehCtA,12544
44
- moleditpy_linux/modules/settings_dialog.py,sha256=9hq2QDmlJsjdblW6f1gorItLg31XJub9tVsgcZ60Cbk,87756
44
+ moleditpy_linux/modules/settings_dialog.py,sha256=5dM12SSnRCIl6JthF9whuq5Dcq79dLXwDp5_6QTUnkI,88559
45
45
  moleditpy_linux/modules/template_preview_item.py,sha256=djdq3tz73d_fJGOvai3E-V9Hk9q9ZW7skx7BV59mooA,6556
46
46
  moleditpy_linux/modules/template_preview_view.py,sha256=4OCHZDO51BvJpKdfrBWJ4_4WfLfFSKxsVIyf7I-Kj2E,3350
47
47
  moleditpy_linux/modules/translation_dialog.py,sha256=x_GJsbVk-cj4aN2KgmYWDRUDInFlXezAoYoTvX-OT30,14553
@@ -51,9 +51,9 @@ moleditpy_linux/modules/assets/file_icon.ico,sha256=yyVj084A7HuMNbV073cE_Ag3Ne40
51
51
  moleditpy_linux/modules/assets/icon.icns,sha256=wD5R6-Vw7K662tVKhu2E1ImN0oUuyAP4youesEQsn9c,139863
52
52
  moleditpy_linux/modules/assets/icon.ico,sha256=RfgFcx7-dHY_2STdsOQCQziY5SNhDr3gPnjO6jzEDPI,147975
53
53
  moleditpy_linux/modules/assets/icon.png,sha256=kCFN1WacYIdy0GN6SFEbNA00ef39pCczBnFdkkBI8Bs,147110
54
- moleditpy_linux-2.4.5.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
55
- moleditpy_linux-2.4.5.dist-info/METADATA,sha256=hRyUoLBN5zV3_M1aGNxUwlAGSHmVakf62lEBfBwISPM,60708
56
- moleditpy_linux-2.4.5.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
57
- moleditpy_linux-2.4.5.dist-info/entry_points.txt,sha256=-OzipSi__yVwlimNtu3eiRP5t5UMg55Cs0udyhXYiyw,60
58
- moleditpy_linux-2.4.5.dist-info/top_level.txt,sha256=qyqe-hDYL6CXyin9E5Me5rVl3PG84VqiOjf9bQvfJLs,16
59
- moleditpy_linux-2.4.5.dist-info/RECORD,,
54
+ moleditpy_linux-2.4.7.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
55
+ moleditpy_linux-2.4.7.dist-info/METADATA,sha256=iFu5Ov9NUgyr0c5AUzscJqZKGQ0pkIpk8g-yOEgHMXo,60708
56
+ moleditpy_linux-2.4.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
57
+ moleditpy_linux-2.4.7.dist-info/entry_points.txt,sha256=-OzipSi__yVwlimNtu3eiRP5t5UMg55Cs0udyhXYiyw,60
58
+ moleditpy_linux-2.4.7.dist-info/top_level.txt,sha256=qyqe-hDYL6CXyin9E5Me5rVl3PG84VqiOjf9bQvfJLs,16
59
+ moleditpy_linux-2.4.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5