MoleditPy-linux 1.18.0__tar.gz → 1.18.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/PKG-INFO +1 -2
  2. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/pyproject.toml +1 -2
  3. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -2
  4. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/align_plane_dialog.py +1 -1
  5. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/alignment_dialog.py +1 -1
  6. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/angle_dialog.py +2 -2
  7. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/bond_length_dialog.py +2 -2
  8. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/constants.py +1 -1
  9. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/constrained_optimization_dialog.py +2 -2
  10. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/dihedral_dialog.py +1 -1
  11. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_app_state.py +1 -1
  12. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_edit_3d.py +7 -7
  13. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_export.py +106 -49
  14. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_main_init.py +3 -3
  15. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_molecular_parsers.py +4 -3
  16. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_project_io.py +2 -2
  17. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_view_3d.py +13 -12
  18. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_view_loaders.py +1 -1
  19. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/molecule_scene.py +7 -12
  20. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/planarize_dialog.py +1 -1
  21. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/settings_dialog.py +5 -23
  22. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/user_template_dialog.py +9 -8
  23. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/LICENSE +0 -0
  24. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/README.md +0 -0
  25. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/setup.cfg +0 -0
  26. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
  27. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
  28. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
  29. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
  30. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
  31. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/__init__.py +0 -0
  32. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/__main__.py +0 -0
  33. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/main.py +0 -0
  34. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/__init__.py +0 -0
  35. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/about_dialog.py +0 -0
  36. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/analysis_window.py +0 -0
  37. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/assets/icon.icns +0 -0
  38. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/assets/icon.ico +0 -0
  39. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/assets/icon.png +0 -0
  40. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/atom_item.py +0 -0
  41. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/bond_item.py +0 -0
  42. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/calculation_worker.py +0 -0
  43. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/color_settings_dialog.py +0 -0
  44. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/custom_interactor_style.py +0 -0
  45. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/custom_qt_interactor.py +0 -0
  46. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/dialog3_d_picking_mixin.py +0 -0
  47. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window.py +0 -0
  48. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_compute.py +0 -0
  49. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_dialog_manager.py +0 -0
  50. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_edit_actions.py +0 -0
  51. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_string_importers.py +0 -0
  52. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/main_window_ui_manager.py +0 -0
  53. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/mirror_dialog.py +0 -0
  54. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/molecular_data.py +0 -0
  55. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/move_group_dialog.py +0 -0
  56. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/periodic_table_dialog.py +0 -0
  57. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/template_preview_item.py +0 -0
  58. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/template_preview_view.py +0 -0
  59. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/translation_dialog.py +0 -0
  60. {moleditpy_linux-1.18.0 → moleditpy_linux-1.18.1}/src/moleditpy_linux/modules/zoomable_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 1.18.0
3
+ Version: 1.18.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
@@ -684,7 +684,6 @@ Classifier: Programming Language :: Python :: 3
684
684
  Classifier: Operating System :: OS Independent
685
685
  Classifier: Intended Audience :: Science/Research
686
686
  Classifier: Topic :: Scientific/Engineering :: Chemistry
687
- Classifier: Programming Language :: Python :: 3.8
688
687
  Classifier: Programming Language :: Python :: 3.9
689
688
  Classifier: Programming Language :: Python :: 3.10
690
689
  Classifier: Programming Language :: Python :: 3.11
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "MoleditPy-linux"
7
7
 
