View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0017052 | Scribus | Scripter | public | 2023-11-18 08:21 | 2024-01-07 16:30 |
Reporter | ale | Assigned To | ale | ||
Priority | normal | Severity | feature | Reproducibility | N/A |
Status | closed | Resolution | fixed | ||
Product Version | 1.7.0.svn | ||||
Fixed in Version | 1.6.1.svn | ||||
Summary | 0017052: Scripter: add functions to get the items in a group | ||||
Description | it should be possible to manipulate the items in a group without ungrouping it. two functions are needed: - get all direct items in the group: getGroupItems() - get all items in the group recursively: getAllGroupItems() | ||||
Tags | No tags attached. | ||||
Patch | Yes | ||||
|
i have createdgetGroupItems(["name", recursive=False, type=0]) -> [dict] i will be creating a few tests and if everything is fine i can soon propose a patch for it. |
|
here it is. with a test file that tests a few use cases and shows how to use the new function. get-group-items.diff (4,969 bytes)
diff --git a/scribus/plugins/scriptplugin/cmdmani.cpp b/scribus/plugins/scriptplugin/cmdmani.cpp index ce0ea2ba3eee7cc56b3bba9b615ac95df82a8a9f..4c6ceec57659598fcbb232d2ca9193fcea6278c8 100644 --- a/scribus/plugins/scriptplugin/cmdmani.cpp +++ b/scribus/plugins/scriptplugin/cmdmani.cpp @@ -12,6 +12,7 @@ for which a new license (GPL+exception) is in place. #include "sctextstream.h" #include "selection.h" #include "undomanager.h" +#include <QQueue> PyObject *scribus_loadimage(PyObject* /* self */, PyObject* args) { @@ -451,6 +452,62 @@ PyObject *scribus_scalegroup(PyObject* /* self */, PyObject* args) Py_RETURN_NONE; } +PyObject *scribus_getGroupItems(PyObject * /*self*/, PyObject* args, PyObject* kw) +{ + if (!checkHaveDocument()) + return nullptr; + + char *name = const_cast<char*>(""); + unsigned int recursive = 0; + unsigned int type = 0; + char* kwargs[] = {const_cast<char*>("name"), const_cast<char*>("recursive"), const_cast<char*>("type"), nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|espi", kwargs, "utf-8", &name, &recursive, &type)) + return nullptr; + + PageItem *pageItem = GetUniqueItem(QString::fromUtf8(name)); + if (pageItem == nullptr) + return nullptr; + + if (!pageItem->isGroup()) + return nullptr; + + auto items = PyList_New(0); + + QQueue<PageItem*> queue; + queue.enqueue(pageItem); + + while (!queue.isEmpty()) + { + const auto pageItem = queue.dequeue(); + for (auto groupItem: pageItem->groupItemList) + { + if (recursive && groupItem->isGroup()) + { + queue.enqueue(groupItem); + continue; + } + if (type == 0 || groupItem->itemType() == type) { + auto itemDict = PyDict_New(); + PyDict_SetItem(itemDict, + PyUnicode_FromString("name"), + PyUnicode_FromString(groupItem->itemName().toUtf8().constData()) + ); + PyDict_SetItem(itemDict, + PyUnicode_FromString("type"), + PyLong_FromUnsignedLong((unsigned long) groupItem->itemType()) + ); + PyDict_SetItem(itemDict, + PyUnicode_FromString("order"), + PyLong_FromUnsignedLong((unsigned long) groupItem->uniqueNr) + ); + PyList_Append(items, itemDict); + } + } + } + + return items; +} + PyObject *scribus_getselectedobject(PyObject* /* self */, PyObject* args) { int i = 0; @@ -707,6 +764,7 @@ void cmdmanidocwarnings() s << scribus_combinepolygons__doc__ << scribus_deselectall__doc__ << scribus_flipobject__doc__ + << scribus_getGroupItems__doc__ << scribus_getselectedobject__doc__ << scribus_groupobjects__doc__ << scribus_islocked__doc__ diff --git a/scribus/plugins/scriptplugin/cmdmani.h b/scribus/plugins/scriptplugin/cmdmani.h index e4d4253a74c17fd8b3f2c3de175932446c1439a3..f42d53bc83dbf2cc9463cc89b0c37451d78894c4 100644 --- a/scribus/plugins/scriptplugin/cmdmani.h +++ b/scribus/plugins/scriptplugin/cmdmani.h @@ -148,6 +148,21 @@ May raise ValueError if an invalid scale factor is passed.\n\ /*! Scale group with object name */ PyObject *scribus_scalegroup(PyObject * /*self*/, PyObject* args); +/*! docstring */ +PyDoc_STRVAR(scribus_getGroupItems__doc__, +QT_TR_NOOP("getGroupItems([\"name\", recursive=False, type=0]) -> [dict]\n\n\ +Return the list of items in the group.\n\ +\n\ +`recursive`: if True and some of the items are groups, also include their items (recursively).\n\ +`type`: if not 0, only retain items of this type.\n\ +\n\ +Each item is defined as a dictionary containing:\n\ +`{'name': str, 'type': int, 'order': int}`\n\ +\n\ +If \"name\" is not given the currently selected item is used.")); +/*! List the items in a group. */ +PyObject *scribus_getGroupItems(PyObject * /*self*/, PyObject* args, PyObject* kw); + /*! docstring */ PyDoc_STRVAR(scribus_loadimage__doc__, QT_TR_NOOP("loadImage(\"filename\" [, \"name\"])\n\ diff --git a/scribus/plugins/scriptplugin/scriptplugin.cpp b/scribus/plugins/scriptplugin/scriptplugin.cpp index 2836dfb9672dd54a06ee5ff98f8084e54a9b9f7c..a5ca29ba30deefebb0b42374c4154a98eb65366b 100644 --- a/scribus/plugins/scriptplugin/scriptplugin.cpp +++ b/scribus/plugins/scriptplugin/scriptplugin.cpp @@ -364,6 +364,7 @@ PyMethodDef scribus_methods[] = { {const_cast<char*>("getFontNames"), (PyCFunction)scribus_getfontnames, METH_NOARGS, tr(scribus_getfontnames__doc__)}, {const_cast<char*>("getFontSize"), scribus_getfontsize, METH_VARARGS, tr(scribus_getfontsize__doc__)}, {const_cast<char*>("getFrameText"), scribus_getframetext, METH_VARARGS, tr(scribus_getframetext__doc__)}, + {const_cast<char*>("getGroupItems"), (PyCFunction)scribus_getGroupItems, METH_VARARGS|METH_KEYWORDS, tr(scribus_getGroupItems__doc__)}, {const_cast<char*>("getGuiLanguage"), (PyCFunction)scribus_getlanguage, METH_NOARGS, tr(scribus_getlanguage__doc__)}, {const_cast<char*>("getHGuides"), (PyCFunction)scribus_getHguides, METH_NOARGS, tr(scribus_getHguides__doc__)}, {const_cast<char*>("getImageColorSpace"), scribus_getimagecolorspace, METH_VARARGS, tr(scribus_getimagecolorspace__doc__) }, getGroupItems.py (3,838 bytes)
#!/usr/bin/env python3 import sys import subprocess try: import scribus except ImportError: pass def run_script() : scribus.newDocument(scribus.PAPER_A4_MM, (10, 10, 10, 10), \ scribus.PORTRAIT, 1, scribus.UNIT_MILLIMETERS, scribus.PAGE_1, 0, 1) # Test a group with text and an image frames scribus.createText(10, 10, 20, 20, 'text-01') scribus.insertText('some text', 0, 'text-01') scribus.createImage(10, 30, 20, 20, 'image-02') group_name = scribus.groupObjects(['text-01', 'image-02']) scribus.setItemName('group-03', group_name) group_items = scribus.getGroupItems('group-03') assert sorted([item['name'] for item in group_items]) == \ sorted(['text-01', 'image-02']) assert sorted([item['type'] for item in group_items]) == sorted([4, 2]) group_items = scribus.getGroupItems('group-03', type=4) assert sorted([item['name'] for item in group_items]) == \ ['text-01'] scribus.unGroupObjects('group-03') # the group does not exist anymore: this should now fail try: group_items = scribus.getGroupItems('group-03') assert False except scribus.NoValidObjectError: assert True # Test recursivity with a group with text frame, an image one and a group scribus.createEllipse(10, 50, 20, 20, 'ellipse-04') scribus.createRect(10, 70, 20, 20, 'square-05') group_name = scribus.groupObjects(['ellipse-04', 'square-05']) scribus.setItemName('group-06', group_name) group_name = scribus.groupObjects(['text-01', 'image-02', 'group-06']) scribus.setItemName('group-07', group_name) group_items = scribus.getGroupItems('group-07') assert sorted([item['name'] for item in group_items]) == \ sorted(['text-01', 'image-02', 'group-06']) group_items = scribus.getGroupItems('group-07', recursive=True) assert sorted([item['name'] for item in group_items]) == \ sorted(['text-01', 'image-02', 'ellipse-04', 'square-05']) assert sorted([item['type'] for item in group_items]) == sorted([4, 2, 6, 6]) group_items = scribus.getGroupItems('group-07') assert sorted([item['name'] for item in group_items]) == \ sorted(['text-01', 'image-02', 'group-06']) group_items = scribus.getGroupItems('group-07', recursive=True, type=4) assert sorted([item['name'] for item in group_items]) == \ ['text-01'] group_items = scribus.getGroupItems('group-07', recursive=True, type=6) assert sorted([item['name'] for item in group_items]) == \ sorted(['ellipse-04', 'square-05']) scribus.unGroupObjects('group-07') # Test getting by type with multiple of the same scribus.createText(10, 90, 20, 20, 'text-08') scribus.insertText('more text', 0, 'text-08') group_name = scribus.groupObjects(['text-01', 'image-02', 'text-08']) scribus.setItemName('group-09', group_name) group_items = scribus.getGroupItems('group-09', type=4) assert sorted([item['name'] for item in group_items]) == \ ['text-01', 'text-08'] # use the current selection scribus.deselectAll() scribus.selectObject('group-09') group_items = scribus.getGroupItems() assert sorted([item['name'] for item in group_items]) == \ sorted(['text-01', 'image-02', 'text-08']) scribus.unGroupObjects('group-09') # scribus.saveDocAs('getGroupItems.sla') # enable for starting manual testing def run_scribus(): # if len(sys.argv) < 2: # print(f'Usage: python3 {sys.argv[0]} filename') # return call_args = ['scribus', '-g', '-py', sys.argv[0]] # call_args += ['--', sys.argv[1]] subprocess.call(call_args) def main(): try: scribus run_script() except NameError: run_scribus() if __name__ == "__main__": main() |
|
any reason why this function has not been considered? i could have used it in a script that i'm writing today. it's basically necessary, for every script that goes through all items on a page or in a document. |
|
Nope, but we will review it. Thanks |
|
@ale, i don't think that the "order" key is set to what you want : groupItem->uniqueNr is just a unique ID used to identify an item in the document, not the item index in the group stack. In order to get the item order in the group stack, you should just iterate pageItem->groupItemList using a traditional index-based loop and not a range-for loop. |
|
If you want to be consistent with what scribus_getpageitems returns (and I think we should), you may also want to use Py_BuildValue instead of creating dictionaries. |
|
1. getPageItems seems to be using uniqueNr for the order and i copied that... but, indeed, both might be wrong. i did not check. 2. personally, for the results, i'd like to introduce more and more dictionaries rather than relying on the position in a list to give a meaning to the values... if both function should return the same "type" of result, i'd prefer getPageItems to return {0: name, 1: type, 2: order, 'name': name, 'type': type, 'order': order}, with a deprecation warning for the usage of 0, 1, and 2 as keys (that is, removing that from the docstring). if you agree with what i wrote above, i can make a new patch, that also modifies getPageItems() and its docstring. |
|
So I committed a version of the patch which returns items as tuples for consistency with getPageItems(). I also fixed a bug where groups embedded inside groups would not be listed in the returned list. For now I kept the uniqueNr returned for "order" as this is what getPageItems() already returns. But this uniqueNr is an indication of item creation order, and not an order in the page item list or in the group stack. So I am not sure how this "order" can be used currently. |
|
ok, thanks! |
Date Modified | Username | Field | Change |
---|---|---|---|
2023-11-18 08:21 | ale | New Issue | |
2023-11-18 21:34 | ale | Note Added: 0050487 | |
2023-11-18 21:34 | ale | Note Edited: 0050487 | |
2023-11-18 22:53 | ale | Note Added: 0050488 | |
2023-11-18 22:53 | ale | File Added: get-group-items.diff | |
2023-11-18 22:53 | ale | File Added: getGroupItems.py | |
2023-11-18 22:53 | ale | Summary | scripter: add functions to get the items in a group => [PATCH] scripter: add functions to get the items in a group |
2023-11-18 22:53 | ale | Patch | No => Yes |
2023-12-27 22:04 | ale | Note Added: 0050705 | |
2023-12-27 22:47 | cbradney | Note Added: 0050706 | |
2024-01-03 00:51 | jghali | Note Added: 0050777 | |
2024-01-03 21:58 | jghali | Note Added: 0050790 | |
2024-01-04 17:40 | ale | Note Added: 0050797 | |
2024-01-07 16:16 | jghali | Note Added: 0050844 | |
2024-01-07 16:16 | jghali | Summary | [PATCH] scripter: add functions to get the items in a group => Scripter: add functions to get the items in a group |
2024-01-07 16:17 | jghali | Assigned To | => ale |
2024-01-07 16:17 | jghali | Status | new => resolved |
2024-01-07 16:17 | jghali | Resolution | open => fixed |
2024-01-07 16:17 | jghali | Fixed in Version | => 1.6.1.svn |
2024-01-07 16:24 | cbradney | Status | resolved => closed |
2024-01-07 16:30 | ale | Note Added: 0050845 |