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

Add quick item childen (can be nested) #27

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,12 @@ jobs:
# Functional tests
- name: Build test app
run: cd tests-functionnal/funq-test-app && cmake . && make
- name: Build test qml app
run: cd tests-functionnal/funq-test-qml-app && cmake . && make
- name: Test injection
run: xvfb-run -a funq tests-functionnal/funq-test-app/funq-test-app --exit-after-startup
- name: Test injection qml
run: xvfb-run -a funq tests-functionnal/funq-test-qml-app/funq-test-qml-app --exit-after-startup
- name: Test functional
run: cd tests-functionnal && xvfb-run -a nosetests
if: ${{ matrix.nosetests != 0}}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Support QML children items

## [1.2.0] - 2019-08-12
### Added
Expand Down
2 changes: 1 addition & 1 deletion client/doc/qml_tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ to the README.rst file about it.
Create the test folder
----------------------

.. code-block::
.. code-block:: none

mkdir qmltest
cd qmltest
Expand Down
2 changes: 2 additions & 0 deletions client/doc/user_api/widgets_models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,5 @@ Example: ::
.. autoclass:: QuickItem

.. automethod:: QuickItem.click

.. automethod:: QuickItem.children
50 changes: 48 additions & 2 deletions client/funq/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,14 @@ class QuickItem(Object):
Represent a QQuickItem or derived.

You can get a :class:`QuickItem` instance by using
:meth:`QuickWindow.item`.
:meth:`QuickWindow.item` or by iterate over :meth:`QuickItem.children`
result

:var oid: Internal gitem ID [type: unsigned long long]
:var path: complete path to the object [type: str]
:var classes: list of names of class inheritance if it inherits from
QObject. [type: list(str) or None]
:var items: list of subitems [type: :class:`QuickItem`]
"""

CPP_CLASS = "QQuickItem"
Expand All @@ -946,6 +953,45 @@ def click(self):
oid=self.oid
)

def children(self, recursive=False):
"""
Returns children items on the :class:`QuickItem`.

:param recursive: when `True`, will call recursively for children items

Example::

quick_window = self.funq.active_widget()
root = quick_window.item('root')
children = root.children()
for child in children.iter():
print(child.properties())
"""
data = self.client.send_command("quick_item_children", oid=self.oid,
recursive=recursive)
return QuickItems.create(self.client, data)

@classmethod
def create(cls, client, data):
"""
Allow to create a :class: `QuickItem` from a dict data decoded from
json.
"""
self = super(QuickItem, cls).create(client, data)
self.items = [cls.create(client, d) for d in data.get('items', [])]

return self


class QuickItems(TreeItems):
"""
Allow to manipulate all children in a QQuickItem.

:var items: list of :class:`QuickItem`
"""

ITEM_CLASS = QuickItem


class QuickWindow(Widget):
"""
Expand Down Expand Up @@ -1026,4 +1072,4 @@ def item(self, alias=None, path=None, id=None):
path=path,
qid=id,
)
return Object.create(self.client, data)
return QuickItem.create(self.client, data)
49 changes: 49 additions & 0 deletions server/libFunq/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,55 @@ QtJson::JsonObject Player::model_item_action(
return result;
}

void dump_quick_items(Player * player, const QList<QQuickItem *> & items, const qulonglong & viewid, bool recursive, QtJson::JsonObject & out) {
rafaeldelucena marked this conversation as resolved.
Show resolved Hide resolved
#ifdef QT_QUICK_LIB
QtJson::JsonArray outitems;
foreach(QQuickItem* item, items) {
QtJson::JsonObject outitem;
qulonglong oid = player->registerObject(item);
outitem["oid"] = oid;
outitem["viewid"] = viewid;
QObject * itemObject = dynamic_cast<QObject *>(item);
if (itemObject) {
const QMetaObject * mo = itemObject->metaObject();
QStringList classes;
while (mo) {
classes << mo->className();
mo = mo->superClass();
}
outitem["classes"] = classes;
outitem["path"] = objectPath(itemObject);
}
if (recursive) {
dump_quick_items(player, item->childItems(), viewid, recursive, outitem);
}
outitems << outitem;
}
out["items"] = outitems;
#else
(void)player;
(void)items;
(void)viewid;
(void)recursive;
(void)out;
#endif
}

QtJson::JsonObject Player::quick_item_children(const QtJson::JsonObject & command) {
QtJson::JsonObject result;
#ifdef QT_QUICK_LIB
QuickItemLocatorContext ctx(this, command, "oid");
if (ctx.hasError()) { return ctx.lastError; }

bool recursive = command["recursive"].toBool();

dump_quick_items(this, ctx.item->childItems(), ctx.id, recursive, result);
#else
result = createQtQuickOnlyError();
#endif
return result;
}

