Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate qml in Python #33

Open
wants to merge 9 commits into
base: roomlist
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 86 additions & 79 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,84 +17,91 @@ jobs:
max-parallel: 1
matrix:
os: [ubuntu-20.04, macos-10.15]
qt-version: [ '6.1.2' ]
python-version: [ '3.8' ]
qt-version: ["6.2.2"]
python-version: ["3.8"]

steps:
- uses: actions/checkout@v2

- name: Cache Qt
id: cache-qt
uses: actions/cache@v2
with:
path: ${{ runner.workspace }}/Qt
key: ${{ runner.os }}-Qt${{ matrix.qt-version }}-cache

- name: Install Qt
uses: jurplel/[email protected]
with:
version: ${{ matrix.qt-version }}
cached: ${{ steps.cache-qt.outputs.cache-hit }}
aqtversion: ==1.2.4 # <1.2.0 has problem with osx installation of Qt 6

- name: Cache Python dependencies
if: ${{ runner.os == 'Linux' }}
uses: actions/cache@v2
with:
path: /home/runner/.cache/pypoetry/
key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Cache Python dependencies
if: ${{ runner.os == 'macOS' }}
uses: actions/cache@v2
with:
path: /Users/runner/Library/Caches/pypoetry/
key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
if [ "${{ matrix.os }}" == "ubuntu-20.04" ]; then
sudo apt-get install llvm
fi

python${{ matrix.python-version }} -m pip install --upgrade pip
git clone --depth 1 --single-branch --branch 1.2.0a2 https://github.com/python-poetry/poetry.git
pushd poetry
python${{ matrix.python-version }} install-poetry.py --version 1.2.0a2
popd
rm -r poetry
echo "$HOME/.local/bin" >> $GITHUB_PATH

if [ "${{ runner.os }}" == "macOS" ]; then
# is needed for macos to make poetry available
export PATH="/Users/runner/.local/bin:$PATH"

# qt on macos is installed in framework mode, includes like 'QtCore/QUrl' cannot be resolved by default
ln -sf $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtCore/
ln -sf $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtGui/
ln -sf $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtNetwork/
fi
poetry env use ${{ matrix.python-version }}
poetry install --verbose
poetry run pip3 install PySide6 # temporary workaround, poetry can't install PySide6. TODO: investigate
# git clone https://github.com/quotient-im/libQuotient.git PyQuotient/libQuotient
git clone https://github.com/Aksem/libQuotient.git -b qt-6.1 PyQuotient/libQuotient

- name: Build
run: |
poetry env use ${{ matrix.python-version }}
mkdir -p build
pushd build
cmake -DBUILD_TESTING=OFF -DBUILD_WITH_QT6=ON -DQT_PATH=$Qt6_DIR -DPYTHON_ENV_VERSION=${{ matrix.python-version }} ../
make
popd

- name: Run unit tests
run: |
poetry env use ${{ matrix.python-version }}
poetry run python3 -m pytest
- uses: actions/checkout@v2

- name: Cache Qt
id: cache-qt
uses: actions/cache@v2
with:
path: ${{ runner.workspace }}/Qt
key: ${{ runner.os }}-Qt${{ matrix.qt-version }}-cache

- name: Install Qt
uses: jurplel/[email protected]
with:
version: ${{ matrix.qt-version }}
cached: ${{ steps.cache-qt.outputs.cache-hit }}
aqtversion: ==1.2.4 # <1.2.0 has problem with osx installation of Qt 6

