digsim-logic-simulator 0.22.0__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.
- digsim/__init__.py +6 -0
- digsim/app/__main__.py +12 -0
- digsim/app/cli.py +68 -0
- digsim/app/gui/__init__.py +6 -0
- digsim/app/gui/_circuit_area.py +468 -0
- digsim/app/gui/_component_selection.py +154 -0
- digsim/app/gui/_main_window.py +163 -0
- digsim/app/gui/_top_bar.py +339 -0
- digsim/app/gui/_utils.py +26 -0
- digsim/app/gui/_warning_dialog.py +46 -0
- digsim/app/gui_objects/__init__.py +7 -0
- digsim/app/gui_objects/_bus_bit_object.py +94 -0
- digsim/app/gui_objects/_buzzer_object.py +97 -0
- digsim/app/gui_objects/_component_context_menu.py +79 -0
- digsim/app/gui_objects/_component_object.py +374 -0
- digsim/app/gui_objects/_component_port_item.py +63 -0
- digsim/app/gui_objects/_dip_switch_object.py +104 -0
- digsim/app/gui_objects/_gui_note_object.py +80 -0
- digsim/app/gui_objects/_gui_object_factory.py +80 -0
- digsim/app/gui_objects/_hexdigit_object.py +53 -0
- digsim/app/gui_objects/_image_objects.py +239 -0
- digsim/app/gui_objects/_label_object.py +97 -0
- digsim/app/gui_objects/_logic_analyzer_object.py +86 -0
- digsim/app/gui_objects/_seven_segment_object.py +131 -0
- digsim/app/gui_objects/_shortcut_objects.py +82 -0
- digsim/app/gui_objects/_yosys_object.py +32 -0
- digsim/app/gui_objects/images/AND.png +0 -0
- digsim/app/gui_objects/images/Analyzer.png +0 -0
- digsim/app/gui_objects/images/BUF.png +0 -0
- digsim/app/gui_objects/images/Buzzer.png +0 -0
- digsim/app/gui_objects/images/Clock.png +0 -0
- digsim/app/gui_objects/images/DFF.png +0 -0
- digsim/app/gui_objects/images/DIP_SWITCH.png +0 -0
- digsim/app/gui_objects/images/FlipFlop.png +0 -0
- digsim/app/gui_objects/images/IC.png +0 -0
- digsim/app/gui_objects/images/LED_OFF.png +0 -0
- digsim/app/gui_objects/images/LED_ON.png +0 -0
- digsim/app/gui_objects/images/MUX.png +0 -0
- digsim/app/gui_objects/images/NAND.png +0 -0
- digsim/app/gui_objects/images/NOR.png +0 -0
- digsim/app/gui_objects/images/NOT.png +0 -0
- digsim/app/gui_objects/images/ONE.png +0 -0
- digsim/app/gui_objects/images/OR.png +0 -0
- digsim/app/gui_objects/images/PB.png +0 -0
- digsim/app/gui_objects/images/Switch_OFF.png +0 -0
- digsim/app/gui_objects/images/Switch_ON.png +0 -0
- digsim/app/gui_objects/images/XNOR.png +0 -0
- digsim/app/gui_objects/images/XOR.png +0 -0
- digsim/app/gui_objects/images/YOSYS.png +0 -0
- digsim/app/gui_objects/images/ZERO.png +0 -0
- digsim/app/images/app_icon.png +0 -0
- digsim/app/model/__init__.py +6 -0
- digsim/app/model/_model.py +210 -0
- digsim/app/model/_model_components.py +162 -0
- digsim/app/model/_model_new_wire.py +57 -0
- digsim/app/model/_model_objects.py +155 -0
- digsim/app/model/_model_settings.py +35 -0
- digsim/app/model/_model_shortcuts.py +72 -0
- digsim/app/settings/__init__.py +8 -0
- digsim/app/settings/_component_settings.py +415 -0
- digsim/app/settings/_gui_settings.py +71 -0
- digsim/app/settings/_shortcut_dialog.py +39 -0
- digsim/circuit/__init__.py +7 -0
- digsim/circuit/_circuit.py +329 -0
- digsim/circuit/_waves_writer.py +61 -0
- digsim/circuit/components/__init__.py +26 -0
- digsim/circuit/components/_bus_bits.py +68 -0
- digsim/circuit/components/_button.py +44 -0
- digsim/circuit/components/_buzzer.py +45 -0
- digsim/circuit/components/_clock.py +54 -0
- digsim/circuit/components/_dip_switch.py +73 -0
- digsim/circuit/components/_flip_flops.py +99 -0
- digsim/circuit/components/_gates.py +246 -0
- digsim/circuit/components/_hexdigit.py +82 -0
- digsim/circuit/components/_ic.py +36 -0
- digsim/circuit/components/_label_wire.py +167 -0
- digsim/circuit/components/_led.py +18 -0
- digsim/circuit/components/_logic_analyzer.py +60 -0
- digsim/circuit/components/_mem64kbyte.py +42 -0
- digsim/circuit/components/_memstdout.py +37 -0
- digsim/circuit/components/_note.py +25 -0
- digsim/circuit/components/_on_off_switch.py +54 -0
- digsim/circuit/components/_seven_segment.py +28 -0
- digsim/circuit/components/_static_level.py +28 -0
- digsim/circuit/components/_static_value.py +44 -0
- digsim/circuit/components/_yosys_atoms.py +1353 -0
- digsim/circuit/components/_yosys_component.py +232 -0
- digsim/circuit/components/atoms/__init__.py +23 -0
- digsim/circuit/components/atoms/_component.py +280 -0
- digsim/circuit/components/atoms/_digsim_exception.py +8 -0
- digsim/circuit/components/atoms/_port.py +398 -0
- digsim/circuit/components/ic/74162.json +1331 -0
- digsim/circuit/components/ic/7448.json +834 -0
- digsim/storage_model/__init__.py +7 -0
- digsim/storage_model/_app.py +58 -0
- digsim/storage_model/_circuit.py +126 -0
- digsim/synth/__init__.py +6 -0
- digsim/synth/__main__.py +67 -0
- digsim/synth/_synthesis.py +156 -0
- digsim/utils/__init__.py +6 -0
- digsim/utils/_yosys_netlist.py +134 -0
- digsim_logic_simulator-0.22.0.dist-info/METADATA +140 -0
- digsim_logic_simulator-0.22.0.dist-info/RECORD +107 -0
- digsim_logic_simulator-0.22.0.dist-info/WHEEL +5 -0
- digsim_logic_simulator-0.22.0.dist-info/entry_points.txt +2 -0
- digsim_logic_simulator-0.22.0.dist-info/licenses/LICENSE.md +32 -0
- digsim_logic_simulator-0.22.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
|
+
# All rights reserved
|
|
3
|
+
|
|
4
|
+
"""A label component placed in the GUI"""
|
|
5
|
+
|
|
6
|
+
from PySide6.QtCore import QPoint, QRect, Qt
|
|
7
|
+
from PySide6.QtGui import QPen, QPolygon
|
|
8
|
+
|
|
9
|
+
from ._component_object import ComponentObject
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LabelObject(ComponentObject):
|
|
13
|
+
"""The class for a bus/bit component placed in the GUI"""
|
|
14
|
+
|
|
15
|
+
_LABEL_PEN = QPen(Qt.black)
|
|
16
|
+
|
|
17
|
+
def __init__(self, app_model, component, xpos, ypos):
|
|
18
|
+
super().__init__(app_model, component, xpos, ypos)
|
|
19
|
+
label = component.label()
|
|
20
|
+
label_w, label_h = self.get_string_metrics(label)
|
|
21
|
+
self.width = 2 * self.PORT_SIDE + label_w
|
|
22
|
+
self.height = self.PORT_SIDE + label_h
|
|
23
|
+
self._input = len(self._component.inports()) > 0
|
|
24
|
+
|
|
25
|
+
if self._input:
|
|
26
|
+
xpos = -ComponentObject.RECT_TO_BORDER
|
|
27
|
+
else:
|
|
28
|
+
xpos = self.width - self.PORT_SIDE - 1
|
|
29
|
+
rect = QRect(
|
|
30
|
+
self.object_pos.x() + xpos,
|
|
31
|
+
self.object_pos.y() + self.height / 2 - self.PORT_SIDE / 2,
|
|
32
|
+
self.PORT_SIDE,
|
|
33
|
+
self.PORT_SIDE,
|
|
34
|
+
)
|
|
35
|
+
port = self.component.port(label)
|
|
36
|
+
self.set_port_rect(port, rect)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def _paint_label(cls, painter, comp_rect, name, selected=False):
|
|
40
|
+
pen = cls._LABEL_PEN
|
|
41
|
+
if selected:
|
|
42
|
+
pen.setWidth(4)
|
|
43
|
+
else:
|
|
44
|
+
pen.setWidth(1)
|
|
45
|
+
painter.setPen(pen)
|
|
46
|
+
painter.setBrush(Qt.SolidPattern)
|
|
47
|
+
painter.setBrush(Qt.gray)
|
|
48
|
+
|
|
49
|
+
if "in" in name.lower():
|
|
50
|
+
points = QPolygon(
|
|
51
|
+
[
|
|
52
|
+
QPoint(comp_rect.x(), comp_rect.y() + comp_rect.height() / 2),
|
|
53
|
+
QPoint(comp_rect.x() + comp_rect.height() / 2, comp_rect.y()),
|
|
54
|
+
QPoint(comp_rect.x() + comp_rect.width(), comp_rect.y()),
|
|
55
|
+
QPoint(comp_rect.x() + comp_rect.width(), comp_rect.y() + comp_rect.height()),
|
|
56
|
+
QPoint(
|
|
57
|
+
comp_rect.x() + comp_rect.height() / 2, comp_rect.y() + comp_rect.height()
|
|
58
|
+
),
|
|
59
|
+
QPoint(comp_rect.x(), comp_rect.y() + comp_rect.height() / 2),
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
else:
|
|
63
|
+
points = QPolygon(
|
|
64
|
+
[
|
|
65
|
+
QPoint(comp_rect.x(), comp_rect.y()),
|
|
66
|
+
QPoint(
|
|
67
|
+
comp_rect.x() + comp_rect.width() - comp_rect.height() / 2, comp_rect.y()
|
|
68
|
+
),
|
|
69
|
+
QPoint(
|
|
70
|
+
comp_rect.x() + comp_rect.width(), comp_rect.y() + comp_rect.height() / 2
|
|
71
|
+
),
|
|
72
|
+
QPoint(
|
|
73
|
+
comp_rect.x() + comp_rect.width() - comp_rect.height() / 2,
|
|
74
|
+
comp_rect.y() + comp_rect.height(),
|
|
75
|
+
),
|
|
76
|
+
QPoint(comp_rect.x(), comp_rect.y() + comp_rect.height()),
|
|
77
|
+
QPoint(comp_rect.x(), comp_rect.y()),
|
|
78
|
+
]
|
|
79
|
+
)
|
|
80
|
+
painter.drawPolygon(points)
|
|
81
|
+
|
|
82
|
+
def paint_component(self, painter):
|
|
83
|
+
if self._input:
|
|
84
|
+
self._paint_label(painter, self.rect(), "sink", self.selected)
|
|
85
|
+
else:
|
|
86
|
+
self._paint_label(painter, self.rect(), "source", self.selected)
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def paint_selectable_component(cls, painter, size, name):
|
|
90
|
+
width = 40
|
|
91
|
+
height = 15
|
|
92
|
+
cls._paint_label(
|
|
93
|
+
painter,
|
|
94
|
+
QRect(size.width() / 2 - width / 2, size.height() / 2 - height / 2, width, height),
|
|
95
|
+
name,
|
|
96
|
+
)
|
|
97
|
+
cls.paint_selectable_component_name(painter, QPoint(0, 0), size, name)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
|
+
# All rights reserved
|
|
3
|
+
|
|
4
|
+
"""A Logic Analyzer component placed in the GUI"""
|
|
5
|
+
|
|
6
|
+
from PySide6.QtCore import QPoint, QRect, Qt
|
|
7
|
+
from PySide6.QtGui import QPainterPath, QPen
|
|
8
|
+
|
|
9
|
+
from ._image_objects import ImageObject
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LogicAnalyzerObject(ImageObject):
|
|
13
|
+
"""The class for a LogicAnalyzer component placed in the GUI"""
|
|
14
|
+
|
|
15
|
+
IMAGE_FILENAME = "images/Analyzer.png"
|
|
16
|
+
|
|
17
|
+
SIGNAL_NAME_WIDTH = 40
|
|
18
|
+
ANALYZER_DISPLAY_WIDTH = 200
|
|
19
|
+
|
|
20
|
+
_ANALYZER_PEN = QPen(Qt.green)
|
|
21
|
+
_SIGNAL_VERTICAL_SCALE = 10
|
|
22
|
+
_SIGNAL_HORIZONTAL_SCALE = 2
|
|
23
|
+
|
|
24
|
+
def __init__(self, app_model, component, xpos, ypos):
|
|
25
|
+
super().__init__(app_model, component, xpos, ypos)
|
|
26
|
+
self.width = self.SIGNAL_NAME_WIDTH + self.ANALYZER_DISPLAY_WIDTH + 2 * self.RECT_TO_BORDER
|
|
27
|
+
self.update_ports()
|
|
28
|
+
|
|
29
|
+
def paint_component(self, painter):
|
|
30
|
+
self.paint_component_base(painter)
|
|
31
|
+
painter.setBrush(Qt.SolidPattern)
|
|
32
|
+
painter.setBrush(Qt.black)
|
|
33
|
+
painter.drawRoundedRect(
|
|
34
|
+
QRect(
|
|
35
|
+
self.object_pos.x() + self.SIGNAL_NAME_WIDTH,
|
|
36
|
+
self.object_pos.y() + 2 * self.RECT_TO_BORDER,
|
|
37
|
+
self.ANALYZER_DISPLAY_WIDTH,
|
|
38
|
+
self.height - 4 * self.RECT_TO_BORDER,
|
|
39
|
+
),
|
|
40
|
+
5,
|
|
41
|
+
5,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
pen = self._ANALYZER_PEN
|
|
45
|
+
pen.setWidth(2)
|
|
46
|
+
painter.setPen(pen)
|
|
47
|
+
signal_data_dict = self.component.signal_data()
|
|
48
|
+
for portname, signal_data in signal_data_dict.items():
|
|
49
|
+
signal_data = signal_data_dict[portname]
|
|
50
|
+
port_pos = self.get_port_pos(portname)
|
|
51
|
+
path = QPainterPath(
|
|
52
|
+
QPoint(
|
|
53
|
+
self.object_pos.x() + self.SIGNAL_NAME_WIDTH,
|
|
54
|
+
port_pos.y() - signal_data[0] * self._SIGNAL_VERTICAL_SCALE,
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
last_level = signal_data[0]
|
|
58
|
+
for idx, level in enumerate(signal_data[1:]):
|
|
59
|
+
if level == last_level:
|
|
60
|
+
continue
|
|
61
|
+
path.lineTo(
|
|
62
|
+
QPoint(
|
|
63
|
+
self.object_pos.x()
|
|
64
|
+
+ self.SIGNAL_NAME_WIDTH
|
|
65
|
+
+ (idx + 1) * self._SIGNAL_HORIZONTAL_SCALE,
|
|
66
|
+
port_pos.y() - last_level * self._SIGNAL_VERTICAL_SCALE,
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
path.lineTo(
|
|
70
|
+
QPoint(
|
|
71
|
+
self.object_pos.x()
|
|
72
|
+
+ self.SIGNAL_NAME_WIDTH
|
|
73
|
+
+ (idx + 1) * self._SIGNAL_HORIZONTAL_SCALE,
|
|
74
|
+
port_pos.y() - level * self._SIGNAL_VERTICAL_SCALE,
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
last_level = level
|
|
78
|
+
path.lineTo(
|
|
79
|
+
QPoint(
|
|
80
|
+
self.object_pos.x()
|
|
81
|
+
+ self.SIGNAL_NAME_WIDTH
|
|
82
|
+
+ len(signal_data) * self._SIGNAL_HORIZONTAL_SCALE,
|
|
83
|
+
port_pos.y() - last_level * self._SIGNAL_VERTICAL_SCALE,
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
painter.drawPath(path)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
|
+
# All rights reserved
|
|
3
|
+
|
|
4
|
+
"""A 7-segment component placed in the GUI"""
|
|
5
|
+
|
|
6
|
+
from PySide6.QtCore import QPoint, Qt
|
|
7
|
+
from PySide6.QtGui import QPainterPath
|
|
8
|
+
|
|
9
|
+
from ._component_object import ComponentObject
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SevenSegmentObject(ComponentObject):
|
|
13
|
+
"""The class for a 7-segment component placed in the GUI"""
|
|
14
|
+
|
|
15
|
+
SEGMENT_TYPE_AND_POS = {
|
|
16
|
+
"A": ("H", QPoint(3, 0)),
|
|
17
|
+
"B": ("V", QPoint(31, 3)),
|
|
18
|
+
"C": ("V", QPoint(31, 33)),
|
|
19
|
+
"D": ("H", QPoint(3, 61)),
|
|
20
|
+
"E": ("V", QPoint(0, 33)),
|
|
21
|
+
"F": ("V", QPoint(0, 3)),
|
|
22
|
+
"G": ("H", QPoint(3, 31)),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
SEGMENT_CORDS = {
|
|
26
|
+
"V": [
|
|
27
|
+
QPoint(3, 3),
|
|
28
|
+
QPoint(3, 20),
|
|
29
|
+
QPoint(0, 25),
|
|
30
|
+
QPoint(-3, 20),
|
|
31
|
+
QPoint(-3, 3),
|
|
32
|
+
QPoint(0, 0),
|
|
33
|
+
],
|
|
34
|
+
"H": [
|
|
35
|
+
QPoint(3, 3),
|
|
36
|
+
QPoint(20, 3),
|
|
37
|
+
QPoint(25, 0),
|
|
38
|
+
QPoint(20, -3),
|
|
39
|
+
QPoint(3, -3),
|
|
40
|
+
QPoint(0, 0),
|
|
41
|
+
],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
PORT_TO_RECT_MARGIN = 5
|
|
45
|
+
RECT_TO_DIGIT_RECT_MARGIN = 5
|
|
46
|
+
DIGIT_RECT_TO_DIGIT_MARGIN = 10
|
|
47
|
+
DIGIT_WIDTH = 54
|
|
48
|
+
DIGIT_HEIGHT = 80
|
|
49
|
+
|
|
50
|
+
DOT_OFFSET_X = 40
|
|
51
|
+
DOT_OFFSET_Y = 60
|
|
52
|
+
DOT_RADIUS = 3
|
|
53
|
+
|
|
54
|
+
def __init__(self, app_model, component, xpos, ypos, port_distance=10):
|
|
55
|
+
super().__init__(app_model, component, xpos, ypos, port_distance=port_distance)
|
|
56
|
+
self.setup_size()
|
|
57
|
+
|
|
58
|
+
def setup_size(self):
|
|
59
|
+
"""Setup the size of the component"""
|
|
60
|
+
str_pixels_w, _ = self.get_string_metrics("dot")
|
|
61
|
+
self.digit_left = self.inport_x_pos() + str_pixels_w + self.PORT_TO_RECT_MARGIN
|
|
62
|
+
self.width = self.digit_left + self.DIGIT_WIDTH + self.RECT_TO_DIGIT_RECT_MARGIN
|
|
63
|
+
self.update_ports()
|
|
64
|
+
|
|
65
|
+
def paint_component(self, painter):
|
|
66
|
+
self.paint_component_base(painter)
|
|
67
|
+
digit_ypos = self.height / 2 - self.DIGIT_HEIGHT / 2
|
|
68
|
+
self.paint_digit_rect(
|
|
69
|
+
painter, self.object_pos.x() + self.digit_left, self.object_pos.y() + digit_ypos
|
|
70
|
+
)
|
|
71
|
+
active_segments = self.component.segments()
|
|
72
|
+
self.draw_digit(
|
|
73
|
+
painter,
|
|
74
|
+
self.object_pos.x() + self.digit_left,
|
|
75
|
+
self.object_pos.y() + digit_ypos,
|
|
76
|
+
active_segments,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def paint_selectable_component(cls, painter, size, name):
|
|
81
|
+
cls.paint_selectable_digit(painter, size, name, "ADEFG")
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def paint_selectable_digit(cls, painter, size, name, segments):
|
|
85
|
+
"""Paint a digit (for selectable component in gui"""
|
|
86
|
+
xpos = size.width() / 2 - cls.DIGIT_WIDTH / 2
|
|
87
|
+
cls.paint_digit_rect(painter, xpos, 0)
|
|
88
|
+
cls.draw_digit(painter, xpos, 0, segments)
|
|
89
|
+
cls.paint_selectable_component_name(painter, QPoint(0, 0), size, name)
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def paint_digit_rect(cls, painter, xpos, ypos, digits=1):
|
|
93
|
+
"""Paint the digit background"""
|
|
94
|
+
painter.setBrush(Qt.SolidPattern)
|
|
95
|
+
painter.setPen(Qt.black)
|
|
96
|
+
painter.drawRoundedRect(xpos, ypos, cls.DIGIT_WIDTH * digits, cls.DIGIT_HEIGHT, 4, 4)
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def draw_digit(cls, painter, xpos, ypos, active_segments):
|
|
100
|
+
"""Paint the LED digit segments"""
|
|
101
|
+
start_point = QPoint(
|
|
102
|
+
xpos + cls.DIGIT_RECT_TO_DIGIT_MARGIN, ypos + cls.DIGIT_RECT_TO_DIGIT_MARGIN
|
|
103
|
+
)
|
|
104
|
+
for seg, type_pos in cls.SEGMENT_TYPE_AND_POS.items():
|
|
105
|
+
if seg in active_segments:
|
|
106
|
+
painter.setPen(Qt.red)
|
|
107
|
+
painter.setBrush(Qt.red)
|
|
108
|
+
else:
|
|
109
|
+
painter.setPen(Qt.darkRed)
|
|
110
|
+
painter.setBrush(Qt.darkRed)
|
|
111
|
+
pos_vector = cls.SEGMENT_CORDS[type_pos[0]]
|
|
112
|
+
offset = type_pos[1]
|
|
113
|
+
path = QPainterPath()
|
|
114
|
+
path.moveTo(offset + start_point)
|
|
115
|
+
for point in pos_vector:
|
|
116
|
+
path.lineTo(offset + point + start_point)
|
|
117
|
+
path.closeSubpath()
|
|
118
|
+
painter.drawPath(path)
|
|
119
|
+
|
|
120
|
+
DOT_OFFSET_X = 40
|
|
121
|
+
DOT_OFFSET_Y = 60
|
|
122
|
+
DOT_RADIUS = 3
|
|
123
|
+
|
|
124
|
+
if "." in active_segments:
|
|
125
|
+
painter.setPen(Qt.red)
|
|
126
|
+
painter.setBrush(Qt.red)
|
|
127
|
+
else:
|
|
128
|
+
painter.setPen(Qt.darkRed)
|
|
129
|
+
painter.setBrush(Qt.darkRed)
|
|
130
|
+
dotPoint = QPoint(DOT_OFFSET_X, DOT_OFFSET_Y) + start_point
|
|
131
|
+
painter.drawEllipse(dotPoint, DOT_RADIUS, DOT_RADIUS)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
|
+
# All rights reserved
|
|
3
|
+
|
|
4
|
+
"""A button component with an image as symbol the GUI"""
|
|
5
|
+
|
|
6
|
+
from functools import partial
|
|
7
|
+
|
|
8
|
+
from PySide6.QtCore import Qt
|
|
9
|
+
from PySide6.QtGui import QAction, QPen
|
|
10
|
+
|
|
11
|
+
from digsim.app.settings import ShortcutDialog
|
|
12
|
+
|
|
13
|
+
from ._image_objects import ImageObject, ImageObjectWithActiveRect
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _ShortcutObject:
|
|
17
|
+
"""The class for a ShortcutObject component placed in the GUI"""
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def add_context_menu_action(cls, menu, parent, app_model, component):
|
|
21
|
+
"""Add context menu action"""
|
|
22
|
+
shortcutAction = QAction("Shortcut", menu)
|
|
23
|
+
menu.addAction(shortcutAction)
|
|
24
|
+
shortcutAction.triggered.connect(partial(cls._shortcut, parent, app_model, component))
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def _shortcut(cls, parent, app_model, component):
|
|
28
|
+
shortcutDialog = ShortcutDialog(parent, app_model)
|
|
29
|
+
key = shortcutDialog.start()
|
|
30
|
+
if key is not None:
|
|
31
|
+
app_model.shortcuts.set_component(key, component)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ButtonObject(ImageObject):
|
|
35
|
+
"""The class for a PushButton image component placed in the GUI"""
|
|
36
|
+
|
|
37
|
+
IMAGE_FILENAME = "images/PB.png"
|
|
38
|
+
BUTTON_RADIUS = 35
|
|
39
|
+
|
|
40
|
+
_BUTTON_ACTIVE_PEN = QPen(Qt.green)
|
|
41
|
+
|
|
42
|
+
def __init__(self, app_model, component, xpos, ypos):
|
|
43
|
+
super().__init__(app_model, component, xpos, ypos)
|
|
44
|
+
self.show_name(False)
|
|
45
|
+
self.paint_port_names(False)
|
|
46
|
+
|
|
47
|
+
def paint_component(self, painter):
|
|
48
|
+
super().paint_component(painter)
|
|
49
|
+
if self.component.active:
|
|
50
|
+
pen = self._BUTTON_ACTIVE_PEN
|
|
51
|
+
pen.setWidth(4)
|
|
52
|
+
painter.setPen(pen)
|
|
53
|
+
painter.setBrush(Qt.NoBrush)
|
|
54
|
+
painter.drawEllipse(
|
|
55
|
+
self.object_pos.x() + self.width / 2 - self.BUTTON_RADIUS,
|
|
56
|
+
self.object_pos.y() + self.height / 2 - self.BUTTON_RADIUS,
|
|
57
|
+
self.BUTTON_RADIUS * 2,
|
|
58
|
+
self.BUTTON_RADIUS * 2,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def add_context_menu_action(self, menu, parent):
|
|
62
|
+
_ShortcutObject.add_context_menu_action(menu, parent, self._app_model, self._component)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class OnOffSwitchObject(ImageObjectWithActiveRect):
|
|
66
|
+
"""The class for a On/Off-Switch image component placed in the GUI"""
|
|
67
|
+
|
|
68
|
+
IMAGE_FILENAME = "images/Switch_OFF.png"
|
|
69
|
+
ACTIVE_IMAGE_FILENAME = "images/Switch_ON.png"
|
|
70
|
+
|
|
71
|
+
def single_click_action(self):
|
|
72
|
+
self._toggle()
|
|
73
|
+
|
|
74
|
+
def add_context_menu_action(self, menu, parent):
|
|
75
|
+
_ShortcutObject.add_context_menu_action(menu, parent, self._app_model, self._component)
|
|
76
|
+
toggleAction = QAction("Toggle Switch", menu)
|
|
77
|
+
menu.addAction(toggleAction)
|
|
78
|
+
toggleAction.triggered.connect(self._toggle)
|
|
79
|
+
|
|
80
|
+
def _toggle(self):
|
|
81
|
+
self.component.toggle()
|
|
82
|
+
self.repaint()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Copyright (c) Fredrik Andersson, 2023-2025
|
|
2
|
+
# All rights reserved
|
|
3
|
+
|
|
4
|
+
"""A yosys component with an image as symbol the GUI"""
|
|
5
|
+
|
|
6
|
+
from PySide6.QtGui import QAction
|
|
7
|
+
|
|
8
|
+
from digsim.circuit.components.atoms import DigsimException
|
|
9
|
+
|
|
10
|
+
from ._image_objects import ImageObject
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class YosysObject(ImageObject):
|
|
14
|
+
"""The class for a Yosys image component placed in the GUI"""
|
|
15
|
+
|
|
16
|
+
IMAGE_FILENAME = "images/YOSYS.png"
|
|
17
|
+
|
|
18
|
+
def paint_component(self, painter):
|
|
19
|
+
self.paint_component_base(painter)
|
|
20
|
+
self.paint_component_name(painter)
|
|
21
|
+
|
|
22
|
+
def add_context_menu_action(self, menu, parent):
|
|
23
|
+
reloadAction = QAction("Reload", menu)
|
|
24
|
+
menu.addAction(reloadAction)
|
|
25
|
+
reloadAction.triggered.connect(self._reload)
|
|
26
|
+
|
|
27
|
+
def _reload(self):
|
|
28
|
+
try:
|
|
29
|
+
self.component.reload_file()
|
|
30
|
+
except DigsimException as exc:
|
|
31
|
+
self._app_model.sig_warning_log.emit("Reload Yosys Warning", str(exc))
|
|
32
|
+
self._app_model.model_reset()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|