摇摆小部件示例#
此示例展示如何以两种不同的方式与自定义小部件交互
从 C++ 示例的完整 Python 翻译
从 C++ 文件生成的 Python 绑定
原始示例包含三个不同的文件
main.cpp/h
,翻译为main.py
,dialog.cpp/h
,翻译为dialog.py
,wigglywidget.cpp/h
,翻译为wigglywidget.py
,但同时也保持原样,以启用通过 Shiboken 的绑定生成。
在 dialog.py
文件中,您将找到两个与前面描述的两种方法相关的导入:
# Python translated file
from wigglywidget import WigglyWidget
# Binding module create with Shiboken
from wiggly import WigglyWidget
构建绑定的步骤#
最重要的文件是
bindings.xml
,用于指定我们想要从 C++ 暴露给 Python 的类bindings.h
,用于包含我们想要暴露的类头文件CMakeList.txt
,包含构建共享库(DLL 或 dylib)的所有指令pyside_config.py
,位于 utils 文件夹中的一级目录中,用于获取 Shiboken 和 PySide 的路径。
现在创建一个 build/
目录,然后在其中运行 cmake
以使用提供的 CMakeLists.txt
macOS/Linux
cd ~/pyside-setup/examples/widgetbinding
Windows
cd C:\pyside-setup\examples\widgetbinding
mkdir build
cd build
cmake -S.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
ninja
ninja install
cd ..
然后可以通过以下方式运行最终示例:
python main.py
你应该看到两个相同的自定义小部件,一个是 Python 翻译版本,另一个是 C++ 版本。
结语#
由于本示例是通过混合 scriptableapplication
和 samplebinding
示例的概念生成的,因此您可以补充 README 中的这些目录中的示例。
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef BINDINGS_H
#define BINDINGS_H
#include "wigglywidget.h"
#endif // BINDINGS_H
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtWidgets import QDialog, QLineEdit, QVBoxLayout
# Python binding from the C++ widget
from wiggly import WigglyWidget as WigglyWidgetCPP
# Python-only widget
from wigglywidget import WigglyWidget as WigglyWidgetPY
class Dialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
wiggly_widget_py = WigglyWidgetPY(self)
wiggly_widget_cpp = WigglyWidgetCPP(self)
lineEdit = QLineEdit(self)
layout = QVBoxLayout(self)
layout.addWidget(wiggly_widget_py)
layout.addWidget(wiggly_widget_cpp)
layout.addWidget(lineEdit)
lineEdit.setClearButtonEnabled(True)
wiggly_widget_py.running = True
wiggly_widget_cpp.setRunning(True)
lineEdit.textChanged.connect(wiggly_widget_py.setText)
lineEdit.textChanged.connect(wiggly_widget_cpp.setText)
lineEdit.setText("🖖 Hello world!")
self.setWindowTitle("Wiggly")
self.resize(360, 145)
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MACROS_H
#define MACROS_H
#include <QtCore/qglobal.h>
// Export symbols when creating .dll and .lib, and import them when using .lib.
#if BINDINGS_BUILD
# define BINDINGS_API Q_DECL_EXPORT
#else
# define BINDINGS_API Q_DECL_IMPORT
#endif
#endif // MACROS_H
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import sys
from PySide6.QtWidgets import QApplication
from dialog import Dialog
if __name__ == "__main__":
app = QApplication()
w = Dialog()
w.show()
sys.exit(app.exec())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
from wigglywidget import WigglyWidget
# Set PYSIDE_DESIGNER_PLUGINS to point to this directory and load the plugin
TOOLTIP = "A cool wiggly widget (Python)"
DOM_XML = """
<ui language='c++'>
<widget class='WigglyWidget' name='wigglyWidget'>
<property name='geometry'>
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>200</height>
</rect>
</property>
<property name='text'>
<string>Hello, world</string>
</property>
</widget>
</ui>
"""
if __name__ == '__main__':
QPyDesignerCustomWidgetCollection.registerCustomWidget(WigglyWidget, module="wigglywidget",
tool_tip=TOOLTIP, xml=DOM_XML)
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "wigglywidget.h"
#include <QtGui/QFontMetrics>
#include <QtGui/QPainter>
#include <QtCore/QTimerEvent>
//! [0]
WigglyWidget::WigglyWidget(QWidget *parent)
: QWidget(parent)
{
setBackgroundRole(QPalette::Midlight);
setAutoFillBackground(true);
QFont newFont = font();
newFont.setPointSize(newFont.pointSize() + 20);
setFont(newFont);
}
//! [0]
//! [1]
void WigglyWidget::paintEvent(QPaintEvent * /* event */)
//! [1] //! [2]
{
if (m_text.isEmpty())
return;
static constexpr int sineTable[16] = {
0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38
};
QFontMetrics metrics(font());
int x = (width() - metrics.horizontalAdvance(m_text)) / 2;
int y = (height() + metrics.ascent() - metrics.descent()) / 2;
QColor color;
//! [2]
//! [3]
QPainter painter(this);
//! [3] //! [4]
int offset = 0;
const auto codePoints = m_text.toUcs4();
for (char32_t codePoint : codePoints) {
const int index = (m_step + offset++) % 16;
color.setHsv((15 - index) * 16, 255, 191);
painter.setPen(color);
QString symbol = QString::fromUcs4(&codePoint, 1);
const int dy = (sineTable[index] * metrics.height()) / 400;
painter.drawText(x, y - dy, symbol);
x += metrics.horizontalAdvance(symbol);
}
}
//! [4]
//! [5]
void WigglyWidget::timerEvent(QTimerEvent *event)
//! [5] //! [6]
{
if (event->timerId() == m_timer.timerId()) {
++m_step;
update();
} else {
QWidget::timerEvent(event);
}
//! [6]
}
QString WigglyWidget::text() const
{
return m_text;
}
void WigglyWidget::setText(const QString &newText)
{
m_text = newText;
}
bool WigglyWidget::isRunning() const
{
return m_timer.isActive();
}
void WigglyWidget::setRunning(bool r)
{
if (r == isRunning())
return;
if (r)
m_timer.start(60, this);
else
m_timer.stop();
}
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef WIGGLYWIDGET_H
#define WIGGLYWIDGET_H
#include "macros.h"
#include <QtWidgets/QWidget>
#include <QtCore/QBasicTimer>
//! [0]
class BINDINGS_API WigglyWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool running READ isRunning WRITE setRunning)
Q_PROPERTY(QString text READ text WRITE setText)
public:
WigglyWidget(QWidget *parent = nullptr);
QString text() const;
bool isRunning() const;
public slots:
void setText(const QString &newText);
void setRunning(bool r);
protected:
void paintEvent(QPaintEvent *event) override;
void timerEvent(QTimerEvent *event) override;
private:
QBasicTimer m_timer;
QString m_text;
int m_step = 0;
};
//! [0]
#endif
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtCore import QBasicTimer, Property
from PySide6.QtGui import QColor, QFontMetrics, QPainter, QPalette
from PySide6.QtWidgets import QWidget
class WigglyWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._step = 0
self._text = ""
self.setBackgroundRole(QPalette.Midlight)
self.setAutoFillBackground(True)
new_font = self.font()
new_font.setPointSize(new_font.pointSize() + 20)
self.setFont(new_font)
self._timer = QBasicTimer()
def isRunning(self):
return self._timer.isActive()
def setRunning(self, r):
if r == self.isRunning():
return
if r:
self._timer.start(60, self)
else:
self._timer.stop()
def paintEvent(self, event):
if not self._text:
return
sineTable = [0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100,
-92, -71, -38]
metrics = QFontMetrics(self.font())
x = (self.width() - metrics.horizontalAdvance(self.text)) / 2
y = (self.height() + metrics.ascent() - metrics.descent()) / 2
color = QColor()
with QPainter(self) as painter:
for i in range(len(self.text)):
index = (self._step + i) % 16
color.setHsv((15 - index) * 16, 255, 191)
painter.setPen(color)
dy = (sineTable[index] * metrics.height()) / 400
c = self._text[i]
painter.drawText(x, y - dy, str(c))
x += metrics.horizontalAdvance(c)
def timerEvent(self, event):
if event.timerId() == self._timer.timerId():
self._step += 1
self.update()
else:
QWidget.timerEvent(event)
def text(self):
return self._text
def setText(self, text):
self._text = text
running = Property(bool, isRunning, setRunning)
text = Property(str, text, setText)