fix all ui

This commit is contained in:
remilia 2025-01-20 19:28:03 +08:00
parent eb2a316100
commit c2ed707a43
8 changed files with 232 additions and 62 deletions

View File

@ -9,7 +9,8 @@ import zmq
from config import PLAYBACK_SOCKET, VIDEO_WIDTH, VIDEO_HEIGHT, LIVE_SOCKET
from nodes.Node import Node
from utils.Msg import BMMsg, ImageArgMsg, KillMsg, SeqMetaMsg, SetPlayMode, SetDeviceConfigMsg, SetRecordMsg
from utils.Msg import BMMsg, ImageArgMsg, KillMsg, SeqMetaMsg, SetPlayMode, SetDeviceConfigMsg, SetRecordMsg, \
RecordFrameMsg
from utils.RfFile import RfFrame, RfSequenceMeta
from utils.RfMat import RfMat
@ -22,10 +23,12 @@ class Beamformer(Node):
def __init__(self):
super(Beamformer, self).__init__()
self.arg = ImageArgMsg('', t_start=0, t_end=1499)
self.seq_meta = None
self.seq_meta_live = None
self.seq_meta_playback = None
self.record_enable = False
self.record_commit = None
self.record_path: Path | None = None
self.play_mode = None
def process(self, data: RfMat):
if data is None:
@ -41,10 +44,19 @@ class Beamformer(Node):
)
self.send(BMMsg(0, d2.__bytes__()))
@property
def current_seq_meta(self):
seq_meta = None
match self.play_mode:
case 'live':
seq_meta = self.seq_meta_live
case 'playback':
seq_meta = self.seq_meta_playback
return seq_meta
def loop(self):
device_socket = self.context.socket(zmq.PULL)
device_socket.setsockopt(zmq.CONFLATE, 1)
device_socket.connect(f"tcp://{PLAYBACK_SOCKET}")
self.c.poller.register(device_socket, zmq.POLLIN)
mat = None
while True:
@ -53,15 +65,24 @@ class Beamformer(Node):
buffer = device_socket.recv()
_, sequence_id, encoder = struct.unpack_from('=iqi', buffer)
s = buffer[4 + 8 + 4:]
if self.seq_meta is not None:
seq_meta = None
match self.play_mode:
case 'live':
seq_meta = self.seq_meta_live
case 'playback':
seq_meta = self.seq_meta_playback
if seq_meta is not None:
fb1 = cp.frombuffer(s, dtype=cp.int16)
if fb1.shape[0] == np.prod(self.seq_meta.shape):
mat = RfMat(fb1.reshape(self.seq_meta.shape), RfFrame.RfFrameMeta(
if fb1.shape[0] == np.prod(seq_meta.shape):
mat = RfMat(fb1.reshape(seq_meta.shape), RfFrame.RfFrameMeta(
encoder=encoder,
sequence_id=sequence_id,
), self.seq_meta)
), seq_meta)
if self.record_enable:
(self.record_path / mat.frame_meta.filename).write_bytes(mat.__bytes__())
bs = mat.__bytes__()
(self.record_path / mat.frame_meta.filename).write_bytes(bs)
self.send(RecordFrameMsg(bs.__len__(), sequence_id))
if self.c.sub in socks and socks[self.c.sub] == zmq.POLLIN:
msg = self.recv()
@ -69,22 +90,34 @@ class Beamformer(Node):
if msg.name == '':
return
if isinstance(msg, SeqMetaMsg):
self.seq_meta = RfSequenceMeta.from_name(msg.s)
match msg.target:
case 'live':
self.seq_meta_live = RfSequenceMeta.from_name(msg.name)
case 'playback':
self.seq_meta_playback = RfSequenceMeta.from_name(msg.name)
if isinstance(msg, ImageArgMsg):
self.arg = msg
if isinstance(msg, SetPlayMode):
self.play_mode = msg.value
if msg.value == 'live':
device_socket.disconnect(f"tcp://{PLAYBACK_SOCKET}")
try:
device_socket.disconnect(f"tcp://{PLAYBACK_SOCKET}")
except:
pass
device_socket.connect(f"tcp://{LIVE_SOCKET}")
elif msg.value == 'playback':
device_socket.disconnect(f"tcp://{LIVE_SOCKET}")
try:
device_socket.disconnect(f"tcp://{LIVE_SOCKET}")
except:
pass
device_socket.connect(f"tcp://{PLAYBACK_SOCKET}")
if isinstance(msg, SetRecordMsg):
self.record_enable = msg.enable
if msg.enable:
seq_meta = self.current_seq_meta
self.record_commit = msg.commit
self.seq_meta.commit = msg.commit
p = Path('/tmp/flandre_record') / self.seq_meta.name
seq_meta.commit = msg.commit
p = Path(msg.base) / seq_meta.name
p.mkdir(parents=True, exist_ok=True)
self.record_path = p

