From 2aff684cc43598ca1ed79637acadce626b35f434 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 2 Sep 2026 09:16:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(vqt6):=20=E4=BF=A1=E5=8F=B7=E6=A7=BD?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E6=9C=BA=E5=88=B6=20+=20=E9=AB=98=E5=B1=82?= =?UTF-8?q?=E5=8C=85=E8=A3=85=E7=B1=BB=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 信号槽:vqt6_signal_register/trigger_slot 锁外回调,覆盖 QPushButton/QCheckBox/QRadioButton/QLineEdit/QComboBox/QSlider/QSpinBox/QProgressBar/QListWidget/QTabWidget/QAction/QTimer 共 13 个信号 - 高层包装:新增 QMainWindow/QDialog/QLineEdit/QCheckBox/QRadioButton/QComboBox/QSlider/QSpinBox/QProgressBar/QTextEdit/QPlainTextEdit/QListWidget/QTabWidget/QStackedWidget/QHBoxLayout/QVBoxLayout/QGridLayout/QMessageBox/QFileDialog/QTimer/QMenuBar/QMenu/QAction/QStatusBar/QColor/QFont 等 26 个类 - 便捷 FFI:vqt6_qlabel_set_text_utf8 / vqt6_qplaintextedit_append_utf8(Viper str 直传,避免 QString 手动管理) - 修复:QApplication 构造需 int& 左值(dummy_argc);QCheckBox 用 checkStateChanged 避免弃用警告 --- vqt6/__init__.vp | 1408 ++++++++++++++++++++++++++++++++++++++ vqt6/_bridge.h | 8 + vqt6/_bridge_core.cpp | 40 +- vqt6/_bridge_widgets.cpp | 148 +++- vqt6/_bridge_widgets.h | 22 + vqt6/_qtcore.vp | 2 + vqt6/_qtwidgets.vp | 23 + 7 files changed, 1646 insertions(+), 5 deletions(-) diff --git a/vqt6/__init__.vp b/vqt6/__init__.vp index 229fdfe..0df799d 100644 --- a/vqt6/__init__.vp +++ b/vqt6/__init__.vp @@ -24,6 +24,67 @@ from vqt6._qtnetwork import * # ============================================================ # 高级 API 类(Pythonic 包装,不透明指针) # ============================================================ + +# ============================================================ +# 模块级 helper:QWidget 基础操作(各控件类复用) +# ============================================================ +def _w_show(ptr: t.CPtr): + vqt6_qwidget_show(ptr) + + +def _w_hide(ptr: t.CPtr): + vqt6_qwidget_hide(ptr) + + +def _w_close(ptr: t.CPtr) -> int: + return vqt6_qwidget_close(ptr) + + +def _w_set_geometry(ptr: t.CPtr, x: int, y: int, w: int, h: int): + vqt6_qwidget_set_geometry(ptr, x, y, w, h) + + +def _w_resize(ptr: t.CPtr, w: int, h: int): + vqt6_qwidget_resize(ptr, w, h) + + +def _w_move(ptr: t.CPtr, x: int, y: int): + vqt6_qwidget_move(ptr, x, y) + + +def _w_set_enabled(ptr: t.CPtr, enabled: int): + vqt6_qwidget_set_enabled(ptr, enabled) + + +def _w_set_visible(ptr: t.CPtr, visible: int): + vqt6_qwidget_set_visible(ptr, visible) + + +def _w_is_visible(ptr: t.CPtr) -> int: + return vqt6_qwidget_is_visible(ptr) + + +def _w_set_window_title(ptr: t.CPtr, title: str): + qs: QtStringPtr = vqt6_qstring_create(title) + vqt6_qwidget_set_window_title(ptr, qs) + vqt6_qstring_free(qs) + + +def _w_set_tool_tip(ptr: t.CPtr, tip: str): + qs: QtStringPtr = vqt6_qstring_create(tip) + vqt6_qwidget_set_tool_tip(ptr, qs) + vqt6_qstring_free(qs) + + +def _w_text_of(qstr: QtStringPtr) -> str: + """QtStringPtr → Viper str(复制到 malloc 缓冲)""" + buf: bytes = stdlib.malloc(1024) + vqt6_qstring_to_utf8(qstr, buf, 1024) + s: str = buf + vqt6_qstring_free(qstr) + return s + + @t.NoVTable class QApplication: """QApplication 包装(不透明指针)""" @@ -187,6 +248,1344 @@ class QPushButton: def setEnabled(self, enabled: int): vqt6_qwidget_set_enabled(self._ptr, enabled) + def setCheckable(self, checkable: int): + vqt6_qpushbutton_set_checkable(self._ptr, checkable) + + def isCheckable(self) -> int: + return vqt6_qpushbutton_is_checkable(self._ptr) + + def setChecked(self, checked: int): + vqt6_qpushbutton_set_checked(self._ptr, checked) + + def isChecked(self) -> int: + return vqt6_qpushbutton_is_checked(self._ptr) + + def setFlat(self, flat: int): + vqt6_qpushbutton_set_flat(self._ptr, flat) + + # 回调(cb 传 Viper 模块级函数,签名 def cb(slot_id: int, data: t.CPtr)) + def clicked(self, cb: bytes, data: bytes = 0): + vqt6_qpushbutton_connect_clicked(self._ptr, cb, data) + + def toggled(self, cb: bytes, data: bytes = 0): + vqt6_qpushbutton_connect_toggled(self._ptr, cb, data) + + +# ============================================================ +# 窗口:QMainWindow / QDialog +# ============================================================ +@t.NoVTable +class QMainWindow: + """QMainWindow 包装""" + _ptr: QtMainWindowPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qmainwindow_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qmainwindow_create() + if self._ptr is None: + raise RuntimeError("Failed to create QMainWindow") + + def setCentralWidget(self, w: t.CPtr): + vqt6_qmainwindow_set_central_widget(self._ptr, w) + + def menuBar(self) -> "QMenuBar": + mb_ptr: QtMenuBarPtr = vqt6_qmainwindow_menu_bar(self._ptr) + mb = QMenuBar() + mb._ptr = mb_ptr + return mb + + def setMenuBar(self, mb: t.CPtr): + vqt6_qmainwindow_set_menu_bar(self._ptr, mb) + + def statusBar(self) -> "QStatusBar": + sb_ptr: QtStatusBarPtr = vqt6_qmainwindow_status_bar(self._ptr) + sb = QStatusBar() + sb._ptr = sb_ptr + return sb + + def addToolBar(self, tb: t.CPtr): + vqt6_qmainwindow_add_tool_bar(self._ptr, tb) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def close(self) -> int: + return _w_close(self._ptr) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def setWindowTitle(self, title: str): + _w_set_window_title(self._ptr, title) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + def setToolTip(self, tip: str): + _w_set_tool_tip(self._ptr, tip) + + +@t.NoVTable +class QDialog: + """QDialog 包装""" + _ptr: QtDialogPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qdialog_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qdialog_create() + if self._ptr is None: + raise RuntimeError("Failed to create QDialog") + + def exec(self) -> int: + return vqt6_qdialog_exec(self._ptr) + + def accept(self): + vqt6_qdialog_accept(self._ptr) + + def reject(self): + vqt6_qdialog_reject(self._ptr) + + def setModal(self, modal: int): + vqt6_qdialog_set_modal(self._ptr, modal) + + def isModal(self) -> int: + return vqt6_qdialog_is_modal(self._ptr) + + def setResult(self, result: int): + vqt6_qdialog_set_result(self._ptr, result) + + def result(self) -> int: + return vqt6_qdialog_result(self._ptr) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def close(self) -> int: + return _w_close(self._ptr) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def setWindowTitle(self, title: str): + _w_set_window_title(self._ptr, title) + + +# ============================================================ +# 输入控件:QLineEdit / QCheckBox / QRadioButton +# ============================================================ +@t.NoVTable +class QLineEdit: + """QLineEdit 包装""" + _ptr: QtLineEditPtr + + def __init__(self, text: str = "", parent: "QWidget | None" = None): + qs: QtStringPtr = vqt6_qstring_create(text) + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qlineedit_create_with_text_parent(qs, parent._ptr) + else: + self._ptr = vqt6_qlineedit_create_with_text(qs) + vqt6_qstring_free(qs) + if self._ptr is None: + raise RuntimeError("Failed to create QLineEdit") + + def setText(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qlineedit_set_text(self._ptr, qs) + vqt6_qstring_free(qs) + + def text(self) -> str: + return _w_text_of(vqt6_qlineedit_text(self._ptr)) + + def setPlaceholderText(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qlineedit_set_placeholder_text(self._ptr, qs) + vqt6_qstring_free(qs) + + def setReadOnly(self, ro: int): + vqt6_qlineedit_set_read_only(self._ptr, ro) + + def isReadOnly(self) -> int: + return vqt6_qlineedit_is_read_only(self._ptr) + + def setMaxLength(self, max_len: int): + vqt6_qlineedit_set_max_length(self._ptr, max_len) + + def selectAll(self): + vqt6_qlineedit_select_all(self._ptr) + + def clear(self): + vqt6_qlineedit_clear(self._ptr) + + def textChanged(self, cb: bytes, data: bytes = 0): + vqt6_qlineedit_connect_text_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QCheckBox: + """QCheckBox 包装""" + _ptr: QtCheckBoxPtr + + def __init__(self, text: str = "", parent: "QWidget | None" = None): + qs: QtStringPtr = vqt6_qstring_create(text) + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qcheckbox_create_with_text_parent(qs, parent._ptr) + else: + self._ptr = vqt6_qcheckbox_create_with_text(qs) + vqt6_qstring_free(qs) + if self._ptr is None: + raise RuntimeError("Failed to create QCheckBox") + + def setText(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qcheckbox_set_text(self._ptr, qs) + vqt6_qstring_free(qs) + + def text(self) -> str: + return _w_text_of(vqt6_qcheckbox_text(self._ptr)) + + def setChecked(self, checked: int): + vqt6_qcheckbox_set_checked(self._ptr, checked) + + def isChecked(self) -> int: + return vqt6_qcheckbox_is_checked(self._ptr) + + def setTristate(self, tristate: int): + vqt6_qcheckbox_set_tristate(self._ptr, tristate) + + def isTristate(self) -> int: + return vqt6_qcheckbox_is_tristate(self._ptr) + + def toggled(self, cb: bytes, data: bytes = 0): + vqt6_qcheckbox_connect_toggled(self._ptr, cb, data) + + def stateChanged(self, cb: bytes, data: bytes = 0): + vqt6_qcheckbox_connect_state_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QRadioButton: + """QRadioButton 包装""" + _ptr: QtRadioButtonPtr + + def __init__(self, text: str = "", parent: "QWidget | None" = None): + qs: QtStringPtr = vqt6_qstring_create(text) + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qradiobutton_create_with_text_parent(qs, parent._ptr) + else: + self._ptr = vqt6_qradiobutton_create_with_text(qs) + vqt6_qstring_free(qs) + if self._ptr is None: + raise RuntimeError("Failed to create QRadioButton") + + def setText(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qradiobutton_set_text(self._ptr, qs) + vqt6_qstring_free(qs) + + def text(self) -> str: + return _w_text_of(vqt6_qradiobutton_text(self._ptr)) + + def setChecked(self, checked: int): + vqt6_qradiobutton_set_checked(self._ptr, checked) + + def isChecked(self) -> int: + return vqt6_qradiobutton_is_checked(self._ptr) + + def toggled(self, cb: bytes, data: bytes = 0): + vqt6_qradiobutton_connect_toggled(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +# ============================================================ +# 选择控件:QComboBox / QSlider / QSpinBox / QProgressBar +# ============================================================ +@t.NoVTable +class QComboBox: + """QComboBox 包装""" + _ptr: QtComboBoxPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qcombobox_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qcombobox_create() + if self._ptr is None: + raise RuntimeError("Failed to create QComboBox") + + def addItem(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qcombobox_add_item(self._ptr, qs) + vqt6_qstring_free(qs) + + def insertItem(self, index: int, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qcombobox_insert_item(self._ptr, index, qs) + vqt6_qstring_free(qs) + + def removeItem(self, index: int): + vqt6_qcombobox_remove_item(self._ptr, index) + + def clear(self): + vqt6_qcombobox_clear(self._ptr) + + def count(self) -> int: + return vqt6_qcombobox_count(self._ptr) + + def currentIndex(self) -> int: + return vqt6_qcombobox_current_index(self._ptr) + + def setCurrentIndex(self, index: int): + vqt6_qcombobox_set_current_index(self._ptr, index) + + def currentText(self) -> str: + return _w_text_of(vqt6_qcombobox_current_text(self._ptr)) + + def itemText(self, index: int) -> str: + return _w_text_of(vqt6_qcombobox_item_text(self._ptr, index)) + + def setEditable(self, editable: int): + vqt6_qcombobox_set_editable(self._ptr, editable) + + def isEditable(self) -> int: + return vqt6_qcombobox_is_editable(self._ptr) + + def currentIndexChanged(self, cb: bytes, data: bytes = 0): + vqt6_qcombobox_connect_current_index_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QSlider: + """QSlider 包装""" + _ptr: QtSliderPtr + + def __init__(self, orientation: int = QT_HORIZONTAL, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qslider_create_with_orientation_parent(orientation, parent._ptr) + else: + self._ptr = vqt6_qslider_create_with_orientation(orientation) + if self._ptr is None: + raise RuntimeError("Failed to create QSlider") + + def setValue(self, value: int): + vqt6_qslider_set_value(self._ptr, value) + + def value(self) -> int: + return vqt6_qslider_value(self._ptr) + + def setMinimum(self, min_val: int): + vqt6_qslider_set_minimum(self._ptr, min_val) + + def setMaximum(self, max_val: int): + vqt6_qslider_set_maximum(self._ptr, max_val) + + def setRange(self, min_val: int, max_val: int): + vqt6_qslider_set_range(self._ptr, min_val, max_val) + + def setOrientation(self, orientation: int): + vqt6_qslider_set_orientation(self._ptr, orientation) + + def orientation(self) -> int: + return vqt6_qslider_orientation(self._ptr) + + def valueChanged(self, cb: bytes, data: bytes = 0): + vqt6_qslider_connect_value_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QSpinBox: + """QSpinBox 包装""" + _ptr: QtSpinBoxPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qspinbox_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qspinbox_create() + if self._ptr is None: + raise RuntimeError("Failed to create QSpinBox") + + def value(self) -> int: + return vqt6_qspinbox_value(self._ptr) + + def setValue(self, value: int): + vqt6_qspinbox_set_value(self._ptr, value) + + def setMinimum(self, min_val: int): + vqt6_qspinbox_set_minimum(self._ptr, min_val) + + def setMaximum(self, max_val: int): + vqt6_qspinbox_set_maximum(self._ptr, max_val) + + def setRange(self, min_val: int, max_val: int): + vqt6_qspinbox_set_range(self._ptr, min_val, max_val) + + def minimum(self) -> int: + return vqt6_qspinbox_minimum(self._ptr) + + def maximum(self) -> int: + return vqt6_qspinbox_maximum(self._ptr) + + def setPrefix(self, prefix: str): + qs: QtStringPtr = vqt6_qstring_create(prefix) + vqt6_qspinbox_set_prefix(self._ptr, qs) + vqt6_qstring_free(qs) + + def setSuffix(self, suffix: str): + qs: QtStringPtr = vqt6_qstring_create(suffix) + vqt6_qspinbox_set_suffix(self._ptr, qs) + vqt6_qstring_free(qs) + + def valueChanged(self, cb: bytes, data: bytes = 0): + vqt6_qspinbox_connect_value_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QProgressBar: + """QProgressBar 包装""" + _ptr: QtProgressBarPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qprogressbar_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qprogressbar_create() + if self._ptr is None: + raise RuntimeError("Failed to create QProgressBar") + + def setValue(self, value: int): + vqt6_qprogressbar_set_value(self._ptr, value) + + def value(self) -> int: + return vqt6_qprogressbar_value(self._ptr) + + def setRange(self, min_val: int, max_val: int): + vqt6_qprogressbar_set_range(self._ptr, min_val, max_val) + + def setMinimum(self, min_val: int): + vqt6_qprogressbar_set_minimum(self._ptr, min_val) + + def setMaximum(self, max_val: int): + vqt6_qprogressbar_set_maximum(self._ptr, max_val) + + def setFormat(self, format_str: str): + qs: QtStringPtr = vqt6_qstring_create(format_str) + vqt6_qprogressbar_set_format(self._ptr, qs) + vqt6_qstring_free(qs) + + def setTextVisible(self, visible: int): + vqt6_qprogressbar_set_text_visible(self._ptr, visible) + + def valueChanged(self, cb: bytes, data: bytes = 0): + vqt6_qprogressbar_connect_value_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +# ============================================================ +# 文本:QTextEdit / QPlainTextEdit +# ============================================================ +@t.NoVTable +class QTextEdit: + """QTextEdit 包装""" + _ptr: QtTextEditPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qtextedit_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qtextedit_create() + if self._ptr is None: + raise RuntimeError("Failed to create QTextEdit") + + def setPlainText(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qtextedit_set_plain_text(self._ptr, qs) + vqt6_qstring_free(qs) + + def toPlainText(self) -> str: + return _w_text_of(vqt6_qtextedit_to_plain_text(self._ptr)) + + def setHtml(self, html: str): + qs: QtStringPtr = vqt6_qstring_create(html) + vqt6_qtextedit_set_html(self._ptr, qs) + vqt6_qstring_free(qs) + + def append(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qtextedit_append(self._ptr, qs) + vqt6_qstring_free(qs) + + def clear(self): + vqt6_qtextedit_clear(self._ptr) + + def setReadOnly(self, ro: int): + vqt6_qtextedit_set_read_only(self._ptr, ro) + + def isReadOnly(self) -> int: + return vqt6_qtextedit_is_read_only(self._ptr) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QPlainTextEdit: + """QPlainTextEdit 包装""" + _ptr: QtPlainTextEditPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qplaintextedit_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qplaintextedit_create() + if self._ptr is None: + raise RuntimeError("Failed to create QPlainTextEdit") + + def setPlainText(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qplaintextedit_set_plain_text(self._ptr, qs) + vqt6_qstring_free(qs) + + def toPlainText(self) -> str: + return _w_text_of(vqt6_qplaintextedit_to_plain_text(self._ptr)) + + def append(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qplaintextedit_append(self._ptr, qs) + vqt6_qstring_free(qs) + + def clear(self): + vqt6_qplaintextedit_clear(self._ptr) + + def setReadOnly(self, ro: int): + vqt6_qplaintextedit_set_read_only(self._ptr, ro) + + def lineCount(self) -> int: + return vqt6_qplaintextedit_line_count(self._ptr) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +# ============================================================ +# 列表:QListWidget / QTabWidget / QStackedWidget +# ============================================================ +@t.NoVTable +class QListWidget: + """QListWidget 包装""" + _ptr: QtListWidgetPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qlistwidget_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qlistwidget_create() + if self._ptr is None: + raise RuntimeError("Failed to create QListWidget") + + def addItem(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qlistwidget_add_item(self._ptr, qs) + vqt6_qstring_free(qs) + + def insertItem(self, row: int, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qlistwidget_insert_item(self._ptr, row, qs) + vqt6_qstring_free(qs) + + def clear(self): + vqt6_qlistwidget_clear(self._ptr) + + def count(self) -> int: + return vqt6_qlistwidget_count(self._ptr) + + def currentRow(self) -> int: + return vqt6_qlistwidget_current_row(self._ptr) + + def setCurrentRow(self, row: int): + vqt6_qlistwidget_set_current_row(self._ptr, row) + + def currentItemText(self) -> str: + return _w_text_of(vqt6_qlistwidget_current_item_text(self._ptr)) + + def itemText(self, row: int) -> str: + return _w_text_of(vqt6_qlistwidget_item_text(self._ptr, row)) + + def setItemText(self, row: int, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qlistwidget_set_item_text(self._ptr, row, qs) + vqt6_qstring_free(qs) + + def currentRowChanged(self, cb: bytes, data: bytes = 0): + vqt6_qlistwidget_connect_current_row_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QTabWidget: + """QTabWidget 包装""" + _ptr: QtTabWidgetPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qtabwidget_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qtabwidget_create() + if self._ptr is None: + raise RuntimeError("Failed to create QTabWidget") + + def addTab(self, w: t.CPtr, label: str) -> int: + qs: QtStringPtr = vqt6_qstring_create(label) + idx: int = vqt6_qtabwidget_add_tab(self._ptr, w, qs) + vqt6_qstring_free(qs) + return idx + + def removeTab(self, index: int): + vqt6_qtabwidget_remove_tab(self._ptr, index) + + def count(self) -> int: + return vqt6_qtabwidget_count(self._ptr) + + def currentIndex(self) -> int: + return vqt6_qtabwidget_current_index(self._ptr) + + def setCurrentIndex(self, index: int): + vqt6_qtabwidget_set_current_index(self._ptr, index) + + def tabText(self, index: int) -> str: + return _w_text_of(vqt6_qtabwidget_tab_text(self._ptr, index)) + + def setTabText(self, index: int, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qtabwidget_set_tab_text(self._ptr, index, qs) + vqt6_qstring_free(qs) + + def currentChanged(self, cb: bytes, data: bytes = 0): + vqt6_qtabwidget_connect_current_changed(self._ptr, cb, data) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +@t.NoVTable +class QStackedWidget: + """QStackedWidget 包装""" + _ptr: QtStackedWidgetPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qstackedwidget_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qstackedwidget_create() + if self._ptr is None: + raise RuntimeError("Failed to create QStackedWidget") + + def addWidget(self, w: t.CPtr) -> int: + return vqt6_qstackedwidget_add_widget(self._ptr, w) + + def removeWidget(self, w: t.CPtr): + vqt6_qstackedwidget_remove_widget(self._ptr, w) + + def count(self) -> int: + return vqt6_qstackedwidget_count(self._ptr) + + def currentIndex(self) -> int: + return vqt6_qstackedwidget_current_index(self._ptr) + + def setCurrentIndex(self, index: int): + vqt6_qstackedwidget_set_current_index(self._ptr, index) + + def currentWidget(self) -> t.CPtr: + return vqt6_qstackedwidget_current_widget(self._ptr) + + def show(self): + _w_show(self._ptr) + + def hide(self): + _w_hide(self._ptr) + + def setGeometry(self, x: int, y: int, w: int, h: int): + _w_set_geometry(self._ptr, x, y, w, h) + + def resize(self, w: int, h: int): + _w_resize(self._ptr, w, h) + + def move(self, x: int, y: int): + _w_move(self._ptr, x, y) + + def setEnabled(self, enabled: int): + _w_set_enabled(self._ptr, enabled) + + +# ============================================================ +# 布局:QHBoxLayout / QVBoxLayout / QGridLayout +# ============================================================ +@t.NoVTable +class QHBoxLayout: + """QHBoxLayout 包装(addWidget 传控件 ._ptr)""" + _ptr: QtBoxLayoutPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qhboxlayout_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qhboxlayout_create() + if self._ptr is None: + raise RuntimeError("Failed to create QHBoxLayout") + + def addWidget(self, widget: t.CPtr): + vqt6_qlayout_add_widget(self._ptr, widget) + + def removeWidget(self, widget: t.CPtr): + vqt6_qlayout_remove_widget(self._ptr, widget) + + def addLayout(self, child: t.CPtr): + vqt6_qhboxlayout_add_layout(self._ptr, child) + + def addStretch(self, stretch: int): + vqt6_qhboxlayout_add_stretch(self._ptr, stretch) + + def addSpacing(self, size: int): + vqt6_qhboxlayout_add_spacing(self._ptr, size) + + def setSpacing(self, spacing: int): + vqt6_qlayout_set_spacing(self._ptr, spacing) + + def setContentsMargins(self, left: int, top: int, right: int, bottom: int): + vqt6_qlayout_set_contents_margins(self._ptr, left, top, right, bottom) + + +@t.NoVTable +class QVBoxLayout: + """QVBoxLayout 包装(addWidget 传控件 ._ptr)""" + _ptr: QtBoxLayoutPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qvboxlayout_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qvboxlayout_create() + if self._ptr is None: + raise RuntimeError("Failed to create QVBoxLayout") + + def addWidget(self, widget: t.CPtr): + vqt6_qlayout_add_widget(self._ptr, widget) + + def removeWidget(self, widget: t.CPtr): + vqt6_qlayout_remove_widget(self._ptr, widget) + + def addLayout(self, child: t.CPtr): + vqt6_qvboxlayout_add_layout(self._ptr, child) + + def addStretch(self, stretch: int): + vqt6_qvboxlayout_add_stretch(self._ptr, stretch) + + def addSpacing(self, size: int): + vqt6_qvboxlayout_add_spacing(self._ptr, size) + + def setSpacing(self, spacing: int): + vqt6_qlayout_set_spacing(self._ptr, spacing) + + def setContentsMargins(self, left: int, top: int, right: int, bottom: int): + vqt6_qlayout_set_contents_margins(self._ptr, left, top, right, bottom) + + +@t.NoVTable +class QGridLayout: + """QGridLayout 包装(addWidget 传控件 ._ptr)""" + _ptr: QtGridLayoutPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qgridlayout_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qgridlayout_create() + if self._ptr is None: + raise RuntimeError("Failed to create QGridLayout") + + def addWidget(self, widget: t.CPtr, row: int, col: int): + vqt6_qgridlayout_add_widget(self._ptr, widget, row, col) + + def addWidgetWithSpan(self, widget: t.CPtr, row: int, col: int, row_span: int, col_span: int): + vqt6_qgridlayout_add_widget_with_span(self._ptr, widget, row, col, row_span, col_span) + + def setSpacing(self, spacing: int): + vqt6_qlayout_set_spacing(self._ptr, spacing) + + def setContentsMargins(self, left: int, top: int, right: int, bottom: int): + vqt6_qlayout_set_contents_margins(self._ptr, left, top, right, bottom) + + +# ============================================================ +# 对话框:QMessageBox / QFileDialog(静态方法) +# ============================================================ +@t.NoVTable +class QMessageBox: + """QMessageBox 静态对话框""" + _ptr: QtMessageBoxPtr + + @staticmethod + def information(parent: "QWidget | None", title: str, text: str, buttons: int = QT_OK) -> int: + qs_title: QtStringPtr = vqt6_qstring_create(title) + qs_text: QtStringPtr = vqt6_qstring_create(text) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + r: int = vqt6_qmessagebox_information(parent_ptr, qs_title, qs_text, buttons) + vqt6_qstring_free(qs_title) + vqt6_qstring_free(qs_text) + return r + + @staticmethod + def warning(parent: "QWidget | None", title: str, text: str, buttons: int = QT_OK) -> int: + qs_title: QtStringPtr = vqt6_qstring_create(title) + qs_text: QtStringPtr = vqt6_qstring_create(text) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + r: int = vqt6_qmessagebox_warning(parent_ptr, qs_title, qs_text, buttons) + vqt6_qstring_free(qs_title) + vqt6_qstring_free(qs_text) + return r + + @staticmethod + def critical(parent: "QWidget | None", title: str, text: str, buttons: int = QT_OK) -> int: + qs_title: QtStringPtr = vqt6_qstring_create(title) + qs_text: QtStringPtr = vqt6_qstring_create(text) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + r: int = vqt6_qmessagebox_critical(parent_ptr, qs_title, qs_text, buttons) + vqt6_qstring_free(qs_title) + vqt6_qstring_free(qs_text) + return r + + @staticmethod + def question(parent: "QWidget | None", title: str, text: str, buttons: int = QT_OK | QT_CANCEL) -> int: + qs_title: QtStringPtr = vqt6_qstring_create(title) + qs_text: QtStringPtr = vqt6_qstring_create(text) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + r: int = vqt6_qmessagebox_question(parent_ptr, qs_title, qs_text, buttons) + vqt6_qstring_free(qs_title) + vqt6_qstring_free(qs_text) + return r + + @staticmethod + def about(parent: "QWidget | None", title: str, text: str): + qs_title: QtStringPtr = vqt6_qstring_create(title) + qs_text: QtStringPtr = vqt6_qstring_create(text) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + vqt6_qmessagebox_about(parent_ptr, qs_title, qs_text) + vqt6_qstring_free(qs_title) + vqt6_qstring_free(qs_text) + + +@t.NoVTable +class QFileDialog: + """QFileDialog 静态对话框""" + _ptr: QtFileDialogPtr + + @staticmethod + def getOpenFileName(parent: "QWidget | None", caption: str, directory: str, filter_str: str = "") -> str: + qs_caption: QtStringPtr = vqt6_qstring_create(caption) + qs_dir: QtStringPtr = vqt6_qstring_create(directory) + qs_filter: QtStringPtr = vqt6_qstring_create(filter_str) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + result: QtStringPtr = vqt6_qfiledialog_get_open_file_name(parent_ptr, qs_caption, qs_dir, qs_filter) + vqt6_qstring_free(qs_caption) + vqt6_qstring_free(qs_dir) + vqt6_qstring_free(qs_filter) + return _w_text_of(result) + + @staticmethod + def getSaveFileName(parent: "QWidget | None", caption: str, directory: str, filter_str: str = "") -> str: + qs_caption: QtStringPtr = vqt6_qstring_create(caption) + qs_dir: QtStringPtr = vqt6_qstring_create(directory) + qs_filter: QtStringPtr = vqt6_qstring_create(filter_str) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + result: QtStringPtr = vqt6_qfiledialog_get_save_file_name(parent_ptr, qs_caption, qs_dir, qs_filter) + vqt6_qstring_free(qs_caption) + vqt6_qstring_free(qs_dir) + vqt6_qstring_free(qs_filter) + return _w_text_of(result) + + @staticmethod + def getExistingDirectory(parent: "QWidget | None", caption: str, directory: str) -> str: + qs_caption: QtStringPtr = vqt6_qstring_create(caption) + qs_dir: QtStringPtr = vqt6_qstring_create(directory) + parent_ptr: t.CPtr = None + if parent is not None and parent._ptr is not None: + parent_ptr = parent._ptr + result: QtStringPtr = vqt6_qfiledialog_get_existing_directory(parent_ptr, qs_caption, qs_dir) + vqt6_qstring_free(qs_caption) + vqt6_qstring_free(qs_dir) + return _w_text_of(result) + + +# ============================================================ +# 定时器:QTimer +# ============================================================ +@t.NoVTable +class QTimer: + """QTimer 包装""" + _ptr: QtTimerPtr + + def __init__(self): + self._ptr = vqt6_qtimer_create() + if self._ptr is None: + raise RuntimeError("Failed to create QTimer") + + def start(self, interval_ms: int): + vqt6_qtimer_start(self._ptr, interval_ms) + + def stop(self): + vqt6_qtimer_stop(self._ptr) + + def setInterval(self, interval_ms: int): + vqt6_qtimer_set_interval(self._ptr, interval_ms) + + def interval(self) -> int: + return vqt6_qtimer_interval(self._ptr) + + def setSingleShot(self, single_shot: int): + vqt6_qtimer_set_single_shot(self._ptr, single_shot) + + def isActive(self) -> int: + return vqt6_qtimer_is_active(self._ptr) + + def timeout(self, cb: bytes, data: bytes = 0): + vqt6_qtimer_connect_timeout(self._ptr, cb, data) + + +# ============================================================ +# 菜单:QMenuBar / QMenu / QAction / QStatusBar +# ============================================================ +@t.NoVTable +class QMenuBar: + """QMenuBar 包装""" + _ptr: QtMenuBarPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qmenubar_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qmenubar_create() + if self._ptr is None: + raise RuntimeError("Failed to create QMenuBar") + + def addMenu(self, title: str) -> "QMenu": + qs: QtStringPtr = vqt6_qstring_create(title) + mp: QtMenuPtr = vqt6_qmenubar_add_menu(self._ptr, qs) + vqt6_qstring_free(qs) + m = QMenu() + m._ptr = mp + return m + + def addAction(self, action: t.CPtr): + vqt6_qmenubar_add_action(self._ptr, action) + + def clear(self): + vqt6_qmenubar_clear(self._ptr) + + +@t.NoVTable +class QMenu: + """QMenu 包装""" + _ptr: QtMenuPtr + + def __init__(self, title: str = ""): + if title != "": + qs: QtStringPtr = vqt6_qstring_create(title) + self._ptr = vqt6_qmenu_create_with_title(qs) + vqt6_qstring_free(qs) + else: + self._ptr = vqt6_qmenu_create() + if self._ptr is None: + raise RuntimeError("Failed to create QMenu") + + def addAction(self, action: t.CPtr): + vqt6_qmenu_add_action(self._ptr, action) + + def addActionWithText(self, text: str) -> "QAction": + qs: QtStringPtr = vqt6_qstring_create(text) + ap: QtActionPtr = vqt6_qmenu_add_action_with_text(self._ptr, qs) + vqt6_qstring_free(qs) + a = QAction() + a._ptr = ap + return a + + def addMenu(self, title: str) -> "QMenu": + qs: QtStringPtr = vqt6_qstring_create(title) + mp: QtMenuPtr = vqt6_qmenu_add_menu(self._ptr, qs) + vqt6_qstring_free(qs) + m = QMenu() + m._ptr = mp + return m + + def addSeparator(self): + vqt6_qmenu_add_separator(self._ptr) + + def setTitle(self, title: str): + qs: QtStringPtr = vqt6_qstring_create(title) + vqt6_qmenu_set_title(self._ptr, qs) + vqt6_qstring_free(qs) + + def clear(self): + vqt6_qmenu_clear(self._ptr) + + +@t.NoVTable +class QAction: + """QAction 包装""" + _ptr: QtActionPtr + + def __init__(self, text: str = ""): + if text != "": + qs: QtStringPtr = vqt6_qstring_create(text) + self._ptr = vqt6_qaction_create_with_text(qs) + vqt6_qstring_free(qs) + else: + self._ptr = vqt6_qaction_create() + if self._ptr is None: + raise RuntimeError("Failed to create QAction") + + def setText(self, text: str): + qs: QtStringPtr = vqt6_qstring_create(text) + vqt6_qaction_set_text(self._ptr, qs) + vqt6_qstring_free(qs) + + def text(self) -> str: + return _w_text_of(vqt6_qaction_text(self._ptr)) + + def setShortcut(self, key_seq: str): + qs: QtStringPtr = vqt6_qstring_create(key_seq) + vqt6_qaction_set_shortcut(self._ptr, qs) + vqt6_qstring_free(qs) + + def setCheckable(self, checkable: int): + vqt6_qaction_set_checkable(self._ptr, checkable) + + def isCheckable(self) -> int: + return vqt6_qaction_is_checkable(self._ptr) + + def setChecked(self, checked: int): + vqt6_qaction_set_checked(self._ptr, checked) + + def isChecked(self) -> int: + return vqt6_qaction_is_checked(self._ptr) + + def setEnabled(self, enabled: int): + vqt6_qaction_set_enabled(self._ptr, enabled) + + def isEnabled(self) -> int: + return vqt6_qaction_is_enabled(self._ptr) + + def trigger(self): + vqt6_qaction_trigger(self._ptr) + + def triggered(self, cb: bytes, data: bytes = 0): + vqt6_qaction_connect_triggered(self._ptr, cb, data) + + +@t.NoVTable +class QStatusBar: + """QStatusBar 包装""" + _ptr: QtStatusBarPtr + + def __init__(self, parent: "QWidget | None" = None): + if parent is not None and parent._ptr is not None: + self._ptr = vqt6_qstatusbar_create_with_parent(parent._ptr) + else: + self._ptr = vqt6_qstatusbar_create() + if self._ptr is None: + raise RuntimeError("Failed to create QStatusBar") + + def showMessage(self, message: str, timeout_ms: int = 0): + qs: QtStringPtr = vqt6_qstring_create(message) + vqt6_qstatusbar_show_message(self._ptr, qs, timeout_ms) + vqt6_qstring_free(qs) + + def clearMessage(self): + vqt6_qstatusbar_clear_message(self._ptr) + + def currentMessage(self) -> str: + return _w_text_of(vqt6_qstatusbar_current_message(self._ptr)) + + def addWidget(self, w: t.CPtr, stretch: int = 0): + vqt6_qstatusbar_add_widget(self._ptr, w, stretch) + + +# ============================================================ +# 绘图基础:QColor / QFont +# ============================================================ +@t.NoVTable +class QColor: + """QColor 包装""" + _ptr: QtColorPtr + + def __init__(self, r: int, g: int, b: int, a: int = 255): + self._ptr = vqt6_qcolor_create_rgba(r, g, b, a) + if self._ptr is None: + raise RuntimeError("Failed to create QColor") + + def red(self) -> int: + return vqt6_qcolor_red(self._ptr) + + def green(self) -> int: + return vqt6_qcolor_green(self._ptr) + + def blue(self) -> int: + return vqt6_qcolor_blue(self._ptr) + + def alpha(self) -> int: + return vqt6_qcolor_alpha(self._ptr) + + +@t.NoVTable +class QFont: + """QFont 包装""" + _ptr: QtFontPtr + + def __init__(self, family: str = "", point_size: int = 0): + if family != "": + qs: QtStringPtr = vqt6_qstring_create(family) + if point_size > 0: + self._ptr = vqt6_qfont_create_with_family_size(qs, point_size) + else: + self._ptr = vqt6_qfont_create_with_family(qs) + vqt6_qstring_free(qs) + else: + self._ptr = vqt6_qfont_create() + if self._ptr is None: + raise RuntimeError("Failed to create QFont") + + def setPointSize(self, size: int): + vqt6_qfont_set_point_size(self._ptr, size) + + def setPixelSize(self, size: int): + vqt6_qfont_set_pixel_size(self._ptr, size) + + def setBold(self, bold: int): + vqt6_qfont_set_bold(self._ptr, bold) + + def setItalic(self, italic: int): + vqt6_qfont_set_italic(self._ptr, italic) + + def setUnderline(self, underline: int): + vqt6_qfont_set_underline(self._ptr, underline) + # ============================================================ # 模块导出 @@ -194,6 +1593,15 @@ class QPushButton: __all__ = [ # 高层类 "QApplication", "QWidget", "QLabel", "QPushButton", + "QMainWindow", "QDialog", + "QLineEdit", "QCheckBox", "QRadioButton", + "QComboBox", "QSlider", "QSpinBox", "QProgressBar", + "QTextEdit", "QPlainTextEdit", "QListWidget", + "QTabWidget", "QStackedWidget", + "QHBoxLayout", "QVBoxLayout", "QGridLayout", + "QMessageBox", "QFileDialog", "QTimer", + "QMenuBar", "QMenu", "QAction", "QStatusBar", + "QColor", "QFont", # 类型 "QtObjectPtr", "QtStringPtr", "QtByteArrayPtr", "QtWidgetPtr", "QtAppPtr", "QtLabelPtr", "QtPushButtonPtr", diff --git a/vqt6/_bridge.h b/vqt6/_bridge.h index e573ea8..97646a9 100644 --- a/vqt6/_bridge.h +++ b/vqt6/_bridge.h @@ -143,6 +143,14 @@ typedef void (*vqt6_slot_callback_t)(int slot_id, void* user_data); VQT6_API void vqt6_register_slot(int slot_id, void* user_data); VQT6_API void vqt6_unregister_slot(int slot_id); VQT6_API void vqt6_trigger_slot(int slot_id); +// 分配新 slot_id 并注册回调(信号→回调桥梁,Qt 信号触发时调用 cb(slot_id, user_data)) +VQT6_API int vqt6_signal_register(vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_signal_unregister(int slot_id); + +// ============================================================ +// 信号连接(QTimer → timeout) +// ============================================================ +VQT6_API void vqt6_qtimer_connect_timeout(void* timer, vqt6_slot_callback_t cb, void* user_data); #ifdef __cplusplus } diff --git a/vqt6/_bridge_core.cpp b/vqt6/_bridge_core.cpp index 3f420d8..71275e0 100644 --- a/vqt6/_bridge_core.cpp +++ b/vqt6/_bridge_core.cpp @@ -20,6 +20,7 @@ namespace { QMutex g_slot_mutex; QHash> g_slots; + int g_next_slot_id = 1000; // 避开小整数 slot_id(用户可能直接用 trigger_slot 测试) } // ============================================================ @@ -425,9 +426,40 @@ extern "C" VQT6_API void vqt6_unregister_slot(int slot_id) { } extern "C" VQT6_API void vqt6_trigger_slot(int slot_id) { - QMutexLocker locker(&g_slot_mutex); - auto it = g_slots.find(slot_id); - if (it != g_slots.end() && it.value().first != nullptr) { - it.value().first(slot_id, it.value().second); + // 拷贝条目后释放锁再回调,避免回调内再触发/注册时对非递归 QMutex 死锁 + QPair entry(nullptr, nullptr); + { + QMutexLocker locker(&g_slot_mutex); + auto it = g_slots.find(slot_id); + if (it == g_slots.end()) return; + entry = it.value(); + } + if (entry.first != nullptr) { + entry.first(slot_id, entry.second); } } + +extern "C" VQT6_API int vqt6_signal_register(vqt6_slot_callback_t cb, void* user_data) { + if (cb == nullptr) return -1; + QMutexLocker locker(&g_slot_mutex); + int id = ++g_next_slot_id; + g_slots.insert(id, QPair(cb, user_data)); + return id; +} + +extern "C" VQT6_API void vqt6_signal_unregister(int slot_id) { + vqt6_unregister_slot(slot_id); +} + +// ============================================================ +// QTimer — timeout 信号 → 回调 +// ============================================================ +extern "C" VQT6_API void vqt6_qtimer_connect_timeout(void* timer, vqt6_slot_callback_t cb, void* user_data) { + if (timer == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(timer), &QTimer::timeout, + static_cast(timer), [slot_id]() { + vqt6_trigger_slot(slot_id); + }); +} diff --git a/vqt6/_bridge_widgets.cpp b/vqt6/_bridge_widgets.cpp index 81f1d48..fb6c31f 100644 --- a/vqt6/_bridge_widgets.cpp +++ b/vqt6/_bridge_widgets.cpp @@ -56,7 +56,8 @@ extern "C" VQT6_API void* vqt6_qapplication_create(int argc, char** argv) { if (argv == nullptr || argc <= 0) { static char vqt6_dummy0[] = "vqt6_app"; static char* vqt6_dummy_argv[2] = { vqt6_dummy0, nullptr }; - return new QApplication(1, vqt6_dummy_argv); + int dummy_argc = 1; + return new QApplication(dummy_argc, vqt6_dummy_argv); } return new QApplication(argc, argv); } @@ -477,6 +478,11 @@ extern "C" VQT6_API void vqt6_qlabel_set_text(void* label, void* text) { static_cast(label)->setText(*static_cast(text)); } +extern "C" VQT6_API void vqt6_qlabel_set_text_utf8(void* label, const char* utf8) { + if (label == nullptr || utf8 == nullptr) return; + static_cast(label)->setText(QString::fromUtf8(utf8)); +} + extern "C" VQT6_API void* vqt6_qlabel_text(void* label) { if (label == nullptr) return nullptr; return new QString(static_cast(label)->text()); @@ -757,6 +763,11 @@ extern "C" VQT6_API void vqt6_qplaintextedit_append(void* pte, void* text) { static_cast(pte)->appendPlainText(*static_cast(text)); } +extern "C" VQT6_API void vqt6_qplaintextedit_append_utf8(void* pte, const char* utf8) { + if (pte == nullptr || utf8 == nullptr) return; + static_cast(pte)->appendPlainText(QString::fromUtf8(utf8)); +} + extern "C" VQT6_API void vqt6_qplaintextedit_clear(void* pte) { if (pte == nullptr) return; static_cast(pte)->clear(); @@ -1749,3 +1760,138 @@ extern "C" VQT6_API void* vqt6_qfiledialog_get_open_file_names(void* parent, voi filter != nullptr ? *static_cast(filter) : QString() )); } + +// ============================================================ +// 信号连接(Qt 信号 → Viper 回调 vqt6_slot_callback_t) +// 模式:vqt6_signal_register 分配 slot_id → Qt lambda 连接信号 → +// 信号触发时 vqt6_trigger_slot(slot_id) 调回 Viper 回调 +// ============================================================ +extern "C" VQT6_API void vqt6_qpushbutton_connect_clicked(void* btn, vqt6_slot_callback_t cb, void* user_data) { + if (btn == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(btn), &QPushButton::clicked, + static_cast(btn), [slot_id](bool) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qpushbutton_connect_toggled(void* btn, vqt6_slot_callback_t cb, void* user_data) { + if (btn == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(btn), &QPushButton::toggled, + static_cast(btn), [slot_id](bool) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qcheckbox_connect_toggled(void* cb_widget, vqt6_slot_callback_t cb, void* user_data) { + if (cb_widget == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(cb_widget), &QCheckBox::toggled, + static_cast(cb_widget), [slot_id](bool) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qcheckbox_connect_state_changed(void* cb_widget, vqt6_slot_callback_t cb, void* user_data) { + if (cb_widget == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(cb_widget), &QCheckBox::checkStateChanged, + static_cast(cb_widget), [slot_id](Qt::CheckState) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qradiobutton_connect_toggled(void* rb, vqt6_slot_callback_t cb, void* user_data) { + if (rb == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(rb), &QRadioButton::toggled, + static_cast(rb), [slot_id](bool) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qlineedit_connect_text_changed(void* le, vqt6_slot_callback_t cb, void* user_data) { + if (le == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(le), &QLineEdit::textChanged, + static_cast(le), [slot_id](const QString&) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qcombobox_connect_current_index_changed(void* cb_widget, vqt6_slot_callback_t cb, void* user_data) { + if (cb_widget == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(cb_widget), &QComboBox::currentIndexChanged, + static_cast(cb_widget), [slot_id](int) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qslider_connect_value_changed(void* s, vqt6_slot_callback_t cb, void* user_data) { + if (s == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(s), &QSlider::valueChanged, + static_cast(s), [slot_id](int) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qspinbox_connect_value_changed(void* sb, vqt6_slot_callback_t cb, void* user_data) { + if (sb == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(sb), &QSpinBox::valueChanged, + static_cast(sb), [slot_id](int) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qprogressbar_connect_value_changed(void* pb, vqt6_slot_callback_t cb, void* user_data) { + if (pb == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(pb), &QProgressBar::valueChanged, + static_cast(pb), [slot_id](int) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qlistwidget_connect_current_row_changed(void* lw, vqt6_slot_callback_t cb, void* user_data) { + if (lw == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(lw), &QListWidget::currentRowChanged, + static_cast(lw), [slot_id](int) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qtabwidget_connect_current_changed(void* tw, vqt6_slot_callback_t cb, void* user_data) { + if (tw == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(tw), &QTabWidget::currentChanged, + static_cast(tw), [slot_id](int) { + vqt6_trigger_slot(slot_id); + }); +} + +extern "C" VQT6_API void vqt6_qaction_connect_triggered(void* action, vqt6_slot_callback_t cb, void* user_data) { + if (action == nullptr) return; + int slot_id = vqt6_signal_register(cb, user_data); + if (slot_id < 0) return; + QObject::connect(static_cast(action), &QAction::triggered, + static_cast(action), [slot_id](bool) { + vqt6_trigger_slot(slot_id); + }); +} diff --git a/vqt6/_bridge_widgets.h b/vqt6/_bridge_widgets.h index 0197377..7453371 100644 --- a/vqt6/_bridge_widgets.h +++ b/vqt6/_bridge_widgets.h @@ -49,6 +49,9 @@ VQT6_API void vqt6_qwidget_set_window_title(void* widget, void* title); VQT6_API void* vqt6_qwidget_window_title(void* widget); VQT6_API void vqt6_qwidget_set_text(void* widget, void* text); VQT6_API void* vqt6_qwidget_text(void* widget); +// UTF-8 便捷版:直接传 C 字符串(Viper str),内部转 QString,避免 Viper 端手动创建/释放 QString +VQT6_API void vqt6_qlabel_set_text_utf8(void* label, const char* utf8); +VQT6_API void vqt6_qplaintextedit_append_utf8(void* pte, const char* utf8); VQT6_API void vqt6_qwidget_set_enabled(void* widget, int enabled); VQT6_API int vqt6_qwidget_is_enabled(void* widget); VQT6_API void vqt6_qwidget_set_parent(void* widget, void* parent); @@ -425,6 +428,25 @@ VQT6_API void* vqt6_qfiledialog_get_save_file_name(void* parent, void* caption, VQT6_API void* vqt6_qfiledialog_get_existing_directory(void* parent, void* caption, void* dir); VQT6_API void* vqt6_qfiledialog_get_open_file_names(void* parent, void* caption, void* dir, void* filter); +// ============================================================ +// 信号连接(Qt 信号 → Viper 回调 vqt6_slot_callback_t) +// 回调统一签名: void cb(int slot_id, void* user_data) +// 触发时回调里可用 user_data(如控件指针)再查询控件状态 +// ============================================================ +VQT6_API void vqt6_qpushbutton_connect_clicked(void* btn, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qpushbutton_connect_toggled(void* btn, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qcheckbox_connect_toggled(void* cb_widget, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qcheckbox_connect_state_changed(void* cb_widget, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qradiobutton_connect_toggled(void* rb, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qlineedit_connect_text_changed(void* le, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qcombobox_connect_current_index_changed(void* cb_widget, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qslider_connect_value_changed(void* s, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qspinbox_connect_value_changed(void* sb, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qprogressbar_connect_value_changed(void* pb, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qlistwidget_connect_current_row_changed(void* lw, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qtabwidget_connect_current_changed(void* tw, vqt6_slot_callback_t cb, void* user_data); +VQT6_API void vqt6_qaction_connect_triggered(void* action, vqt6_slot_callback_t cb, void* user_data); + #ifdef __cplusplus } #endif diff --git a/vqt6/_qtcore.vp b/vqt6/_qtcore.vp index ddb7035..86de0e0 100644 --- a/vqt6/_qtcore.vp +++ b/vqt6/_qtcore.vp @@ -99,6 +99,8 @@ def vqt6_qtimer_interval(timer: QtTimerPtr) -> t.CInt | t.State: pass def vqt6_qtimer_is_active(timer: QtTimerPtr) -> t.CInt | t.State: pass def vqt6_qtimer_set_single_shot(timer: QtTimerPtr, single_shot: t.CInt) -> t.State: pass def vqt6_qtimer_is_single_shot(timer: QtTimerPtr) -> t.CInt | t.State: pass +# timeout 信号 → 回调(cb 是 Viper 模块级函数,签名 (slot_id: int, user_data: t.CPtr)) +def vqt6_qtimer_connect_timeout(timer: QtTimerPtr, cb: bytes, user_data: bytes) -> t.State: pass # ============================================================ diff --git a/vqt6/_qtwidgets.vp b/vqt6/_qtwidgets.vp index 025d026..8213f5b 100644 --- a/vqt6/_qtwidgets.vp +++ b/vqt6/_qtwidgets.vp @@ -132,6 +132,8 @@ def vqt6_qlabel_create() -> QtLabelPtr | t.State: pass def vqt6_qlabel_create_with_text(text: QtStringPtr) -> QtLabelPtr | t.State: pass def vqt6_qlabel_create_with_text_parent(text: QtStringPtr, parent: QtWidgetPtr) -> QtLabelPtr | t.State: pass def vqt6_qlabel_set_text(label: QtLabelPtr, text: QtStringPtr) -> t.State: pass +# UTF-8 便捷版:直接传 Viper str,避免手动创建/释放 QString +def vqt6_qlabel_set_text_utf8(label: QtLabelPtr, text: t.CChar | t.CPtr) -> t.State: pass def vqt6_qlabel_text(label: QtLabelPtr) -> QtStringPtr | t.State: pass def vqt6_qlabel_set_alignment(label: QtLabelPtr, alignment: t.CInt) -> t.State: pass def vqt6_qlabel_alignment(label: QtLabelPtr) -> t.CInt | t.State: pass @@ -203,6 +205,8 @@ def vqt6_qplaintextedit_create_with_parent(parent: QtWidgetPtr) -> QtPlainTextEd def vqt6_qplaintextedit_to_plain_text(pte: QtPlainTextEditPtr) -> QtStringPtr | t.State: pass def vqt6_qplaintextedit_set_plain_text(pte: QtPlainTextEditPtr, text: QtStringPtr) -> t.State: pass def vqt6_qplaintextedit_append(pte: QtPlainTextEditPtr, text: QtStringPtr) -> t.State: pass +# UTF-8 便捷版:直接传 Viper str,避免手动创建/释放 QString +def vqt6_qplaintextedit_append_utf8(pte: QtPlainTextEditPtr, text: t.CChar | t.CPtr) -> t.State: pass def vqt6_qplaintextedit_clear(pte: QtPlainTextEditPtr) -> t.State: pass def vqt6_qplaintextedit_set_read_only(pte: QtPlainTextEditPtr, ro: t.CInt) -> t.State: pass def vqt6_qplaintextedit_line_count(pte: QtPlainTextEditPtr) -> t.CInt | t.State: pass @@ -476,3 +480,22 @@ def vqt6_qinputdialog_get_text(parent: QtWidgetPtr, title: QtStringPtr, label: Q def vqt6_qinputdialog_get_int(parent: QtWidgetPtr, title: QtStringPtr, label: QtStringPtr, value: t.CInt, min_val: t.CInt, max_val: t.CInt, step: t.CInt, ok_pressed: t.CInt | t.CPtr) -> t.CInt | t.State: pass def vqt6_qinputdialog_get_double(parent: QtWidgetPtr, title: QtStringPtr, label: QtStringPtr, value: t.CDouble, min_val: t.CDouble, max_val: t.CDouble, decimals: t.CInt, ok_pressed: t.CInt | t.CPtr) -> t.CDouble | t.State: pass def vqt6_qinputdialog_get_item(parent: QtWidgetPtr, title: QtStringPtr, label: QtStringPtr, items: QtStringListPtr, current: t.CInt, editable: t.CInt, ok_pressed: t.CInt | t.CPtr) -> QtStringPtr | t.State: pass + + +# ============================================================ +# 信号连接(Qt 信号 → Viper 回调) +# 回调统一签名: def cb(slot_id: int, user_data: t.CPtr) +# ============================================================ +def vqt6_qpushbutton_connect_clicked(btn: QtPushButtonPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qpushbutton_connect_toggled(btn: QtPushButtonPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qcheckbox_connect_toggled(cb_widget: QtCheckBoxPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qcheckbox_connect_state_changed(cb_widget: QtCheckBoxPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qradiobutton_connect_toggled(rb: QtRadioButtonPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qlineedit_connect_text_changed(le: QtLineEditPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qcombobox_connect_current_index_changed(cb_widget: QtComboBoxPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qslider_connect_value_changed(s: QtSliderPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qspinbox_connect_value_changed(sb: QtSpinBoxPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qprogressbar_connect_value_changed(pb: QtProgressBarPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qlistwidget_connect_current_row_changed(lw: QtListWidgetPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qtabwidget_connect_current_changed(tw: QtTabWidgetPtr, cb: bytes, user_data: bytes) -> t.State: pass +def vqt6_qaction_connect_triggered(action: QtActionPtr, cb: bytes, user_data: bytes) -> t.State: pass