Skip to content

Commit ef7bb87

Browse files
committed
Add QIOPipe
Add an implementation for a QIODevice that can be used to work with anonymous pipes. It needs to be able to emit the bytesWritten and readyRead signals. This implementation is lifted from the qt5 source tree (qtdeclarative/tests/auto/qmlls/lifecycle) and might be added to Qt in the future, at which point it will be removed from the PySide source tree. Change-Id: Iff1208a366dad747352e7507da0818934c26aa4f Reviewed-by: Friedemann Kleint <[email protected]>
1 parent 52d7a31 commit ef7bb87

File tree

7 files changed

+225
-3
lines changed

7 files changed

+225
-3
lines changed

sources/pyside6/PySide6/QtCore/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33

44
project(QtCore)
55

6+
set(CMAKE_AUTOMOC ON)
7+
68
set(QtCore_DROPPED_ENTRIES )
79

810
set(QtCore_static_sources
911
"${QtCore_SOURCE_DIR}/glue/qeasingcurve_glue.cpp"
1012
"${QtCore_SOURCE_DIR}/glue/core_snippets.cpp"
1113
"${QtCore_SOURCE_DIR}/glue/qtcorehelper.cpp"
14+
"${QtCore_SOURCE_DIR}/glue/qiopipe.cpp"
15+
"${pyside6_SOURCE_DIR}/qiopipe.h"
1216
)
1317