View File

@ -38,7 +38,7 @@ class Loader(Node):
if base is None:
continue
rff = RfSequence.from_folder(base / r.value)
self.send(SeqMetaMsg(rff.meta.name))
self.send(SeqMetaMsg('playback', rff.meta.name))
self.send(SeqIdMinMax(*rff.seq_id_minmax))
elif isinstance(r, SetBaseMsg):
base = Path(r.value)

View File

@ -1,10 +1,11 @@
import logging
import sys
from enum import Enum, auto
from pathlib import Path
from PyQt6 import QtCore
from PyQt6.QtCore import QByteArray
from PyQt6.QtWidgets import QMainWindow, QApplication, QFrame
from PyQt6.QtWidgets import QMainWindow, QApplication, QFrame, QMessageBox
from Main import Ui_MainWindow
from ZMQReceiver import ZMQReceiver
@ -12,7 +13,7 @@ from config import DS
from nodes.Node import Node
from utils.Msg import KillMsg, Msg, ImageArgMsg, SelectSeqMsg, SeqIdMinMax, MoveAxisMsg, SeqListMsg, SetBaseMsg, \
SeqMetaMsg, SetPlayMode, DeviceConnectedMsg, DeviceEnabledMsg, DeviceOnlineMsg, SetDeviceEnabledMsg, \
SetDeviceConnectedMsg, DeviceConfigListMsg, SetDeviceConfigMsg, SetRecordMsg, RobotRtsiMsg
SetDeviceConnectedMsg, DeviceConfigListMsg, SetDeviceConfigMsg, SetRecordMsg, RobotRtsiMsg, RecordFrameMsg
from utils.RfFile import RfSequenceMeta
@ -22,6 +23,26 @@ class LinkStatus(Enum):
GREEN = auto()
def humanbytes(B):
"""Return the given bytes as a human friendly KB, MB, GB, or TB string."""
B = float(B)
KB = float(1024)
MB = float(KB ** 2) # 1,048,576
GB = float(KB ** 3) # 1,073,741,824
TB = float(KB ** 4) # 1,099,511,627,776
if B < KB:
return '{0} {1}'.format(B, 'Bytes' if 0 == B > 1 else 'Byte')
elif KB <= B < MB:
return '{0:.2f} KB'.format(B / KB)
elif MB <= B < GB:
return '{0:.2f} MB'.format(B / MB)
elif GB <= B < TB:
return '{0:.2f} GB'.format(B / GB)
elif TB <= B:
return '{0:.2f} TB'.format(B / TB)
class Adv(QMainWindow, Ui_MainWindow):
def __init__(self, p: Node, parent=None):
super(Adv, self).__init__(parent)
@ -35,7 +56,7 @@ class Adv(QMainWindow, Ui_MainWindow):
self.c_playback_seq_name.currentIndexChanged.connect(self.cbc)
self.s_sid.valueChanged.connect(self.c_sid)
self.arg = ImageArgMsg('ui', t_start=0, t_end=1499)
self.b_base.clicked.connect(lambda: self.p.send(SetBaseMsg(self.l_base.text())))
self.b_base.clicked.connect(self.on_base)
self.seq_meta: RfSequenceMeta | None = None
self.b_play_live.clicked.connect(self.on_play_live)
self.b_play_playback.clicked.connect(self.on_play_playback)
@ -45,9 +66,24 @@ class Adv(QMainWindow, Ui_MainWindow):
self.device_enabled = False
# self.b_device_enabled.clicked.connect(lambda: self.p.send(SetDeviceEnabledMsg(not self.device_enabled)))
# self.b_device_connected.clicked.connect(lambda: self.p.send(SetDeviceConnectedMsg(not self.device_connected)))
self.c_seq_meta.currentIndexChanged.connect(self.on_m)
self.c_seq_meta.currentIndexChanged.connect(self.on_seq_meta)
self.l_base.setText(DS.__str__())
self.b1, self.b2 = False, False
self.b3, self.b4 = False, False
self.record_size_cnt = 0
self.record_frame_cnt = 0
def update_b_play_live_enabled(self):
self.b_play_live.setEnabled(all([self.b1, self.b2]))
def on_base(self):
if Path(self.l_base.text()).exists():
self.p.send(SetBaseMsg(self.l_base.text()))
self.b1 = True
self.update_b_play_live_enabled()
def set_device_connection(self, status: LinkStatus):
match status:
case LinkStatus.RED:
@ -78,23 +114,28 @@ class Adv(QMainWindow, Ui_MainWindow):
self.lb_device_enable.setText('Enabled')
self.lb_device_enable.setStyleSheet('background-color: LightGreen;')
def update_c_playback_seq_name_enable(self):
self.c_playback_seq_name.setEnabled(all([self.b3, self.b4]))
def on_play_live(self):
self.b_play_live.setStyleSheet('background-color: red;')
self.b_play_playback.setStyleSheet('')
self.b_record.setEnabled(True)
self.l_record_commit.setEnabled(True)
self.p.send(SeqMetaMsg(self.c_seq_meta.itemText(self.c_seq_meta.currentIndex())))
self.p.send(SeqMetaMsg('live', self.c_seq_meta.itemText(self.c_seq_meta.currentIndex())))
self.p.send(SetPlayMode('live'))
self.c_playback_seq_name.setEnabled(False)
self.b3 = False
self.update_c_playback_seq_name_enable()
def on_play_playback(self):
self.b_play_live.setStyleSheet('')
self.b_play_playback.setStyleSheet('background-color: red;')
self.b_record.setEnabled(False)
self.l_record_commit.setEnabled(False)
self.p.send(SeqMetaMsg(self.c_playback_seq_name.itemText(self.c_playback_seq_name.currentIndex())))
self.p.send(SeqMetaMsg('playback', self.c_playback_seq_name.itemText(self.c_playback_seq_name.currentIndex())))
self.p.send(SetPlayMode('playback'))
self.c_playback_seq_name.setEnabled(True)
self.b3 = True
self.update_c_playback_seq_name_enable()
def on_device_disable(self):
self.p.send(SetDeviceEnabledMsg(False))
@ -165,15 +206,20 @@ class Adv(QMainWindow, Ui_MainWindow):
def on_record(self):
if self.record:
self.l_record_commit.setEnabled(True)
self.p.send(SetRecordMsg(False))
self.record = False
self.b_record.setStyleSheet('')
else:
self.l_record_commit.setEnabled(False)
if self.l_record_commit.text() != '':
self.p.send(SetRecordMsg(True, self.l_record_commit.text()))
self.record_size_cnt = 0
self.record_frame_cnt = 0
self.p.send(SetRecordMsg(True, self.l_record_commit.text(), self.l_base.text()))
self.record = True
self.b_record.setStyleSheet('background-color: red;')
else:
QMessageBox.warning(None, 'hint', 'Commit is empty!!')
def on_zmq_event(self, msg: QByteArray):
msg = Msg.decode_msg(msg.data())
@ -190,9 +236,13 @@ class Adv(QMainWindow, Ui_MainWindow):
elif isinstance(msg, SeqIdMinMax):
self.s_sid.setMinimum(msg.min)
self.s_sid.setMaximum(msg.max)
self.s_sid.setValue(msg.min)
self.s_sid.setMinimum(msg.min)
self.sp_sid.setMaximum(msg.max)
self.sp_sid.setValue(msg.min)
self.sp_sid.setValue(msg.min)
elif isinstance(msg, SeqListMsg):
self.c_playback_seq_name.setEnabled(True)
self.b4 = True
self.update_c_playback_seq_name_enable()
self.c_playback_seq_name.clear()
for name in msg.value:
self.c_playback_seq_name.addItem(name)
@ -200,7 +250,7 @@ class Adv(QMainWindow, Ui_MainWindow):
self.p.send(SelectSeqMsg(self.c_playback_seq_name.itemText(self.c_playback_seq_name.currentIndex())))
self.b_play_playback.setEnabled(True)
elif isinstance(msg, SeqMetaMsg):
self.seq_meta = RfSequenceMeta.from_name(msg.s)
self.seq_meta = RfSequenceMeta.from_name(msg.name)
self.s_t_start.setMaximum(max(self.seq_meta.shape))
self.s_t_end.setMaximum(max(self.seq_meta.shape))
elif isinstance(msg, DeviceConnectedMsg):
@ -228,6 +278,13 @@ class Adv(QMainWindow, Ui_MainWindow):
elif isinstance(msg, RobotRtsiMsg):
self.l_robot_pos.setText(','.join([f'{v:.3f}' for v in msg.pos]))
self.l_robot_force.setText(','.join([f'{v:.3f}' for v in msg.force]))
elif isinstance(msg, RecordFrameMsg):
self.record_frame_cnt += 1
self.record_size_cnt += msg.size
self.l_record_size.setText(humanbytes(self.record_size_cnt))
self.l_record_frames.setText(str(self.record_frame_cnt))
self.l_record_max_sid.setText(str(msg.current_sid))
def closeEvent(self, event):
self.p.send(KillMsg(''))
@ -254,21 +311,25 @@ class Adv(QMainWindow, Ui_MainWindow):
@QtCore.pyqtSlot(int)
def c_sid(self, v):
if self.s_sid.sender() is None:
self.sp_sid.setValue(v)
self.p.send(MoveAxisMsg('ui', 'S', v))
@QtCore.pyqtSlot(int)
def on_m(self, v):
def on_seq_meta(self, v):
if self.c_seq_meta.sender() is None or isinstance(self.c_seq_meta.sender(), QFrame):
if self.c_seq_meta.itemText(v) != 'Empty':
self.b2 = True
self.update_b_play_live_enabled()
self.p.send(SetDeviceConfigMsg(self.c_seq_meta.itemData(v)))
self.p.send(SeqMetaMsg(self.c_seq_meta.itemText(v)))
self.p.send(SeqMetaMsg('live', self.c_seq_meta.itemText(v)))
class MainUI(Node):
topics = [ImageArgMsg, SeqIdMinMax, MoveAxisMsg,
SeqListMsg, SeqMetaMsg,
DeviceConnectedMsg, DeviceEnabledMsg, DeviceOnlineMsg, DeviceConfigListMsg,
RobotRtsiMsg]
RobotRtsiMsg,
RecordFrameMsg]
def __init__(self, level=logging.INFO):
super().__init__(level=level)