8
- version = "1.18.0"
8
+ version = "1.18.1"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -24,7 +24,6 @@ classifiers = [
24
24
  "Operating System :: OS Independent",
25
25
  "Intended Audience :: Science/Research",
26
26
  "Topic :: Scientific/Engineering :: Chemistry",
27
- "Programming Language :: Python :: 3.8",
28
27
  "Programming Language :: Python :: 3.9",
29
28
  "Programming Language :: Python :: 3.10",
30
29
  "Programming Language :: Python :: 3.11",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 1.18.0
3
+ Version: 1.18.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
@@ -684,7 +684,6 @@ Classifier: Programming Language :: Python :: 3
684
684
  Classifier: Operating System :: OS Independent
685
685
  Classifier: Intended Audience :: Science/Research
686
686
  Classifier: Topic :: Scientific/Engineering :: Chemistry
687
- Classifier: Programming Language :: Python :: 3.8
688
687
  Classifier: Programming Language :: Python :: 3.9
689
688
  Classifier: Programming Language :: Python :: 3.10
690
689
  Classifier: Programming Language :: Python :: 3.11
@@ -194,7 +194,7 @@ class AlignPlaneDialog(Dialog3DPickingMixin, QDialog):
194
194
  for label_actor in self.selection_labels:
195
195
  try:
196
196
  self.main_window.plotter.remove_actor(label_actor)
197
- except:
197
+ except Exception:
198
198
  pass
199
199
  self.selection_labels = []
200
200
 
@@ -166,7 +166,7 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
166
166
  for label_actor in self.selection_labels:
167
167
  try:
168
168
  self.main_window.plotter.remove_actor(label_actor)
169
- except:
169
+ except Exception:
170
170
  pass
171
171
  self.selection_labels = []
172
172
 
@@ -202,7 +202,7 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
202
202
  for label_actor in self.selection_labels:
203
203
  try:
204
204
  self.main_window.plotter.remove_actor(label_actor)
205
- except:
205
+ except Exception:
206
206
  pass
207
207
  self.selection_labels = []
208
208
 
@@ -212,7 +212,7 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
212
212
  for label_actor in self.selection_labels:
213
213
  try:
214
214
  self.main_window.plotter.remove_actor(label_actor)
215
- except:
215
+ except Exception:
216
216
  pass
217
217
  self.selection_labels = []
218
218
 
@@ -190,7 +190,7 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
190
190
  for label_actor in self.selection_labels:
191
191
  try:
192
192
  self.main_window.plotter.remove_actor(label_actor)
193
- except:
193
+ except Exception:
194
194
  pass
195
195
  self.selection_labels = []
196
196
 
@@ -200,7 +200,7 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
200
200
  for label_actor in self.selection_labels:
201
201
  try:
202
202
  self.main_window.plotter.remove_actor(label_actor)
203
- except:
203
+ except Exception:
204
204
  pass
205
205
  self.selection_labels = []
206
206
 
@@ -16,7 +16,7 @@ from PyQt6.QtGui import QFont, QColor
16
16
  from rdkit import Chem
17
17
 
18
18
  #Version
19
- VERSION = '1.18.0'
19
+ VERSION = '1.18.1'
20
20
 
21
21
  ATOM_RADIUS = 18
22
22
  BOND_OFFSET = 3.5
@@ -394,7 +394,7 @@ class ConstrainedOptimizationDialog(Dialog3DPickingMixin, QDialog):
394
394
  for label_actor in self.constraint_labels:
395
395
  try:
396
396
  self.main_window.plotter.remove_actor(label_actor)
397
- except:
397
+ except Exception:
398
398
  pass
399
399
  self.constraint_labels = []
400
400
 
@@ -595,7 +595,7 @@ class ConstrainedOptimizationDialog(Dialog3DPickingMixin, QDialog):
595
595
  for label_actor in self.selection_labels:
596
596
  try:
597
597
  self.main_window.plotter.remove_actor(label_actor)
598
- except:
598
+ except Exception:
599
599
  pass
600
600
  self.selection_labels = []
601
601
 
@@ -207,7 +207,7 @@ class DihedralDialog(Dialog3DPickingMixin, QDialog):
207
207
  for label_actor in self.selection_labels:
208
208
  try:
209
209
  self.main_window.plotter.remove_actor(label_actor)
210
- except:
210
+ except Exception:
211
211
  pass
212
212
  self.selection_labels = []
213
213
 
@@ -574,7 +574,7 @@ class MainWindowAppState(object):
574
574
  inchi_key = Chem.MolToInchiKey(self.current_mol)
575
575
  json_data["identifiers"]["inchi"] = inchi
576
576
  json_data["identifiers"]["inchi_key"] = inchi_key
577
- except:
577
+ except Exception:
578
578
  pass # InChI生成に失敗した場合は無視
579
579
 
580
580
  except Exception as e:
@@ -119,7 +119,7 @@ class MainWindowEdit3d(object):
119
119
  for dialog in dialogs_to_close:
120
120
  try:
121
121
  dialog.close()
122
- except:
122
+ except Exception:
123
123
  pass
124
124
  self.active_3d_dialogs.clear()
125
125
 
@@ -169,7 +169,7 @@ class MainWindowEdit3d(object):
169
169
  try:
170
170
  # 既存の測定ラベルを削除
171
171
  self.plotter.remove_actor('measurement_labels')
172
- except:
172
+ except Exception:
173
173
  pass
174
174
 
175
175
  if not self.measurement_labels or not self.current_mol:
@@ -208,7 +208,7 @@ class MainWindowEdit3d(object):
208
208
  self.measurement_labels.clear()
209
209
  try:
210
210
  self.plotter.remove_actor('measurement_labels')
211
- except:
211
+ except Exception:
212
212
  pass
213
213
 
214
214
  # 2Dビューの測定ラベルも削除
@@ -219,7 +219,7 @@ class MainWindowEdit3d(object):
219
219
  try:
220
220
  self.plotter.remove_actor(self.measurement_text_actor)
221
221
  self.measurement_text_actor = None
222
- except:
222
+ except Exception:
223
223
  pass
224
224
 
225
225
  self.plotter.render()
@@ -434,7 +434,7 @@ class MainWindowEdit3d(object):
434
434
  if self.measurement_text_actor:
435
435
  try:
436
436
  self.plotter.remove_actor(self.measurement_text_actor)
437
- except:
437
+ except Exception:
438
438
  pass
439
439
 
440
440
  if not measurement_lines:
@@ -453,7 +453,7 @@ class MainWindowEdit3d(object):
453
453
  text_color = 'black' if luminance > 128 else 'white'
454
454
  else:
455
455
  text_color = 'white'
456
- except:
456
+ except Exception:
457
457
  text_color = 'white'
458
458
 
459
459
  # 左上に表示(小さな等幅フォント)
@@ -496,7 +496,7 @@ class MainWindowEdit3d(object):
496
496
  try:
497
497
  # 既存の選択ハイライトを削除
498
498
  self.plotter.remove_actor('selection_highlight')
499
- except:
499
+ except Exception:
500
500
  pass
501
501
 
502
502
  if not self.selected_atoms_3d or not self.current_mol:
@@ -178,11 +178,6 @@ class MainWindowExport(object):
178
178
  except Exception as e:
179
179
  self.statusBar().showMessage(f"Error exporting OBJ/MTL: {e}")
180
180
 
181
- return meshes_with_colors
182
-
183
- except Exception:
184
- return []
185
-
186
181
 
187
182
 
188
183
  def create_multi_material_obj(self, meshes_with_colors, obj_path, mtl_path):
@@ -199,17 +194,17 @@ class MainWindowExport(object):
199
194
  material_name = f"material_{i}_{mesh_data['name'].replace(' ', '_')}"
200
195
 
201
196
  mtl_file.write(f"newmtl {material_name}\n")
202
- mtl_file.write("Ka 0.2 0.2 0.2\n") # Ambient
197
+ mtl_file.write(f"Ka 0.2 0.2 0.2\n") # Ambient
203
198
  mtl_file.write(f"Kd {color[0]/255.0:.3f} {color[1]/255.0:.3f} {color[2]/255.0:.3f}\n") # Diffuse
204
- mtl_file.write("Ks 0.5 0.5 0.5\n") # Specular
205
- mtl_file.write("Ns 32.0\n") # Specular exponent
206
- mtl_file.write("illum 2\n") # Illumination model
207
- mtl_file.write("\n")
199
+ mtl_file.write(f"Ks 0.5 0.5 0.5\n") # Specular
200
+ mtl_file.write(f"Ns 32.0\n") # Specular exponent
201
+ mtl_file.write(f"illum 2\n") # Illumination model
202
+ mtl_file.write(f"\n")
208
203
 
209
204
  # OBJファイルを作成
210
205
  with open(obj_path, 'w') as obj_file:
211
- obj_file.write("# OBJ file with multiple materials\n")
212
- obj_file.write("# Generated with individual object colors\n")
206
+ obj_file.write(f"# OBJ file with multiple materials\n")
207
+ obj_file.write(f"# Generated with individual object colors\n")
213
208
  obj_file.write(f"mtllib {os.path.basename(mtl_path)}\n\n")
214
209
 
215
210
  vertex_offset = 1 # OBJファイルの頂点インデックスは1から始まる
@@ -229,6 +224,7 @@ class MainWindowExport(object):
229
224
  obj_file.write(f"v {point[0]:.6f} {point[1]:.6f} {point[2]:.6f}\n")
230
225
 
231
226
  # 面を書き込み
227
+ faces_written = 0
232
228
  for j in range(mesh.n_cells):
233
229
  cell = mesh.get_cell(j)
234
230
  if cell.type == 5: # VTK_TRIANGLE
@@ -237,6 +233,25 @@ class MainWindowExport(object):
237
233
  v2 = points_in_cell[1] + vertex_offset
238
234
  v3 = points_in_cell[2] + vertex_offset
239
235
  obj_file.write(f"f {v1} {v2} {v3}\n")
236
+ faces_written += 1
237
+ elif cell.type == 6: # VTK_TRIANGLE_STRIP
238
+ # Triangle strips share vertices between adjacent triangles
239
+ # For n points, we get (n-2) triangles
240
+ points_in_cell = cell.point_ids
241
+ n_points = len(points_in_cell)
242
+ for k in range(n_points - 2):
243
+ if k % 2 == 0:
244
+ # Even triangles: use points k, k+1, k+2
245
+ v1 = points_in_cell[k] + vertex_offset
246
+ v2 = points_in_cell[k+1] + vertex_offset
247
+ v3 = points_in_cell[k+2] + vertex_offset
248
+ else:
249
+ # Odd triangles: reverse winding to maintain consistent orientation
250
+ v1 = points_in_cell[k+1] + vertex_offset
251
+ v2 = points_in_cell[k] + vertex_offset
252
+ v3 = points_in_cell[k+2] + vertex_offset
253
+ obj_file.write(f"f {v1} {v2} {v3}\n")
254
+ faces_written += 1
240
255
  elif cell.type == 9: # VTK_QUAD
241
256
  points_in_cell = cell.point_ids
242
257
  v1 = points_in_cell[0] + vertex_offset
@@ -244,10 +259,12 @@ class MainWindowExport(object):
244
259
  v3 = points_in_cell[2] + vertex_offset
245
260
  v4 = points_in_cell[3] + vertex_offset
246
261
  obj_file.write(f"f {v1} {v2} {v3} {v4}\n")
262
+ faces_written += 1
247
263
 
248
- vertex_offset += mesh.n_points
249
- obj_file.write("\n")
250
264
 
265
+ vertex_offset += mesh.n_points
266
+ obj_file.write(f"\n")
267
+
251
268
  except Exception as e:
252
269
  raise Exception(f"Failed to create multi-material OBJ: {e}")
253
270
 
@@ -310,24 +327,25 @@ class MainWindowExport(object):
310
327
  # VTKアクターからポリデータを取得する複数の方法を試行
311
328
  mesh = None
312
329
 
313
- # 方法1: mapperのinputから取得
330
+ # 方法1: mapperのinputから取得 (Improved)
331
+ mapper = None
314
332
  if hasattr(actor, 'mapper') and actor.mapper is not None:
315
- if hasattr(actor.mapper, 'input') and actor.mapper.input is not None:
316
- mesh = actor.mapper.input
317
- elif hasattr(actor.mapper, 'GetInput') and actor.mapper.GetInput() is not None:
318
- mesh = actor.mapper.GetInput()
333
+ mapper = actor.mapper
334
+ elif hasattr(actor, 'GetMapper'):
335
+ mapper = actor.GetMapper()
336
+
337
+ if mapper is not None:
338
+ if hasattr(mapper, 'input') and mapper.input is not None:
339
+ mesh = mapper.input
340
+ elif hasattr(mapper, 'GetInput') and mapper.GetInput() is not None:
341
+ mesh = mapper.GetInput()
342
+ elif hasattr(mapper, 'GetInputAsDataSet'):
343
+ mesh = mapper.GetInputAsDataSet()
319
344
 
320
345
  # 方法2: PyVistaプロッターの内部データから取得
321
346
  if mesh is None and actor_name in self.plotter.mesh:
322
347
  mesh = self.plotter.mesh[actor_name]
323
348
 
324
- # 方法3: PyVistaのメッシュデータベースから検索
325
- if mesh is None:
326
- for mesh_name, mesh_data in self.plotter.mesh.items():
327
- if mesh_data is not None and mesh_data.n_points > 0:
328
- mesh = mesh_data
329
- break
330
-
331
349
  if mesh is not None and hasattr(mesh, 'n_points') and mesh.n_points > 0:
332
350
  # PyVistaメッシュに変換(必要な場合)
333
351
  if not isinstance(mesh, pv.PolyData):
@@ -391,23 +409,26 @@ class MainWindowExport(object):
391
409
  # VTKアクターからポリデータを取得する複数の方法を試行
392
410
  mesh = None
393
411
 
394
- # 方法1: mapperのinputから取得
412
+ # 方法1: mapperのinputから取得 (Improved)
413
+ mapper = None
395
414
  if hasattr(actor, 'mapper') and actor.mapper is not None:
396
- if hasattr(actor.mapper, 'input') and actor.mapper.input is not None:
397
- mesh = actor.mapper.input
398
- elif hasattr(actor.mapper, 'GetInput') and actor.mapper.GetInput() is not None:
399
- mesh = actor.mapper.GetInput()
415
+ mapper = actor.mapper
416
+ elif hasattr(actor, 'GetMapper'):
417
+ mapper = actor.GetMapper()
418
+
419
+ if mapper is not None:
420
+ if hasattr(mapper, 'input') and mapper.input is not None:
421
+ mesh = mapper.input
422
+ elif hasattr(mapper, 'GetInput') and mapper.GetInput() is not None:
423
+ mesh = mapper.GetInput()
424
+ elif hasattr(mapper, 'GetInputAsDataSet'):
425
+ mesh = mapper.GetInputAsDataSet()
400
426
 
401
427
  # 方法2: PyVistaプロッターの内部データから取得
402
428
  if mesh is None and actor_name in self.plotter.mesh:
403
429
  mesh = self.plotter.mesh[actor_name]
404
430
 
405
- # 方法3: PyVistaのメッシュデータベースから検索
406
- if mesh is None:
407
- for mesh_name, mesh_data in self.plotter.mesh.items():
408
- if mesh_data is not None and mesh_data.n_points > 0:
409
- mesh = mesh_data
410
- break
431
+ # 方法3: Removed unsafe fallback
411
432
 
412
433
  if mesh is not None and hasattr(mesh, 'n_points') and mesh.n_points > 0:
413
434
  # PyVistaメッシュに変換(必要な場合)
@@ -447,17 +468,26 @@ class MainWindowExport(object):
447
468
  actors = renderer.actors
448
469
 
449
470
  actor_count = 0
471
+
450
472
  for actor_name, actor in actors.items():
451
473
  try:
452
474
  # VTKアクターからポリデータを取得
453
475
  mesh = None
454
476
 
455
- # 方法1: mapperのinputから取得
477
+ # 方法1: mapperのinputから取得 (Improved)
478
+ mapper = None
456
479
  if hasattr(actor, 'mapper') and actor.mapper is not None:
457
- if hasattr(actor.mapper, 'input') and actor.mapper.input is not None:
458
- mesh = actor.mapper.input
459
- elif hasattr(actor.mapper, 'GetInput') and actor.mapper.GetInput() is not None:
460
- mesh = actor.mapper.GetInput()
480
+ mapper = actor.mapper
481
+ elif hasattr(actor, 'GetMapper'):
482
+ mapper = actor.GetMapper()
483
+
484
+ if mapper is not None:
485
+ if hasattr(mapper, 'input') and mapper.input is not None:
486
+ mesh = mapper.input
487
+ elif hasattr(mapper, 'GetInput') and mapper.GetInput() is not None:
488
+ mesh = mapper.GetInput()
489
+ elif hasattr(mapper, 'GetInputAsDataSet'):
490
+ mesh = mapper.GetInputAsDataSet()
461
491
 
462
492
  # 方法2: PyVistaプロッターの内部データから取得
463
493
  if mesh is None and actor_name in self.plotter.mesh:
@@ -484,7 +514,7 @@ class MainWindowExport(object):
484
514
  if prop is not None:
485
515
  vtk_color = prop.GetColor()
486
516
  color = [int(c * 255) for c in vtk_color]
487
- except:
517
+ except Exception:
488
518
  # 色取得に失敗した場合はデフォルト色をそのまま使用
489
519
  pass
490
520
 
@@ -513,6 +543,16 @@ class MainWindowExport(object):
513
543
  # 単一の colors 配列があればそれを使う
514
544
  elif 'colors' in pd:
515
545
  colors = np.asarray(pd['colors'])
546
+
547
+ # cell_dataのcolorsも確認(Tubeフィルタなどはcell_dataに色を持つ場合がある)
548
+ if colors is None and 'colors' in mesh_copy.cell_data:
549
+ try:
550
+ # cell_dataをpoint_dataに変換
551
+ temp_mesh = mesh_copy.cell_data_to_point_data()
552
+ if 'colors' in temp_mesh.point_data:
553
+ colors = np.asarray(temp_mesh.point_data['colors'])
554
+ except Exception:
555
+ pass
516
556
 
517
557
  if colors is not None and colors.size > 0:
518
558
  # 整数に変換。colors が 0-1 の float の場合は 255 倍して正規化する。
@@ -539,18 +579,26 @@ class MainWindowExport(object):
539
579
 
540
580
  # 一意な色ごとにサブメッシュを抽出して追加
541
581
  unique_colors, inverse = np.unique(colors_int, axis=0, return_inverse=True)
582
+
583
+ split_success = False
542
584
  if unique_colors.shape[0] > 1:
543
585
  for uc_idx, uc in enumerate(unique_colors):
544
586
  point_inds = np.where(inverse == uc_idx)[0]
545
587
  if point_inds.size == 0:
546
588
  continue
547
589
  try:
548
- submesh = mesh_copy.extract_points(point_inds, adjacent_cells=True)
590
+ # Use temp_mesh if available (has point data), else mesh_copy
591
+ target_mesh = temp_mesh if 'temp_mesh' in locals() else mesh_copy
592
+
593
+ # extract_points with adjacent_cells=False to avoid pulling in neighbors
594
+ submesh = target_mesh.extract_points(point_inds, adjacent_cells=False)
595
+
549
596
  except Exception:
550
597
  # extract_points が利用できない場合はスキップ
551
598
  continue
552
599
  if submesh is None or getattr(submesh, 'n_points', 0) == 0:
553
600
  continue
601
+
554
602
  color_rgb = [int(uc[0]), int(uc[1]), int(uc[2])]
555
603
  meshes_with_colors.append({
556
604
  'mesh': submesh,
@@ -559,13 +607,22 @@ class MainWindowExport(object):
559
607
  'type': 'display_actor',
560
608
  'actor_name': actor_name
561
609
  })
562
- actor_count += 1
563
- # 分割したので以下の通常追加は行わない
564
- continue
610
+ split_success = True
611
+
612
+ if split_success:
613
+ actor_count += 1
614
+ # 分割に成功したので以下の通常追加は行わない
615
+ continue
616
+ # If splitting failed (no submeshes added), fall through to default
617
+ else:
618
+ # 色が1色のみの場合は、その色を使用してメッシュ全体を出力
619
+ uc = unique_colors[0]
620
+ color = [int(uc[0]), int(uc[1]), int(uc[2])]
621
+ # ここでは continue せず、下のデフォルト追加処理に任せる(colorを更新したため)
565
622
  except Exception:
566
623
  # 分割処理に失敗した場合はフォールバックで単体メッシュを追加
567
624
  pass
568
-
625
+
569
626
  meshes_with_colors.append({
570
627
  'mesh': mesh_copy,
571
628
  'color': color,
@@ -577,9 +634,9 @@ class MainWindowExport(object):
577
634
  actor_count += 1
578
635
 
579
636
  except Exception as e:
580
- print(f"Error processing actor {actor_name}: {e}")
581
637
  continue
582
638
 
639
+
583
640
  return meshes_with_colors
584
641
 
585
642
  except Exception as e:
@@ -1563,7 +1563,6 @@ class MainWindowMainInit(object):
1563
1563
  'wireframe_bond_radius': 0.01,
1564
1564
  'wireframe_resolution': 6,
1565
1565
  # Stick model parameters
1566
- 'stick_atom_radius': 0.15,
1567
1566
  'stick_bond_radius': 0.15,
1568
1567
  'stick_resolution': 16,
1569
1568
  # Multiple bond offset parameters (per-model)
@@ -1573,12 +1572,13 @@ class MainWindowMainInit(object):
1573
1572
  'ball_stick_triple_bond_radius_factor': 0.75,
1574
1573
  'wireframe_double_bond_offset_factor': 3.0,
1575
1574
  'wireframe_triple_bond_offset_factor': 3.0,
1576
- 'wireframe_double_bond_radius_factor': 1.0,
1575
+ 'wireframe_double_bond_radius_factor': 0.8,
1577
1576
  'wireframe_triple_bond_radius_factor': 0.75,
1578
1577
  'stick_double_bond_offset_factor': 1.5,
1579
1578
  'stick_triple_bond_offset_factor': 1.0,
1580
1579
  'stick_double_bond_radius_factor': 0.60,
1581
1580
  'stick_triple_bond_radius_factor': 0.40,
1581
+ 'aromatic_torus_thickness_factor': 0.6,
1582
1582
  # Ensure conversion/optimization defaults are present
1583
1583
  # If True, attempts to be permissive when RDKit raises chemical/sanitization errors
1584
1584
  # during file import (useful for viewing malformed XYZ/MOL files).
@@ -1629,7 +1629,7 @@ class MainWindowMainInit(object):
1629
1629
  ('ball_stick_triple_bond_radius_factor', 'triple_bond_radius_factor', 0.75),
1630
1630
  ('wireframe_double_bond_offset_factor', 'double_bond_offset_factor', 3.0),
1631
1631
  ('wireframe_triple_bond_offset_factor', 'triple_bond_offset_factor', 3.0),
1632
- ('wireframe_double_bond_radius_factor', 'double_bond_radius_factor', 1.0),
1632
+ ('wireframe_double_bond_radius_factor', 'double_bond_radius_factor', 0.8),
1633
1633
  ('wireframe_triple_bond_radius_factor', 'triple_bond_radius_factor', 0.75),
1634
1634
  ('stick_double_bond_offset_factor', 'double_bond_offset_factor', 1.5),
1635
1635
  ('stick_triple_bond_offset_factor', 'triple_bond_offset_factor', 1.0),
@@ -22,6 +22,7 @@ import io
22
22
  import os
23
23
  import contextlib
24
24
  import traceback
25
+ import logging
25
26
 
26
27
 
27
28
  # RDKit imports (explicit to satisfy flake8 and used features)
@@ -59,7 +60,7 @@ if OBABEL_AVAILABLE:
59
60
  # If import fails here, disable OBABEL locally; avoid raising
60
61
  pybel = None
61
62
  OBABEL_AVAILABLE = False
62
- print("Warning: openbabel.pybel not available. Open Babel fallback and OBabel-based options will be disabled.")
63
+ logging.warning("Warning: openbabel.pybel not available. Open Babel fallback and OBabel-based options will be disabled.")
63
64
  else:
64
65
  pybel = None
65
66
 
@@ -901,12 +902,12 @@ class MainWindowMolecularParsers(object):
901
902
  try:
902
903
  mol.AddBond(i, j, Chem.BondType.SINGLE)
903
904
  bonds_added.append((i, j, distance))
904
- except:
905
+ except Exception:
905
906
  # 既に結合が存在する場合はスキップ
906
907
  pass
907
908
 
908
909
  # デバッグ情報(オプション)
909
- # print(f"Added {len(bonds_added)} bonds based on distance analysis")
910
+ # Added bonds based on distance analysis
910
911
 
911
912
  return len(bonds_added)
912
913
 
@@ -430,9 +430,9 @@ class MainWindowProjectIo(object):
430
430
  # 拡張子不明の場合はJSONとして試行
431
431
  try:
432
432
  self.load_json_data(file_path)
433
- except:
433
+ except Exception:
434
434
  try:
435
435
  self.load_raw_data(file_path)
436
- except:
436
+ except Exception:
437
437
  self.statusBar().showMessage("Error: Unable to determine file format.")
438
438
 
@@ -19,6 +19,7 @@ MainWindow (main_window.py) から分離されたモジュール
19
19
 
20
20
  import numpy as np
21
21
  import vtk
22
+ import logging
22
23
 
23
24
 
24
25
  # RDKit imports (explicit to satisfy flake8 and used features)
@@ -58,7 +59,7 @@ if OBABEL_AVAILABLE:
58
59
  # If import fails here, disable OBABEL locally; avoid raising
59
60
  pybel = None
60
61
  OBABEL_AVAILABLE = False
61
- print("Warning: openbabel.pybel not available. Open Babel fallback and OBabel-based options will be disabled.")
62
+ logging.warning("Warning: openbabel.pybel not available. Open Babel fallback and OBabel-based options will be disabled.")
62
63
  else:
63
64
  pybel = None
64
65
 
@@ -201,7 +202,7 @@ class MainWindowView3d(object):
201
202
  resolution = self.settings.get('wireframe_resolution', 6)
202
203
  rad = np.array([0.01 for s in sym]) # 極小値(使用されない)
203
204
  elif self.current_3d_style == 'stick':
204
- atom_radius = self.settings.get('stick_atom_radius', 0.15)
205
+ atom_radius = self.settings.get('stick_bond_radius', 0.15) # Use bond radius for atoms
205
206
  resolution = self.settings.get('stick_resolution', 16)
206
207
  rad = np.array([atom_radius for s in sym])
207
208
  else: # ball_and_stick
@@ -278,7 +279,7 @@ class MainWindowView3d(object):
278
279
 
279
280
  # 結合描画と同じ計算
280
281
  sphere_radius = cyl_radius * radius_factor
281
- except:
282
+ except Exception:
282
283
  sphere_radius = 0.09 # デフォルト値
283
284
  offset_distance = 0.15 # デフォルト値
284
285
 
@@ -437,7 +438,7 @@ class MainWindowView3d(object):
437
438
  double_radius_factor = self.settings.get('ball_stick_double_bond_radius_factor', 0.8)
438
439
  triple_radius_factor = self.settings.get('ball_stick_triple_bond_radius_factor', 0.75)
439
440
  elif self.current_3d_style == 'wireframe':
440
- double_radius_factor = self.settings.get('wireframe_double_bond_radius_factor', 1.0)
441
+ double_radius_factor = self.settings.get('wireframe_double_bond_radius_factor', 0.8)
441
442
  triple_radius_factor = self.settings.get('wireframe_triple_bond_radius_factor', 0.75)
442
443
  elif self.current_3d_style == 'stick':
443
444
  double_radius_factor = self.settings.get('stick_double_bond_radius_factor', 0.60)
@@ -660,7 +661,7 @@ class MainWindowView3d(object):
660
661
  self.plotter.add_mesh(circle_line, color=torus_color, **mesh_props)
661
662
 
662
663
  except Exception as e:
663
- print(f"Error rendering aromatic circles: {e}")
664
+ logging.error(f"Error rendering aromatic circles: {e}")
664
665
 
665
666
  if getattr(self, 'show_chiral_labels', False):
666
667
  try:
@@ -820,7 +821,7 @@ class MainWindowView3d(object):
820
821
  try:
821
822
  # 既存のE/Zラベルを削除
822
823
  self.plotter.remove_actor('ez_labels')
823
- except:
824
+ except Exception:
824
825
  pass
825
826
 
826
827
  pts, labels = [], []
@@ -837,7 +838,7 @@ class MainWindowView3d(object):
837
838
  # 3D座標からステレオ化学を再計算 (molに対して行う)
838
839
  # これにより、2Dでの描画状態に関わらず、現在の3D座標に基づいたE/Z判定が行われる
839
840
  Chem.AssignStereochemistry(mol, cleanIt=True, force=True, flagPossibleStereoCenters=True)
840
- except:
841
+ except Exception:
841
842
  pass
842
843
 
843
844
  for bond in mol.GetBonds():
@@ -1171,7 +1172,7 @@ class MainWindowView3d(object):
1171
1172
  for nm in self.atom_label_legend_names:
1172
1173
  try:
1173
1174
  self.plotter.remove_actor(nm)
1174
- except:
1175
+ except Exception:
1175
1176
  pass
1176
1177
  self.atom_label_legend_names = []
1177
1178
 
@@ -1237,12 +1238,12 @@ class MainWindowView3d(object):
1237
1238
  for a in list(self.current_atom_info_labels):
1238
1239
  try:
1239
1240
  self.plotter.remove_actor(a)
1240
- except:
1241
+ except Exception:
1241
1242
  pass
1242
1243
  else:
1243
1244
  try:
1244
1245
  self.plotter.remove_actor(self.current_atom_info_labels)
1245
- except:
1246
+ except Exception:
1246
1247
  pass
1247
1248
  except Exception:
1248
1249
  pass
@@ -1255,7 +1256,7 @@ class MainWindowView3d(object):
1255
1256
  for nm in list(self.atom_label_legend_names):
1256
1257
  try:
1257
1258
  self.plotter.remove_actor(nm)
1258
- except:
1259
+ except Exception:
1259
1260
  pass
1260
1261
  except Exception:
1261
1262
  pass
@@ -1401,7 +1402,7 @@ class MainWindowView3d(object):
1401
1402
  if renderer and hasattr(renderer, 'SetNumberOfLayers'):
1402
1403
  try:
1403
1404
  renderer.SetNumberOfLayers(2) # レイヤー0:3Dオブジェクト、レイヤー1:2Dオーバーレイ
1404
- except:
1405
+ except Exception:
1405
1406
  pass # PyVistaのバージョンによってはサポートされていない場合がある
1406
1407
 
1407
1408
  # --- 3D軸ウィジェットの設定 ---
@@ -317,7 +317,7 @@ class MainWindowViewLoaders(object):
317
317
  # Keep mol as-is (may lack conformer); downstream code checks for conformers
318
318
  else:
319
319
  raise
320
- except:
320
+ except Exception:
321
321
  self.statusBar().showMessage("Failed to generate 3D coordinates")
322
322
  return
323
323
 
@@ -11,6 +11,7 @@ DOI: 10.5281/zenodo.17268532
11
11
  """
12
12
 
13
13
  import traceback
14
+ import logging
14
15
 
15
16
  from PyQt6.QtWidgets import (
16
17
  QApplication, QGraphicsScene, QGraphicsItem,
@@ -203,9 +204,7 @@ class MoleculeScene(QGraphicsScene):
203
204
  self.window.push_undo_state()
204
205
  data_changed = False # ここでundo済みなので以降で積まない
205
206
  except Exception as e:
206
- print(f"Error clearing E/Z label: {e}")
207
-
208
- traceback.print_exc()
207
+ logging.error(f"Error clearing E/Z label: {e}", exc_info=True)
209
208
  if hasattr(self.window, 'statusBar'):
210
209
  self.window.statusBar().showMessage(f"Error clearing E/Z label: {e}", 5000)
211
210
  # AtomItemは何もしない
@@ -387,9 +386,7 @@ class MoleculeScene(QGraphicsScene):
387
386
  self.update_bond_stereo(b, new_stereo)
388
387
  self.window.push_undo_state() # ここでUndo stackに積む
389
388
  except Exception as e:
390
- print(f"Error in E/Z stereo toggle: {e}")
391
-
392
- traceback.print_exc()
389
+ logging.error(f"Error in E/Z stereo toggle: {e}", exc_info=True)
393
390
  if hasattr(self.window, 'statusBar'):
394
391
  self.window.statusBar().showMessage(f"Error changing E/Z stereochemistry: {e}", 5000)
395
392
  return # この後の処理は行わない
@@ -610,7 +607,7 @@ class MoleculeScene(QGraphicsScene):
610
607
  def create_bond(self, start_atom, end_atom, bond_order=None, bond_stereo=None):
611
608
  try:
612
609
  if start_atom is None or end_atom is None:
613
- print("Error: Cannot create bond with None atoms")
610
+ logging.error("Error: Cannot create bond with None atoms")
614
611
  return
615
612
 
616
613
  exist_b = self.find_bond_between(start_atom, end_atom)
@@ -637,9 +634,7 @@ class MoleculeScene(QGraphicsScene):
637
634
  end_atom.update_style()
638
635
 
639
636
  except Exception as e:
640
- print(f"Error creating bond: {e}")
641
-
642
- traceback.print_exc()
637
+ logging.error(f"Error creating bond: {e}", exc_info=True)
643
638
 
644
639
  def add_molecule_fragment(self, points, bonds_info, existing_items=None, symbol='C'):
645
640
  """
@@ -1668,9 +1663,9 @@ class MoleculeScene(QGraphicsScene):
1668
1663
  elif key == Qt.Key.Key_1 and (bond.order != 1 or bond.stereo != 0):
1669
1664
  bond.order = 1; bond.stereo = 0
1670
1665
  elif key == Qt.Key.Key_2 and (bond.order != 2 or bond.stereo != 0):
1671
- bond.order = 2; bond.stereo = 0; needs_update = True
1666
+ bond.order = 2; bond.stereo = 0
1672
1667
  elif key == Qt.Key.Key_3 and bond.order != 3:
1673
- bond.order = 3; bond.stereo = 0; needs_update = True
1668
+ bond.order = 3; bond.stereo = 0
1674
1669
 
1675
1670
  # 4. 実際に変更があった場合のみデータモデルを更新
1676
1671
  if old_order != bond.order or old_stereo != bond.stereo:
@@ -157,7 +157,7 @@ class PlanarizeDialog(Dialog3DPickingMixin, QDialog):
157
157
  for label_actor in self.selection_labels:
158
158
  try:
159
159
  self.main_window.plotter.remove_actor(label_actor)
160
- except:
160
+ except Exception:
161
161
  pass
162
162
  self.selection_labels = []
163
163
 
@@ -53,7 +53,6 @@ class SettingsDialog(QDialog):
53
53
  'wireframe_bond_radius': 0.01,
54
54
  'wireframe_resolution': 6,
55
55
  # Stick model parameters
56
- 'stick_atom_radius': 0.15,
57
56
  'stick_bond_radius': 0.15,
58
57
  'stick_resolution': 16,
59
58
  # Multiple bond offset parameters (per-model)
@@ -278,8 +277,8 @@ class SettingsDialog(QDialog):
278
277
  # Aromatic torus thickness factor
279
278
  self.aromatic_torus_thickness_slider = QSlider(Qt.Orientation.Horizontal)
280
279
  self.aromatic_torus_thickness_slider.setRange(10, 300) # 0.1x to 3.0x
281
- self.aromatic_torus_thickness_slider.setValue(100) # Default 1.0x
282
- self.aromatic_torus_thickness_label = QLabel("1.0")
280
+ self.aromatic_torus_thickness_slider.setValue(60) # Default 0.6x
281
+ self.aromatic_torus_thickness_label = QLabel("0.6")
283
282
  self.aromatic_torus_thickness_slider.valueChanged.connect(
284
283
  lambda v: self.aromatic_torus_thickness_label.setText(f"{v/100:.1f}")
285
284
  )
@@ -564,17 +563,7 @@ class SettingsDialog(QDialog):
564
563
  info_label.setStyleSheet("color: #666; font-style: italic; margin-top: 10px;")
565
564
  form_layout.addRow(info_label)
566
565
 
567
- # 原子半径
568
- self.stick_atom_radius_slider = QSlider(Qt.Orientation.Horizontal)
569
- self.stick_atom_radius_slider.setRange(5, 50) # 0.05 ~ 0.5
570
- self.stick_atom_radius_label = QLabel("0.15")
571
- self.stick_atom_radius_slider.valueChanged.connect(lambda v: self.stick_atom_radius_label.setText(f"{v/100:.2f}"))
572
- atom_radius_layout = QHBoxLayout()
573
- atom_radius_layout.addWidget(self.stick_atom_radius_slider)
574
- atom_radius_layout.addWidget(self.stick_atom_radius_label)
575
- form_layout.addRow("Atom Radius:", atom_radius_layout)
576
-
577
- # ボンド半径
566
+ # ボンド半径(原子半径も同じ値を使用)
578
567
  self.stick_bond_radius_slider = QSlider(Qt.Orientation.Horizontal)
579
568
  self.stick_bond_radius_slider.setRange(5, 50) # 0.05 ~ 0.5
580
569
  self.stick_bond_radius_label = QLabel("0.15")
@@ -673,7 +662,7 @@ class SettingsDialog(QDialog):
673
662
  'display_kekule_3d': self.default_settings.get('display_kekule_3d', False),
674
663
  'always_ask_charge': self.default_settings.get('always_ask_charge', False),
675
664
  'display_aromatic_circles_3d': self.default_settings.get('display_aromatic_circles_3d', False),
676
- 'aromatic_torus_thickness_factor': self.default_settings.get('aromatic_torus_thickness_factor', 1.0),
665
+ 'aromatic_torus_thickness_factor': self.default_settings.get('aromatic_torus_thickness_factor', 0.6),
677
666
  },
678
667
  "Ball & Stick": {
679
668
  'ball_stick_atom_scale': self.default_settings['ball_stick_atom_scale'],
@@ -700,7 +689,6 @@ class SettingsDialog(QDialog):
700
689
  'wireframe_triple_bond_radius_factor': self.default_settings.get('wireframe_triple_bond_radius_factor', 0.75)
701
690
  },
702
691
  "Stick": {
703
- 'stick_atom_radius': self.default_settings['stick_atom_radius'],
704
692
  'stick_bond_radius': self.default_settings['stick_bond_radius'],
705
693
  'stick_resolution': self.default_settings['stick_resolution'],
706
694
  'stick_double_bond_offset_factor': self.default_settings.get('stick_double_bond_offset_factor', 1.5),
@@ -844,7 +832,6 @@ class SettingsDialog(QDialog):
844
832
  'wireframe_bond_radius': self.wf_bond_radius_slider.value() / 100.0,
845
833
  'wireframe_resolution': self.wf_resolution_slider.value(),
846
834
  # Stick settings
847
- 'stick_atom_radius': self.stick_atom_radius_slider.value() / 100.0,
848
835
  'stick_bond_radius': self.stick_bond_radius_slider.value() / 100.0,
849
836
  'stick_resolution': self.stick_resolution_slider.value(),
850
837
  # Multi-bond settings (per-model)
@@ -944,10 +931,6 @@ class SettingsDialog(QDialog):
944
931
  self.wf_resolution_label.setText(str(settings_dict.get('wireframe_resolution', self.default_settings['wireframe_resolution'])))
945
932
 
946
933
  # Stick設定
947
- stick_atom_radius = int(settings_dict.get('stick_atom_radius', self.default_settings['stick_atom_radius']) * 100)
948
- self.stick_atom_radius_slider.setValue(stick_atom_radius)
949
- self.stick_atom_radius_label.setText(f"{stick_atom_radius/100:.2f}")
950
-
951
934
  stick_bond_radius = int(settings_dict.get('stick_bond_radius', self.default_settings['stick_bond_radius']) * 100)
952
935
  self.stick_bond_radius_slider.setValue(stick_bond_radius)
953
936
  self.stick_bond_radius_label.setText(f"{stick_bond_radius/100:.2f}")
@@ -1019,7 +1002,7 @@ class SettingsDialog(QDialog):
1019
1002
  self.always_ask_charge_checkbox.setChecked(settings_dict.get('always_ask_charge', self.default_settings.get('always_ask_charge', False)))
1020
1003
  # Aromatic ring circle display and torus thickness factor
1021
1004
  self.aromatic_circle_checkbox.setChecked(settings_dict.get('display_aromatic_circles_3d', self.default_settings.get('display_aromatic_circles_3d', False)))
1022
- thickness_factor = float(settings_dict.get('aromatic_torus_thickness_factor', self.default_settings.get('aromatic_torus_thickness_factor', 1.0)))
1005
+ thickness_factor = float(settings_dict.get('aromatic_torus_thickness_factor', self.default_settings.get('aromatic_torus_thickness_factor', 0.6)))
1023
1006
  try:
1024
1007
  self.aromatic_torus_thickness_slider.setValue(int(thickness_factor * 100))
1025
1008
  self.aromatic_torus_thickness_label.setText(f"{thickness_factor:.1f}")
@@ -1060,7 +1043,6 @@ class SettingsDialog(QDialog):
1060
1043
  'wireframe_bond_radius': self.wf_bond_radius_slider.value() / 100.0,
1061
1044
  'wireframe_resolution': self.wf_resolution_slider.value(),
1062
1045
  # Stick settings
1063
- 'stick_atom_radius': self.stick_atom_radius_slider.value() / 100.0,
1064
1046
  'stick_bond_radius': self.stick_bond_radius_slider.value() / 100.0,
1065
1047
  'stick_resolution': self.stick_resolution_slider.value(),
1066
1048
  # Multiple bond offset settings (per-model)
@@ -23,6 +23,7 @@ except Exception:
23
23
  from modules.constants import VERSION, CPK_COLORS
24
24
  import os
25
25
  import json
26
+ import logging
26
27
 
27
28
  class UserTemplateDialog(QDialog):
28
29
  """ユーザーテンプレート管理ダイアログ"""
@@ -106,7 +107,7 @@ class UserTemplateDialog(QDialog):
106
107
  elif hasattr(child, 'refit_view'):
107
108
  child.refit_view()
108
109
  except Exception as e:
109
- print(f"Warning: Failed to refit template previews: {e}")
110
+ logging.warning(f"Warning: Failed to refit template previews: {e}")
110
111
 
111
112
  def showEvent(self, event):
112
113
  """ダイアログ表示時にプレビューを適切にフィット"""
@@ -136,7 +137,7 @@ class UserTemplateDialog(QDialog):
136
137
  template_data['filepath'] = filepath
137
138
  self.user_templates.append(template_data)
138
139
  except Exception as e:
139
- print(f"Error loading user templates: {e}")
140
+ logging.error(f"Error loading user templates: {e}")
140
141
 
141
142
  self.update_template_grid()
142
143
 
@@ -146,7 +147,7 @@ class UserTemplateDialog(QDialog):
146
147
  with open(filepath, 'r', encoding='utf-8') as f:
147
148
  return json.load(f)
148
149
  except Exception as e:
149
- print(f"Error loading template file {filepath}: {e}")
150
+ logging.error(f"Error loading template file {filepath}: {e}")
150
151
  return None
151
152
 
152
153
  def save_template_file(self, filepath, template_data):
@@ -156,7 +157,7 @@ class UserTemplateDialog(QDialog):
156
157
  json.dump(template_data, f, indent=2, ensure_ascii=False)
157
158
  return True
158
159
  except Exception as e:
159
- print(f"Error saving template file {filepath}: {e}")
160
+ logging.error(f"Error saving template file {filepath}: {e}")
160
161
  return False
161
162
 
162
163
  def update_template_grid(self):
@@ -253,7 +254,7 @@ class UserTemplateDialog(QDialog):
253
254
  if view and not rect.isEmpty():
254
255
  view.fitInView(rect, Qt.AspectRatioMode.KeepAspectRatio)
255
256
  except Exception as e:
256
- print(f"Warning: Failed to fit preview view: {e}")
257
+ logging.warning(f"Warning: Failed to fit preview view: {e}")
257
258
 
258
259
  def draw_template_preview(self, scene, template_data, view_size=None):
259
260
  """テンプレートプレビューを描画 - fitInView縮小率に基づく動的スケーリング"""
@@ -303,7 +304,7 @@ class UserTemplateDialog(QDialog):
303
304
  scale_factor = 4.0
304
305
 
305
306
  # Debug info (can be removed in production)
306
- # print(f"Mol size: {mol_size:.1f}, Fit scale: {fit_scale:.3f}, Scale factor: {scale_factor:.2f}")
307
+ # logging.debug(f"Mol size: {mol_size:.1f}, Fit scale: {fit_scale:.3f}, Scale factor: {scale_factor:.2f}")
307
308
  else:
308
309
  scale_factor = 1.0
309
310
 
@@ -481,7 +482,7 @@ class UserTemplateDialog(QDialog):
481
482
  except Exception:
482
483
  pass
483
484
  except Exception as e:
484
- print(f"Warning: Failed to switch main window to template mode: {e}")
485
+ logging.warning(f"Warning: Failed to switch main window to template mode: {e}")
485
486
 
486
487
  def use_template(self, template_data):
487
488
  """テンプレートを使用(エディタに適用)"""
@@ -518,7 +519,7 @@ class UserTemplateDialog(QDialog):
518
519
  # Mark selected and keep dialog open
519
520
  self.selected_template = template_data
520
521
  except Exception as e:
521
- print(f"Warning: Failed to switch main window to template mode: {e}")
522
+ logging.warning(f"Warning: Failed to switch main window to template mode: {e}")
522
523
 
523
524
  # Don't close dialog - keep it open for easy template switching
524
525
  # self.accept()