Skip to content

Commit

Permalink
GHA CI: add checks for grid items order
Browse files Browse the repository at this point in the history
Now all items under `QGridLayout` are required to be sorted. This allow
us to omit tabstop order. The tabstop order will follow the layout order.

The script can be invoked to fix wrong grid items order in .ui files:
```console
python check_grid_items_order.py file.ui
```
  • Loading branch information
Chocobo1 committed Dec 14, 2024
1 parent 4c6dd8e commit c887a6f
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 4 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci_python.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Lint code (auxiliary scripts)
run: |
pyflakes $PY_FILES
bandit --skip B314,B405 $PY_FILES
bandit --skip B101,B314,B405 $PY_FILES
- name: Format code (auxiliary scripts)
run: |
Expand All @@ -61,7 +61,7 @@ jobs:
echo $PY_FILES
echo "PY_FILES=$PY_FILES" >> "$GITHUB_ENV"
- name: Check typings (search engine)
- name: Check typings (search engine)
run: |
MYPYPATH="src/searchengine/nova3" \
mypy \
Expand Down
93 changes: 93 additions & 0 deletions .github/workflows/helper/pre-commit/check_grid_items_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python3

# A pre-commit hook for checking items order in grid layouts
# Copyright (C) 2024 Mike Tzou (Chocobo1)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# In addition, as a special exception, the copyright holders give permission to
# link this program with the OpenSSL project's "OpenSSL" library (or with
# modified versions of it that use the same license as the "OpenSSL" library),
# and distribute the linked executables. You must obey the GNU General Public
# License in all respects for all of the code used other than "OpenSSL". If you
# modify file(s), you may extend this exception to your version of the file(s),
# but you are not obligated to do so. If you do not wish to do so, delete this
# exception statement from your version.

from collections.abc import Callable, Sequence
from typing import Optional
import argparse
import re
import xml.etree.ElementTree as ElementTree
import sys


def traversePostOrder(root: ElementTree.Element, visitFunc: Callable[[ElementTree.Element], None]) -> None:
stack = [(root, False)]

while len(stack) > 0:
(element, visit) = stack.pop()
if visit:
visitFunc(element)
else:
stack.append((element, True))
stack.extend((child, False) for child in reversed(element))


def modifyElement(element: ElementTree.Element) -> None:
def getSortKey(e: ElementTree.Element) -> tuple[int, int]:
if e.tag == 'item':
return (int(e.attrib['row']), int(e.attrib['column']))
return (-1, -1) # don't care

if element.tag == 'layout' and element.attrib['class'] == 'QGridLayout' and len(element) > 0:
element[:] = sorted(element, key=getSortKey)

# workaround_2a: ElementTree will unescape `"` and we need to escape it back
if element.tag == 'string' and element.text is not None:
element.text = element.text.replace('"', '"')


def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv)

for filename in args.filenames:
with open(filename, 'r+') as f:
orig = f.read()
root = ElementTree.fromstring(orig)
traversePostOrder(root, modifyElement)
ElementTree.indent(root, ' ')

# workaround_1: cannot use `xml_declaration=True` since it uses single quotes instead of Qt preferred double quotes
ret = f'<?xml version="1.0" encoding="UTF-8"?>\n{ElementTree.tostring(root, 'unicode')}\n'

# workaround_2b: ElementTree will turn `&quot;` into `&amp;quot;`, so revert it back
ret = ret.replace('&amp;quot;', '&quot;')

# workaround_3: Qt prefers no whitespaces in self-closing tags
ret = re.sub('<(.+) +/>', r'<\1/>', ret)

if ret != orig:
f.seek(0)
f.write(ret)
f.truncate()

return 0


if __name__ == '__main__':
sys.exit(main())
6 changes: 4 additions & 2 deletions .github/workflows/helper/pre-commit/check_translation_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
# but you are not obligated to do so. If you do not wish to do so, delete this
# exception statement from your version.

from typing import Optional, Sequence
from collections.abc import Sequence
from typing import Optional
import argparse
import re
import sys


def main(argv: Optional[Sequence[str]] = None) -> int:
Expand Down Expand Up @@ -67,4 +69,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:


if __name__ == '__main__':
exit(main())
sys.exit(main())
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
repos:
- repo: local
hooks:
- id: check-grid-order
name: Check items order in grid layouts
entry: .github/workflows/helper/pre-commit/check_grid_items_order.py
language: script
files: \.ui$

- id: check-translation-tag
name: Check newline characters in <translation> tag
entry: .github/workflows/helper/pre-commit/check_translation_tag.py
Expand Down

0 comments on commit c887a6f

Please sign in to comment.