View File

@ -26,12 +26,11 @@ class Ui_MainWindow(object):
self.label_4 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_4.setObjectName("label_4")
self.gridLayout_7.addWidget(self.label_4, 2, 0, 1, 1)
self.spinBox_7 = QtWidgets.QSpinBox(parent=self.centralwidget)
self.spinBox_7.setObjectName("spinBox_7")
self.gridLayout_7.addWidget(self.spinBox_7, 2, 2, 1, 1)
self.sp_sid = QtWidgets.QSpinBox(parent=self.centralwidget)
self.sp_sid.setObjectName("sp_sid")
self.gridLayout_7.addWidget(self.sp_sid, 2, 2, 1, 1)
self.b_play_playback = QtWidgets.QPushButton(parent=self.centralwidget)
self.b_play_playback.setEnabled(False)
self.b_play_playback.setStyleSheet("background-color : red")
self.b_play_playback.setObjectName("b_play_playback")
self.gridLayout_7.addWidget(self.b_play_playback, 0, 0, 1, 3)
self.c_playback_seq_name = QtWidgets.QComboBox(parent=self.centralwidget)
@ -50,19 +49,41 @@ class Ui_MainWindow(object):
self.l_record_commit.setEnabled(False)
self.l_record_commit.setObjectName("l_record_commit")
self.gridLayout_6.addWidget(self.l_record_commit, 1, 1, 1, 1)
self.label_17 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_17.setObjectName("label_17")
self.gridLayout_6.addWidget(self.label_17, 1, 0, 1, 1)
self.b_record = QtWidgets.QPushButton(parent=self.centralwidget)
self.b_record.setEnabled(False)
self.b_record.setObjectName("b_record")
self.gridLayout_6.addWidget(self.b_record, 1, 2, 1, 1)
self.label_17 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_17.setObjectName("label_17")
self.gridLayout_6.addWidget(self.label_17, 1, 0, 1, 1)
self.b_play_live = QtWidgets.QPushButton(parent=self.centralwidget)
self.b_play_live.setObjectName("b_play_live")
self.gridLayout_6.addWidget(self.b_play_live, 0, 0, 1, 3)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_18 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_18.setObjectName("label_18")
self.gridLayout_6.addWidget(self.label_18, 2, 0, 1, 3)
self.horizontalLayout_2.addWidget(self.label_18)
self.l_record_frames = QtWidgets.QLabel(parent=self.centralwidget)
self.l_record_frames.setObjectName("l_record_frames")
self.horizontalLayout_2.addWidget(self.l_record_frames)
self.label_25 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_25.setObjectName("label_25")
self.horizontalLayout_2.addWidget(self.label_25)
self.l_record_size = QtWidgets.QLabel(parent=self.centralwidget)
self.l_record_size.setObjectName("l_record_size")
self.horizontalLayout_2.addWidget(self.l_record_size)
self.label_26 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_26.setObjectName("label_26")
self.horizontalLayout_2.addWidget(self.label_26)
self.l_record_max_sid = QtWidgets.QLabel(parent=self.centralwidget)
self.l_record_max_sid.setObjectName("l_record_max_sid")
self.horizontalLayout_2.addWidget(self.l_record_max_sid)
self.horizontalLayout_2.setStretch(1, 1)
self.horizontalLayout_2.setStretch(3, 1)
self.horizontalLayout_2.setStretch(5, 1)
self.gridLayout_6.addLayout(self.horizontalLayout_2, 2, 0, 1, 3)
self.b_play_live = QtWidgets.QPushButton(parent=self.centralwidget)
self.b_play_live.setEnabled(False)
self.b_play_live.setObjectName("b_play_live")
self.gridLayout_6.addWidget(self.b_play_live, 0, 0, 1, 3)
self.gridLayout.addLayout(self.gridLayout_6, 1, 0, 1, 1)
self.gridLayout_4 = QtWidgets.QGridLayout()
self.gridLayout_4.setObjectName("gridLayout_4")
@ -316,10 +337,15 @@ class Ui_MainWindow(object):
self.b_play_playback.setText(_translate("MainWindow", "Playback"))
self.c_playback_seq_name.setItemText(0, _translate("MainWindow", "Unset"))
self.label_16.setText(_translate("MainWindow", "SeqName"))
self.b_record.setText(_translate("MainWindow", "Record"))
self.label_17.setText(_translate("MainWindow", "RecordName"))
self.b_record.setText(_translate("MainWindow", "Record"))
self.label_18.setText(_translate("MainWindow", "Record Frames:"))
self.l_record_frames.setText(_translate("MainWindow", "0"))
self.label_25.setText(_translate("MainWindow", "Record Size:"))
self.l_record_size.setText(_translate("MainWindow", "0"))
self.label_26.setText(_translate("MainWindow", "Max SeqId:"))
self.l_record_max_sid.setText(_translate("MainWindow", "0"))
self.b_play_live.setText(_translate("MainWindow", "Live"))
self.label_18.setText(_translate("MainWindow", "Space left 1000GB"))
self.label_14.setText(_translate("MainWindow", "Yal"))
self.label_13.setText(_translate("MainWindow", "Pitch"))
self.label_3.setText(_translate("MainWindow", "Probe Position"))