- name: Cache Python dependencies
if: ${{ runner.os == 'Linux' }}
uses: actions/cache@v2
with:
path: /home/runner/.cache/pypoetry/
key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Cache Python dependencies
if: ${{ runner.os == 'macOS' }}
uses: actions/cache@v2
with:
path: /Users/runner/Library/Caches/pypoetry/
key: ${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Set up build environment(Linux)
if: ${{ runner.os == 'Linux' }}
run: |
# use gcc 10 on linux
CXX_VERSION_POSTFIX='-10'
echo "CC=gcc$CXX_VERSION_POSTFIX" >>$GITHUB_ENV
echo "CXX=g++$CXX_VERSION_POSTFIX" >>$GITHUB_ENV

- name: Install dependencies
run: |
if [ "${{ matrix.os }}" == "ubuntu-20.04" ]; then
sudo apt-get install llvm
fi

python${{ matrix.python-version }} -m pip install --upgrade pip
git clone --depth 1 --single-branch --branch 1.2.0a2 https://github.com/python-poetry/poetry.git
pushd poetry
python${{ matrix.python-version }} install-poetry.py --version 1.2.0a2
popd
rm -r poetry
echo "$HOME/.local/bin" >> $GITHUB_PATH

if [ "${{ runner.os }}" == "macOS" ]; then
# is needed for macos to make poetry available
export PATH="/Users/runner/.local/bin:$PATH"

# qt on macos is installed in framework mode, includes like 'QtCore/QUrl' cannot be resolved by default
ln -sf $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtCore.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtCore/
ln -sf $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtGui.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtGui/
ln -sf $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/* $Qt6_DIR/lib/QtNetwork.framework/Versions/A/Headers/${{ matrix.qt-version }}/QtNetwork/
fi
poetry env use ${{ matrix.python-version }}
poetry install --verbose
poetry run pip3 install PySide6 # temporary workaround, poetry can't install PySide6. TODO: investigate
git clone https://github.com/quotient-im/libQuotient.git PyQuotient/libQuotient

- name: Build
run: |
poetry env use ${{ matrix.python-version }}
mkdir -p build
pushd build
cmake -DBUILD_TESTING=OFF -DBUILD_WITH_QT6=ON -DQT_PATH=$Qt6_DIR -DPYTHON_ENV_VERSION=${{ matrix.python-version }} ../
make
popd

- name: Run unit tests
run: |
poetry env use ${{ matrix.python-version }}
poetry run python3 -m pytest
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ PyQuotient/libQuotient.a
__pycache__
.pytest_cache

.vscode
.vscode

build.sh
8 changes: 5 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ project(LibQuotientBindings)
option(USE_POETRY "Use poetry package manager" ON)
option(USE_PREBUILD_LIBQUOTIENT "Use prebuilt quotient" OFF)
SET(QT_PATH "" CACHE STRING "Path to qt directory")
SET(PYTHON_ENV_VERSION "3.9" CACHE STRING "Python env version(e.g. 3.9)")
SET(PYTHON_ENV_VERSION "3.8" CACHE STRING "Python env version(e.g. 3.8)")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_PATH}/lib/cmake/Qt6 ${QT_PATH}/lib/cmake/)

# ================================ General configuration ======================================
Expand Down Expand Up @@ -439,6 +439,8 @@ quotient_timelineitem_wrapper.cpp
quotient_timelineitem_wrapper.h
quotient_pendingeventitem_wrapper.cpp
quotient_pendingeventitem_wrapper.h
quotient_eventstatus_wrapper.h
quotient_eventstatus_wrapper.cpp
)
# generated wrappers end

Expand Down Expand Up @@ -705,9 +707,9 @@ target_link_libraries(${bindings_library} PRIVATE Qt6::Core Qt6::Gui Qt6::Networ
add_library(pyside STATIC IMPORTED GLOBAL)
# TODO: change hardcoded name
if(UNIX AND NOT APPLE)
SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.so.6.1)
SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.so.6.2)
else(APPLE)
SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.6.1.dylib)
SET_TARGET_PROPERTIES(pyside PROPERTIES IMPORTED_LOCATION ${pyside6_path}/libpyside6.abi3.6.2.dylib)
endif()
target_link_libraries(${bindings_library} PRIVATE pyside)

Expand Down
22 changes: 22 additions & 0 deletions PyQuotient/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
from enum import Flag
from PySide6 import QtCore
from .PyQuotient import Quotient
from __feature__ import snake_case, true_property


class EventStatus(QtCore.QObject):

@QtCore.QFlag
class Code(Flag):
Normal = 0x00 #< No special designation
Submitted = 0x01 #< The event has just been submitted for sending
FileUploaded = 0x02 #< The file attached to the event has been
# uploaded to the server
Departed = 0x03 #< The event has left the client
ReachedServer = 0x04 #< The server has received the event
SendingFailed = 0x05 #< The server could not receive the event
Redacted = 0x08 #< The event has been redacted
Replaced = 0x10 #< The event has been replaced
Hidden = 0x100


Quotient.EventStatus = EventStatus
4 changes: 4 additions & 0 deletions PyQuotient/typesystems/jobs/typesystem_basejob.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
<property name="error" type="int" get="error" />
<property name="errorString" type="QString" get="errorString" />
<property name="errorUrl" type="QUrl" get="errorUrl" />

<!-- remove deprecated methods -->
<modify-function signature="apiEndpoint()const" remove="true" />
<modify-function signature="setApiEndpoint(QString)" remove="true" />
</object-type>
</namespace-type>
</typesystem>
3 changes: 3 additions & 0 deletions PyQuotient/typesystems/typesystem_index.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<enum-type name="JoinState"/>
</namespace-type>

<rejection class="Quotient" function-name="fromJson" />
<rejection class="Quotient" function-name="doLoadEvent" />

<load-typesystem name="jobs/typesystem_basejob.xml" />
<load-typesystem name="typesystem_csapi.xml" />
<load-typesystem name="jobs/typesystem_requestdata.xml" />
Expand Down
4 changes: 2 additions & 2 deletions PyQuotient/typesystems/typesystem_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
<namespace-type name="Quotient">
<object-type name="Settings" />
<object-type name="SettingsGroup">
<declare-function signature="SettingsGroup(QString)" />
<!-- <declare-function signature="SettingsGroup(QString)" /> -->
</object-type>
<object-type name="AccountSettings">
<declare-function signature="AccountSettings(QString)" />
<!-- <declare-function signature="AccountSettings(QString)" /> -->
</object-type>
</namespace-type>
</typesystem>
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

`poetry install`

or use pip with qt repository `http://download.qt.io/official_releases/QtForPython/`(see dependencies and their versions in pyproject.toml) [More](https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html):
or use pip with qt repository `http://download.qt.io/official_releases/QtForPython/`(see dependencies and their versions in pyproject.toml) [More](https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html):

`pip3 install --index-url=http://download.qt.io/official_releases/QtForPython/ --trusted-host download.qt.io shiboken6 PySide6 shiboken6-generator`

2. Clone libQuotient:
Expand All @@ -29,7 +29,6 @@ Optional parameters:

**Note:**: deprecated API(properties, methods, etc) is not tested!


### Usage

After PyQuotient import use also `__feature__` import to be able to use API with snake case and true properties:
Expand All @@ -52,6 +51,8 @@ If you use pip, install development requirements:

Run tests:

* If you use poetry: `poetry run python -m pytest`
- If you use poetry: `poetry run python -m pytest`

- otherwise: `python -m pytest`

* otherwise: `python -m pytest`
Update resources in demo client: `poetry run pyside6-rcc demo/resources.qrc -o demo/resources.py`
60 changes: 60 additions & 0 deletions demo/chatroomwidget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from PySide6 import QtCore, QtWidgets, QtQuickWidgets, QtQml
from PyQuotient import Quotient

from demo.models.messageeventmodel import MessageEventModel

from __feature__ import snake_case, true_property


class ChatRoomWidget(QtWidgets.QWidget):
resourceRequested = QtCore.Signal()
roomSettingsRequested = QtCore.Signal()
showStatusMessage = QtCore.Signal()

def __init__(self, parent=None) -> None:
super().__init__(parent)

self.message_model = MessageEventModel(self)

QtQml.qmlRegisterUncreatableType(Quotient.Room, 'Quotient', 1, 0, 'Room', 'Room objects can only be created by libQuotient')
QtQml.qmlRegisterUncreatableType(Quotient.User, 'Quotient', 1, 0, 'User', 'User objects can only be created by libQuotient')
QtQml.qmlRegisterType(Quotient.GetRoomEventsJob, 'Quotient', 1, 0, 'GetRoomEventsJob')
QtQml.qmlRegisterType(MessageEventModel, 'Quotient', 1, 0, 'MessageEventModel')

QtQml.qmlRegisterType(Quotient.Settings, 'Quotient', 1, 0, 'Settings')

self.timeline_widget = QtQuickWidgets.QQuickWidget(self)
self.timeline_widget.size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.timeline_widget.resize_mode = QtQuickWidgets.QQuickWidget.SizeRootObjectToView

self.timeline_widget.root_context().set_context_property("messageModel", self.message_model)
self.timeline_widget.root_context().set_context_property("controller", self)
self.timeline_widget.root_context().set_context_property("room", None)

self.timeline_widget.source = "qrc:///qml/Timeline.qml"

self.message_text_edit = QtWidgets.QTextEdit(self)
self.message_text_edit.maximum_height = self.maximum_chat_edit_height()
self.send_button = QtWidgets.QPushButton('Send', self)
self.send_button.clicked.connect(self.send_message)

self.h_layout = QtWidgets.QHBoxLayout()
self.h_layout.add_widget(self.message_text_edit)
self.h_layout.add_widget(self.send_button)

self.v_layout = QtWidgets.QVBoxLayout(self)
self.v_layout.add_widget(self.timeline_widget)
self.v_layout.add_layout(self.h_layout)
self.set_layout(self.v_layout)


@QtCore.Slot(str, bool)
def onMessageShownChanged(self, eventId: str, shown: bool): # name should be in camelCase, because it is used so in QML
...

def maximum_chat_edit_height(self):
return self.maximum_height / 3

@QtCore.Slot()
def send_message(self):
...
11 changes: 8 additions & 3 deletions demo/mainwindow.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import math

import demo.resources
from PySide6 import QtCore, QtWidgets, QtGui
from PyQuotient import Quotient
from demo.accountregistry import AccountRegistry
from demo.chatroomwidget import ChatRoomWidget
from demo.logindialog import LoginDialog
from demo.roomlistdock import RoomListDock
from demo.pyquaternionroom import PyquaternionRoom
from __feature__ import snake_case, true_property


Expand All @@ -25,6 +26,10 @@ def __init__(self):
self.room_list_dock.roomSelected.connect(self.select_room)
self.add_dock_widget(QtCore.Qt.LeftDockWidgetArea, self.room_list_dock)

self.chat_room_widget = ChatRoomWidget(self)
self.chat_room_widget.showStatusMessage.connect(lambda x = '': self.status_bar().show_message(x))
self.set_central_widget(self.chat_room_widget)

self.create_menu()
# Only GUI, account settings will be loaded in invoke_login
self.load_settings()
Expand Down Expand Up @@ -219,8 +224,8 @@ def show_millis_to_recon(self, connection: Quotient.Connection):
user=connection.local_user_id, seconds=math.ceil(connection.millis_to_reconnect)
))

@QtCore.Slot(PyquaternionRoom)
def select_room(self, room: PyquaternionRoom) -> None:
@QtCore.Slot(Quotient.Room)
def select_room(self, room: Quotient.Room) -> None:
if room is not None:
print(f'Opening room {room.object_name()}')
elif self.current_room is not None:
Expand Down
Loading