void Player::_model_item_action(const QString & action,
QAbstractItemView * widget,
const QModelIndex & index) {
Expand Down
1 change: 1 addition & 0 deletions server/libFunq/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public slots:

QtJson::JsonObject quick_item_find(const QtJson::JsonObject & command);
QtJson::JsonObject quick_item_click(const QtJson::JsonObject & command);
QtJson::JsonObject quick_item_children(const QtJson::JsonObject & command);

protected:
QtJson::JsonObject createQtQuickOnlyError() {
Expand Down
4 changes: 4 additions & 0 deletions tests-functionnal/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ def start_dialog(self, btn_name):

def get_status_text(self):
return self.funq.widget(path='mainWindow::statusBar::QLabel').properties()['text']


class QmlAppTestCase(FunqTestCase):
__app_config_name__ = 'qml_app_test'
64 changes: 64 additions & 0 deletions tests-functionnal/funq-test-qml-app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.14)

# Set the project name and target
project(funq-test-qml-app)

# Specify C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Set a default build type if none is specified (important for CI)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()

# Find the appropriate version of Qt (either Qt5 or Qt6)
find_package(Qt6 QUIET COMPONENTS Widgets Quick Qml)
if (NOT Qt6_FOUND)
find_package(Qt5 REQUIRED COMPONENTS Widgets Quick Qml)
set(QT_VERSION_MAJOR 5)
else()
set(QT_VERSION_MAJOR 6)
endif()

# Set the output directory for the executable
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/)

# Add the main source file
add_executable(${PROJECT_NAME} main.cpp)

# Add the resource file and link against the appropriate Qt libraries
if (QT_VERSION_MAJOR EQUAL 5)
qt5_add_resources(QT_RESOURCES resources.qrc)
target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::Quick Qt5::Qml)
elseif (QT_VERSION_MAJOR EQUAL 6)
qt_add_resources(QT_RESOURCES resources.qrc)
target_link_libraries(${PROJECT_NAME} Qt6::Widgets Qt6::Quick Qt6::Qml)
endif()

# Include the generated resource files in the target
target_sources(${PROJECT_NAME} PRIVATE ${QT_RESOURCES})

# Platform-specific settings
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
# Windows-specific settings
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Release)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# macOS-specific settings (e.g., bundle into an .app)
set(MACOSX_BUNDLE TRUE)
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER com.yourdomain.funq-test-qml-app
MACOSX_BUNDLE_BUNDLE_NAME "FunqTestQMLApp"
MACOSX_BUNDLE_BUNDLE_VERSION "1.0"
MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Linux-specific settings
set(CMAKE_INSTALL_RPATH "$ORIGIN")
endif()

# Installation rules (optional, for installation packaging)
install(TARGETS ${PROJECT_NAME}
BUNDLE DESTINATION .
RUNTIME DESTINATION bin
)
12 changes: 12 additions & 0 deletions tests-functionnal/funq-test-qml-app/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <QQuickView>
#include <QGuiApplication>

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);

QQuickView * view = new QQuickView();
view->setSource(QUrl("qrc:///qml/children.qml"));
view->show();
return app.exec();
}
32 changes: 32 additions & 0 deletions tests-functionnal/funq-test-qml-app/qml/children.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import QtQuick 2.0

Rectangle {
id: main
width: 600
height: 600
color: "red"

Text {
text: "Parent"
}

Rectangle {
width: 400
height: 400
color: "green"
anchors.centerIn: parent
Text {
text: "Child"
}

Rectangle {
width: 200
height: 200
color: "blue"
anchors.centerIn: parent
Text {
text: "Grandchild"
}
}
}
}
5 changes: 5 additions & 0 deletions tests-functionnal/funq-test-qml-app/resources.qrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>qml/children.qml</file>
</qresource>
</RCC>
8 changes: 8 additions & 0 deletions tests-functionnal/funq.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ aliases = app_test.alias
executable_stdout = NULL
executable_stderr = NULL
screenshot_on_error = 1

[qml_app_test]
executable = ./funq-test-qml-app/funq-test-qml-app
funq_port = 9999
cwd = .
executable_stdout = NULL
executable_stderr = NULL
screenshot_on_error = 1
57 changes: 57 additions & 0 deletions tests-functionnal/test_qml_item_children.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-

# Copyright: SCLE SFE
# Contributor: Rafael de Lucena Valle <rafaeldelucena@gmail.com>
#
# This software is a computer program whose purpose is to test graphical
# applications written with the QT framework (http://qt.digia.com/).
#
# This software is governed by the CeCILL v2.1 license under French law and
# abiding by the rules of distribution of free software. You can use,
# modify and/ or redistribute the software under the terms of the CeCILL
# license as circulated by CEA, CNRS and INRIA at the following URL
# "http://www.cecill.info".
#
# As a counterpart to the access to the source code and rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty and the software's author, the holder of the
# economic rights, and the successive licensors have only limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading, using, modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean that it is complicated to manipulate, and that also
# therefore means that it is reserved for developers and experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and, more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL v2.1 license and that you accept its terms.

from base import QmlAppTestCase
from funq.client import FunqClient


class TestQmlItemChilden(QmlAppTestCase):

def get_children_by_property(self, prop, recursive=False):
self.funq = FunqClient()
widget = self.funq.active_widget()
item = widget.item(id='main')
children = item.children(recursive=recursive)
return [child.properties().get(prop)
for child in children.iter() if prop in child.properties()]

def test_non_recursive_children(self):
children = self.get_children_by_property('text')
self.assertIn('Parent', children)

def test_recursive_children(self):
children = self.get_children_by_property('text', recursive=True)
self.assertIn('Parent', children)
self.assertIn('Child', children)
self.assertIn('Grandchild', children)
Loading