1418
if(ENABLE_WIN)
@@ -163,6 +167,7 @@ ${QtCore_GEN_DIR}/qsystemsemaphore_wrapper.cpp
163167
${QtCore_GEN_DIR}/qt_wrapper.cpp
164168
${QtCore_GEN_DIR}/qtcorehelper_qgenericargumentholder_wrapper.cpp
165169
${QtCore_GEN_DIR}/qtcorehelper_qgenericreturnargumentholder_wrapper.cpp
170+
${QtCore_GEN_DIR}/qtcorehelper_qiopipe_wrapper.cpp
166171
${QtCore_GEN_DIR}/qtcorehelper_qmutexlocker_wrapper.cpp
167172
${QtCore_GEN_DIR}/qtemporarydir_wrapper.cpp
168173
${QtCore_GEN_DIR}/qtemporaryfile_wrapper.cpp
@@ -242,6 +247,7 @@ set(QtCore_include_dirs ${QtCore_SOURCE_DIR}
242247
)
243248
set(QtCore_libraries pyside6
244249
${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
250+
Qt::CorePrivate
245251
)
246252

247253
create_pyside_module(NAME QtCore
@@ -281,4 +287,5 @@ if (APPLE)
281287
endforeach()
282288
endif()
283289

284-
install(FILES ${pyside6_SOURCE_DIR}/qtcorehelper.h DESTINATION include/PySide6/QtCore/)
290+
install(FILES ${pyside6_SOURCE_DIR}/qtcorehelper.h ${pyside6_SOURCE_DIR}/qiopipe.h
291+
DESTINATION include/PySide6/QtCore/)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
#include <qtcorehelper.h>
2+
#include <qiopipe.h>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright (C) 2024 The Qt Company Ltd.
2+
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3+
4+
#include "qiopipe.h"
5+
6+
#include <QtCore/private/qobject_p.h>
7+
#include <QtCore/qdebug.h>
8+
#include <QtCore/qpointer.h>
9+
10+
#include <memory>
11+
12+
QT_BEGIN_NAMESPACE
13+
14+
namespace QtCoreHelper
15+
{
16+
17+
class QPipeEndPoint : public QIODevice
18+
{
19+
Q_OBJECT
20+
21+
public:
22+
bool isSequential() const override;
23+
qint64 bytesAvailable() const override;
24+
25+
void setRemoteEndPoint(QPipeEndPoint *other);
26+
27+
protected:
28+
qint64 readData(char *data, qint64 maxlen) override;
29+
qint64 writeData(const char *data, qint64 len) override;
30+
31+
private:
32+
QByteArray m_buffer;
33+
QPointer<QPipeEndPoint> m_remoteEndPoint;
34+
};
35+
36+
class QIOPipePrivate final : public QObjectPrivate
37+
{
38+
Q_DECLARE_PUBLIC(QIOPipe)
39+
public:
40+
QIOPipePrivate();
41+
~QIOPipePrivate() {};
42+
43+
std::unique_ptr<QPipeEndPoint> end1;
44+
std::unique_ptr<QPipeEndPoint> end2;
45+
};
46+
47+
QIOPipe::QIOPipe(QObject *parent) : QObject(*(new QIOPipePrivate()), parent) { }
48+
49+
bool QIOPipe::open(QIODevice::OpenMode mode)
50+
{
51+
Q_D(QIOPipe);
52+
53+
if (!d->end1->open(mode))
54+
return false;
55+
switch (mode & QIODevice::ReadWrite) {
56+
case QIODevice::WriteOnly:
57+
case QIODevice::ReadOnly:
58+
return d->end2->open(mode ^ QIODevice::ReadWrite);
59+
default:
60+
return d->end2->open(mode);
61+
}
62+
}
63+
64+
QIODevice *QIOPipe::end1() const
65+
{
66+
Q_D(const QIOPipe);
67+
return d->end1.get();
68+
}
69+
70+
QIODevice *QIOPipe::end2() const
71+
{
72+
Q_D(const QIOPipe);
73+
return d->end2.get();
74+
}
75+
76+
QIOPipePrivate::QIOPipePrivate() : end1(std::make_unique<QPipeEndPoint>()),
77+
end2(std::make_unique<QPipeEndPoint>())
78+
{
79+
end1->setRemoteEndPoint(end2.get());
80+
end2->setRemoteEndPoint(end1.get());
81+
}
82+
83+
bool QPipeEndPoint::isSequential() const
84+
{
85+
return true;
86+
}
87+
88+
qint64 QPipeEndPoint::bytesAvailable() const
89+
{
90+
return m_buffer.size() + QIODevice::bytesAvailable();
91+
}
92+
93+
void QPipeEndPoint::setRemoteEndPoint(QPipeEndPoint *other)
94+
{
95+
m_remoteEndPoint = other;
96+
}
97+
98+
qint64 QPipeEndPoint::readData(char *data, qint64 maxlen)
99+
{
100+
maxlen = qMin(maxlen, static_cast<qint64>(m_buffer.size()));
101+
if (maxlen <= 0)
102+
return 0;
103+
104+
Q_ASSERT(maxlen > 0);
105+
memcpy(data, m_buffer.data(), static_cast<size_t>(maxlen));
106+
m_buffer = m_buffer.mid(maxlen);
107+
return maxlen;
108+
}
109+
110+
qint64 QPipeEndPoint::writeData(const char *data, qint64 len)
111+
{
112+
if (!m_remoteEndPoint)
113+
return -1;
114+
115+
if (len <= 0)
116+
return 0;
117+
118+
QByteArray &buffer = m_remoteEndPoint->m_buffer;
119+
const qint64 prevLen = buffer.size();
120+
Q_ASSERT(prevLen >= 0);
121+
len = qMin(len, std::numeric_limits<int>::max() - prevLen);
122+
123+
if (len == 0)
124+
return 0;
125+
126+
Q_ASSERT(len > 0);
127+
Q_ASSERT(prevLen + len > 0);
128+
Q_ASSERT(prevLen + len <= std::numeric_limits<int>::max());
129+
130+
buffer.resize(prevLen + len);
131+
memcpy(buffer.data() + prevLen, data, static_cast<size_t>(len));
132+
Q_EMIT bytesWritten(len);
133+
Q_EMIT m_remoteEndPoint->readyRead();
134+
return len;
135+
}
136+
137+
} // namespace QtCoreHelper
138+
139+
QT_END_NAMESPACE
140+
141+
#include "qiopipe.moc"

sources/pyside6/PySide6/QtCore/typesystem_core_common.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,6 +2472,7 @@
24722472
<inject-code file="../glue/qtcore.cpp" snippet="unlock"/>
24732473
</add-function>
24742474
</object-type>
2475+
<object-type name="QIOPipe"/>
24752476
<value-type name="QGenericArgumentHolder"/>
24762477
<value-type name="QGenericReturnArgumentHolder"/>
24772478
</namespace-type>

sources/pyside6/PySide6/qiopipe.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (C) 2024 The Qt Company Ltd.
2+
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3+
4+
#ifndef QIOPIPE_H
5+
#define QIOPIPE_H
6+
7+
#include <QtCore/qiodevicebase.h>
8+
#include <QtCore/qobject.h>
9+
10+
QT_BEGIN_NAMESPACE
11+
12+
class QIODevice;
13+
14+
namespace QtCoreHelper
15+
{
16+
17+
class QIOPipePrivate;
18+
class QIOPipe : public QObject
19+
{
20+
Q_OBJECT
21+
Q_DECLARE_PRIVATE(QIOPipe)
22+
23+
public:
24+
QIOPipe(QObject *parent = nullptr);
25+
26+
bool open(QIODeviceBase::OpenMode mode);
27+
28+
QIODevice *end1() const;
29+
QIODevice *end2() const;
30+
};
31+
32+
} // namespace QtCoreHelper
33+
34+
QT_END_NAMESPACE
35+
36+
#endif // QIOPIPE_H

sources/pyside6/libpyside/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ set(libpyside_libraries Qt::Core Qt::CorePrivate)
88
set(CMAKE_AUTOMOC ON)
99

1010
set(libpyside_HEADERS # installed below
11-
pysideqslotobject_p.h
1211
class_property.h
1312
dynamicqmetaobject.h
1413
feature_select.h
@@ -33,6 +32,7 @@ set(libpyside_HEADERS # installed below
3332
pysideqhash.h
3433
pysideqmetatype.h
3534
pysideqobject.h
35+
pysideqslotobject_p.h
3636
pysidesignal.h
3737
pysidesignal_p.h
3838
pysideslot_p.h
@@ -44,7 +44,6 @@ set(libpyside_HEADERS # installed below
4444
)
4545

4646
set(libpyside_SRC
47-
pysideqslotobject_p.cpp
4847
class_property.cpp
4948
dynamicqmetaobject.cpp
5049
feature_select.cpp
@@ -53,6 +52,7 @@ set(libpyside_SRC
5352
pysideclassdecorator.cpp
5453
pysideclassinfo.cpp
5554
pysideqenum.cpp
55+
pysideqslotobject_p.cpp
5656
pysidemetafunction.cpp
5757
pysidesignal.cpp
5858
pysideslot.cpp
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright (C) 2024 The Qt Company Ltd.
2+
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3+
4+
'''Test cases for the QIOPipe class'''
5+
6+
from PySide6.QtCore import QIODevice, QIOPipe
7+
8+
import unittest
9+
10+
11+
class QIOPipeTest(unittest.TestCase):
12+
def setUp(self) -> None:
13+
self.pipe = QIOPipe()
14+
self.pipe.open(QIODevice.OpenModeFlag.ReadWrite)
15+
return super().setUp()
16+
17+
def tearDown(self) -> None:
18+
super().tearDown()
19+
20+
def ready_read_bytes_written(self):
21+
received_data = self.pipe.end2().readAll().data()
22+
self.assertEqual(received_data, self.data)
23+
24+
def test_readyRead(self):
25+
self.data = b"Hello, World!"
26+
self.pipe.end2().readyRead.connect(self.ready_read_bytes_written)
27+
self.pipe.end1().write(self.data)
28+
29+
def test_bytesWritten(self):
30+
self.data = b"Hello, World!"
31+
self.pipe.end2().bytesWritten.connect(self.ready_read_bytes_written)
32+
self.pipe.end1().write(self.data)
33+
34+
35+
if __name__ == '__main__':
36+
unittest.main()

0 commit comments

Comments
 (0)