View File

@ -32,16 +32,13 @@
</widget>
</item>
<item row="2" column="2">
<widget class="QSpinBox" name="spinBox_7"/>
<widget class="QSpinBox" name="sp_sid"/>
</item>
<item row="0" column="0" colspan="3">
<widget class="QPushButton" name="b_play_playback">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color : red</string>
</property>
<property name="text">
<string>Playback</string>
</property>
@ -80,6 +77,13 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>RecordName</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="b_record">
<property name="enabled">
@ -90,27 +94,62 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>RecordName</string>
</property>
</widget>
<item row="2" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0,1,0,1">
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Record Frames:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_record_frames">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_25">
<property name="text">
<string>Record Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_record_size">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_26">
<property name="text">
<string>Max SeqId:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_record_max_sid">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="3">
<widget class="QPushButton" name="b_play_live">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Live</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Space left 1000GB</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">

View File

@ -28,6 +28,7 @@ class BG(Enum):
DeviceConfigListMsg = auto()
SetRecordMsg = auto()
RobotRtsiMsg = auto()
RecordFrameMsg = auto()
class Msg:
@ -116,6 +117,7 @@ class DeviceOnlineMsg(BoolMsg):
class SetRecordMsg(Msg):
enable: bool
commit: str = ''
base: str = ''
class SetDeviceConfigMsg(StrMsg):
@ -141,7 +143,8 @@ class SetPlayMode(StrMsg):
@dataclasses.dataclass
class SeqMetaMsg(Msg):
s: str = ''
target: str
name: str
@dataclasses.dataclass
@ -169,6 +172,12 @@ class DeviceConfigListMsg(Msg):
arr: list[tuple[str, str]]
@dataclasses.dataclass
class RecordFrameMsg(Msg):
size: int
current_sid: int
class BMMsg(Msg):
def __init__(self, t: int, data: bytes):
self.data = data

