PyQt6绘制原子双键/三键异常:135°与315°方向显示为单键
问题描述
我正在为选中的原子绘制候选化学键:通过cos和sin计算出以45°步长分布的8个候选原子位置,这部分功能正常。但绘制双键或三键时,135°(左上)和315°(右下)方向的键始终显示为单键,无法正确渲染为设定的双键/三键。
相关代码片段
主绘制逻辑
# Calculate possible positions for new atoms in 360° around the selected atom if self.selected_atom is not None: potential_positions = self.calc_potential_positions(self.selected_atom) for pos in potential_positions: # If atoms at position don't overlap, draw the potential bonds and atoms in different colour if not self.check_atom_overlap(pos[0], pos[1]): if self.bond_order == 2: self.draw_double_bond(self.selected_atom.x_coords, self.selected_atom.y_coords, pos[0], pos[1], False) elif self.bond_order == 3: self.draw_triple_bond(self.selected_atom.x_coords, self.selected_atom.y_coords, pos[0], pos[1], False) else: self.draw_single_bond(self.selected_atom.x_coords, self.selected_atom.y_coords, pos[0], pos[1], False) self.draw_atom_circle(pos[0], pos[1], self.selected_atom.x_coords, self.selected_atom.y_coords) self.draw_atom(pos[0], pos[1], self.element.SYMBOL, True) self.draw_center_atom(self.selected_atom.x_coords, self.selected_atom.y_coords, self.selected_atom.symbol)
候选位置计算方法
@staticmethod def calc_potential_positions(atom: chem_editor_logic.Atom) -> list[tuple[int, int]]: """ :param atom: :return: list of tuples containing x and y coordinates of potential positions for new atoms """ x = atom.x_coords y = atom.y_coords distance = 40 # Calculate coordinates for angles in steps of 45 degrees from 0 to 360 coordinates_list = [] for angle_degrees in range(0, 360, 45): angle_radians = math.radians(angle_degrees) new_x = x + distance * math.cos(angle_radians) new_y = y + distance * math.sin(angle_radians) coordinates_list.append((int(new_x), int(new_y))) print(x, y) print(coordinates_list) return coordinates_list
化学键绘制方法
def draw_single_bond(self, atom1_x: int, atom1_y: int, atom2_x: int, atom2_y: int, actual_bond: bool) -> None: painter = QPainter(self) pen = QPen() # Set colour black pen.setColor(QColor(0, 0, 0)) pen.setWidth(2) if not actual_bond: # Set colour red pen.setColor(QColor(255, 0, 0)) painter.setPen(pen) painter.drawLine(QPoint(atom1_x, atom1_y), QPoint(atom2_x, atom2_y)) def draw_double_bond(self, atom1_x: int, atom1_y: int, atom2_x: int, atom2_y: int, actual_bond: bool) -> None: painter = QPainter(self) pen = QPen() # Set colour black pen.setColor(QColor(0, 0, 0)) pen.setWidth(2) if not actual_bond: # Set colour red pen.setColor(QColor(255, 0, 0)) painter.setPen(pen) # Draw two lines for a double bond offset = 3 painter.drawLine(QPoint(atom1_x - offset, atom1_y - offset), QPoint(atom2_x - offset, atom2_y - offset)) painter.drawLine(QPoint(atom1_x + offset, atom1_y + offset), QPoint(atom2_x + offset, atom2_y + offset)) def draw_triple_bond(self, atom1_x: int, atom1_y: int, atom2_x: int, atom2_y: int, actual_bond: bool) -> None: painter = QPainter(self) pen = QPen() # Set colour black pen.setColor(QColor(0, 0, 0)) pen.setWidth(2) if not actual_bond: # Set colour red pen.setColor(QColor(255, 0, 0)) painter.setPen(pen) # Draw three lines for a triple bond offset = 4 painter.drawLine(QPoint(atom1_x, atom1_y), QPoint(atom2_x, atom2_y)) painter.drawLine(QPoint(atom1_x - offset, atom1_y - offset), QPoint(atom2_x - offset, atom2_y - offset)) painter.drawLine(QPoint(atom1_x + offset, atom1_y + offset), QPoint(atom2_x + offset, atom2_y + offset))
问题原因
当前双键和三键的绘制逻辑使用固定的±offset同时偏移x和y坐标,这种方式仅在**轴对齐方向(0°、90°、180°、270°)**有效。对于135°(左上,x减少、y增加)和315°(右下,x增加、y减少)的对角线方向:
- 两条偏移线会与主键线平行且视觉上重叠成一条线,看起来和单键无异
- 本质是固定的x/y偏移没有垂直于键的方向,导致辅助线无法区分开来
解决方案
修改draw_double_bond和draw_triple_bond方法,根据键的方向计算垂直偏移量,确保辅助线始终垂直于主键方向,从而在所有角度下都能正确显示双键/三键。
具体实现步骤:
- 计算键的方向向量
dx = atom2_x - atom1_x,dy = atom2_y - atom1_y - 计算垂直于键方向的单位向量:2D中向量
(dx, dy)的垂直向量为(-dy, dx)和(dy, -dx),归一化后乘以偏移量得到实际偏移坐标 - 使用偏移后的坐标绘制辅助线
修改后的代码示例:
import math def draw_double_bond(self, atom1_x: int, atom1_y: int, atom2_x: int, atom2_y: int, actual_bond: bool) -> None: painter = QPainter(self) pen = QPen() pen.setColor(QColor(0, 0, 0)) pen.setWidth(2) if not actual_bond: pen.setColor(QColor(255, 0, 0)) painter.setPen(pen) # 计算方向向量和垂直偏移 dx = atom2_x - atom1_x dy = atom2_y - atom1_y length = math.hypot(dx, dy) if length == 0: return # 避免除以0 # 垂直单位向量(两个方向) perp_x1 = -dy / length perp_y1 = dx / length perp_x2 = dy / length perp_y2 = -dx / length offset = 3 # 计算偏移后的起点和终点 start1_x = atom1_x + perp_x1 * offset start1_y = atom1_y + perp_y1 * offset end1_x = atom2_x + perp_x1 * offset end1_y = atom2_y + perp_y1 * offset start2_x = atom1_x + perp_x2 * offset start2_y = atom1_y + perp_y2 * offset end2_x = atom2_x + perp_x2 * offset end2_y = atom2_y + perp_y2 * offset painter.drawLine(QPoint(int(start1_x), int(start1_y)), QPoint(int(end1_x), int(end1_y))) painter.drawLine(QPoint(int(start2_x), int(start2_y)), QPoint(int(end2_x), int(end2_y))) def draw_triple_bond(self, atom1_x: int, atom1_y: int, atom2_x: int, atom2_y: int, actual_bond: bool) -> None: painter = QPainter(self) pen = QPen() pen.setColor(QColor(0, 0, 0)) pen.setWidth(2) if not actual_bond: pen.setColor(QColor(255, 0, 0)) painter.setPen(pen) # 先画主键 painter.drawLine(QPoint(atom1_x, atom1_y), QPoint(atom2_x, atom2_y)) # 计算方向向量和垂直偏移 dx = atom2_x - atom1_x dy = atom2_y - atom1_y length = math.hypot(dx, dy) if length == 0: return # 垂直单位向量 perp_x1 = -dy / length perp_y1 = dx / length perp_x2 = dy / length perp_y2 = -dx / length offset = 4 # 计算偏移后的起点和终点 start1_x = atom1_x + perp_x1 * offset start1_y = atom1_y + perp_y1 * offset end1_x = atom2_x + perp_x1 * offset end1_y = atom2_y + perp_y1 * offset start2_x = atom1_x + perp_x2 * offset start2_y = atom1_y + perp_y2 * offset end2_x = atom2_x + perp_x2 * offset end2_y = atom2_y + perp_y2 * offset painter.drawLine(QPoint(int(start1_x), int(start1_y)), QPoint(int(end1_x), int(end1_y))) painter.drawLine(QPoint(int(start2_x), int(start2_y)), QPoint(int(end2_x), int(end2_y)))
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