View File

@ -232,7 +232,7 @@ class RfSequence:
@property
def seq_id_minmax(self):
mmin = 1000000
mmin = 2**32
mmax = 0
for f in self.frames:
mmin = min(mmin, f.meta.sequence_id)

View File

@ -4331,6 +4331,8 @@ name = "pyqt6-qt6"
version = "6.8.1"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/19/b89eb6cecbdf1e65a44658a083693a967e9d428370026711b624e928a8ca/PyQt6_Qt6-6.8.1-1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:2f4b8b55b1414b93f340f22e8c88d25550efcdebc4b65a3927dd947b73bd4358", size = 80877444 },
{ url = "https://files.pythonhosted.org/packages/87/1b/94d3710ee7ef93ee99c1dac512f631de5e310f6b21e43f474ef269f840b6/PyQt6_Qt6-6.8.1-1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:98aa99fe38ae68c5318284cd28f3479ba538c40bf6ece293980abae0925c1b24", size = 79473622 },
{ url = "https://files.pythonhosted.org/packages/df/0a/c47a1cc272b418faff8af79b121f0cecd32b09d634253254e3a990432220/PyQt6_Qt6-6.8.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:1eb8460a1fdb38d0b2458c2974c01d471c1e59e4eb19ea63fc447aaba3ad530e", size = 65754939 },
{ url = "https://files.pythonhosted.org/packages/b1/e6/cc4fbc97a7d0955185e33add3ce00480f0023424d17ac6f864a504f60251/PyQt6_Qt6-6.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f3790c4ce4dc576e48b8718d55fb8743057e6cbd53a6ca1dd253ffbac9b7287", size = 59956028 },
{ url = "https://files.pythonhosted.org/packages/01/22/c2997fe76d765d9ba960e9a099238cb419a316362bdde50fedacc23e7c7d/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d6ca5d2b9d2ec0ee4a814b2175f641a5c4299cb80b45e0f5f8356632663f89b3", size = 72561636 },