pax_global_header00006660000000000000000000000064147174362460014530gustar00rootroot0000000000000052 comment=6288241bc5c6b917c7858c276ab2ef3312ce924e qstylizer-0.2.4/000077500000000000000000000000001471743624600136015ustar00rootroot00000000000000qstylizer-0.2.4/.editorconfig000066400000000000000000000002561471743624600162610ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf qstylizer-0.2.4/.gitignore000066400000000000000000000001011471743624600155610ustar00rootroot00000000000000*.pyc *~ .idea/ .cache .eggs *.egg-info build test_stylesheet.py qstylizer-0.2.4/.readthedocs.yml000066400000000000000000000001171471743624600166660ustar00rootroot00000000000000python: setup_py_install: true requirements_file: doc_requirements.txt qstylizer-0.2.4/.travis.yml000066400000000000000000000003071471743624600157120ustar00rootroot00000000000000language: python matrix: include: - python: 2.7 env: TOXENV=py27 - python: 3.5 env: TOXENV=py35 - python: 3.9 env: TOXENV=py39 install: pip install tox script: tox qstylizer-0.2.4/LICENSE.txt000066400000000000000000000020601471743624600154220ustar00rootroot00000000000000MIT License Copyright (c) 2018 Brett Lambright Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. qstylizer-0.2.4/README.rst000066400000000000000000000076561471743624600153060ustar00rootroot00000000000000********* qstylizer ********* .. image:: https://travis-ci.com/blambright/qstylizer.svg?branch=master :target: https://www.travis-ci.com/blambright/qstylizer .. image:: https://readthedocs.org/projects/qstylizer/badge/?version=latest :target: http://qstylizer.readthedocs.io/en/latest/?badge=latest ------------ Installation ------------ .. code-block:: bash pip install qstylizer ------------- Documentation ------------- `Read the Docs `_. ------------ Introduction ------------ *qstylizer* is a python package designed to help with the construction of PyQt/PySide stylesheets. .. code-block:: python import PyQt5.QtWidgets import qstylizer.style css = qstylizer.style.StyleSheet() css.setValues( backgroundColor="green", color="#F0F0F0", marginLeft="2px" ) css.QToolButton.setValues( border="1px transparent lightblue", borderRadius="3px", margin="1px", padding="3px" ) css.QToolButton.menuButton.pressed.setValues( border="1px solid #333333", padding="5px", backgroundColor="#333333" ) # The following is also valid and is equivalent to the statement above. css["QToolButton::menu-button:pressed"].setValues(**{ "border": "1px solid #333333", "padding": "5px", "background-color": "#333333", }) css.QCheckBox.disabled.backgroundColor.setValue("#797979") widget = PyQt5.QtWidgets.QWidget() widget.setStyleSheet(css.toString()) The stylesheet generated above looks like this when passed to setStyleSheet():: * { color: #F0F0F0; margin-left: 2px; background-color: green; } QToolButton { border-radius: 3px; padding: 3px; border: 1px transparent lightblue; margin: 1px; } QToolButton::menu-button:pressed { padding: 5px; border: 1px solid #333333; background-color: #333333; } QCheckBox:disabled { background-color: #797979; } The true power comes from parsing an existing stylesheet and tweaking individual property values. .. code-block:: python import qstylizer.parser css = qstylizer.parser.parse( """ QToolButton::menu-button:pressed { padding: 5px; border: 1px solid #333333; background-color: #333333; } QCheckBox:disabled { background-color: #797979; } """ ) css.QToolButton.menuButton.pressed.padding.setValue("10px") css.QCheckBox.disabled.backgroundColor.setValue("#222222") print(css.toString()) Output:: QToolButton::menu-button:pressed { padding: 10px; border: 1px solid #333333; background-color: #333333; } QCheckBox:disabled { background-color: #222222; } ------- License ------- MIT License Copyright (c) 2018 Brett Lambright Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. qstylizer-0.2.4/doc/000077500000000000000000000000001471743624600143465ustar00rootroot00000000000000qstylizer-0.2.4/doc/Makefile000066400000000000000000000011411471743624600160030ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx SPHINXPROJ = qstylizer SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)qstylizer-0.2.4/doc/api/000077500000000000000000000000001471743624600151175ustar00rootroot00000000000000qstylizer-0.2.4/doc/api/descriptor/000077500000000000000000000000001471743624600172755ustar00rootroot00000000000000qstylizer-0.2.4/doc/api/descriptor/index.rst000066400000000000000000000002261471743624600211360ustar00rootroot00000000000000==================== qstylizer.descriptor ==================== .. automodule:: qstylizer.descriptor .. toctree:: :maxdepth: 1 :glob: * qstylizer-0.2.4/doc/api/descriptor/prop.rst000066400000000000000000000002331471743624600210050ustar00rootroot00000000000000========================= qstylizer.descriptor.prop ========================= .. automodule:: qstylizer.descriptor.prop :members: :undoc-members: qstylizer-0.2.4/doc/api/descriptor/pseudoprop.rst000066400000000000000000000002631471743624600222300ustar00rootroot00000000000000=============================== qstylizer.descriptor.pseudoprop =============================== .. automodule:: qstylizer.descriptor.pseudoprop :members: :undoc-members: qstylizer-0.2.4/doc/api/descriptor/pseudostate.rst000066400000000000000000000002671471743624600223740ustar00rootroot00000000000000================================ qstylizer.descriptor.pseudostate ================================ .. automodule:: qstylizer.descriptor.pseudostate :members: :undoc-members: qstylizer-0.2.4/doc/api/descriptor/qclass.rst000066400000000000000000000002431471743624600213140ustar00rootroot00000000000000=========================== qstylizer.descriptor.qclass =========================== .. automodule:: qstylizer.descriptor.qclass :members: :undoc-members: qstylizer-0.2.4/doc/api/descriptor/stylerule.rst000066400000000000000000000002601471743624600220550ustar00rootroot00000000000000============================== qstylizer.descriptor.stylerule ============================== .. automodule:: qstylizer.descriptor.stylerule :members: :undoc-members: qstylizer-0.2.4/doc/api/descriptor/subcontrol.rst000066400000000000000000000002631471743624600222220ustar00rootroot00000000000000=============================== qstylizer.descriptor.subcontrol =============================== .. automodule:: qstylizer.descriptor.subcontrol :members: :undoc-members: qstylizer-0.2.4/doc/api/index.rst000066400000000000000000000002301471743624600167530ustar00rootroot00000000000000============= API Reference ============= *qstylizer* .. automodule:: qstylizer .. toctree:: :maxdepth: 1 :glob: descriptor/index * qstylizer-0.2.4/doc/api/parser.rst000066400000000000000000000001671471743624600171510ustar00rootroot00000000000000================ qstylizer.parser ================ .. automodule:: qstylizer.parser :members: :undoc-members: qstylizer-0.2.4/doc/api/style.rst000066400000000000000000000001631471743624600170110ustar00rootroot00000000000000=============== qstylizer.style =============== .. automodule:: qstylizer.style :members: :undoc-members: qstylizer-0.2.4/doc/conf.py000066400000000000000000000127411471743624600156520ustar00rootroot00000000000000# coding: utf-8 # # qstylizer documentation build configuration file, created by # sphinx-quickstart on Sat Nov 4 21:16:20 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.append( os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) ) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = "1.0" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named "sphinx.ext.*") or your custom # ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinxcontrib.autoprogram" ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = [".rst", ".md"] source_suffix = ".rst" # The master toctree document. master_doc = "index" # General information about the project. project = u"qstylizer" copyright = u"2017, Brett Lambright" author = u"Brett Lambright" # The version info for the project you"re documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. # version = u"0.1.0" # The full version, including alpha/beta/rc tags. # release = u"0.1.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = "qstylizerdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ("letterpaper" or "a4paper"). # # "papersize": "letterpaper", # The font size ("10pt", "11pt" or "12pt"). # # "pointsize": "10pt", # Additional stuff for the LaTeX preamble. # # "preamble": "", # Latex figure (float) alignment # # "figure_align": "htbp", } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, "qstylizer.tex", u"qstylizer Documentation", u"Brett Lambright", "manual"), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, "qstylizer", u"qstylizer Documentation", [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, "qstylizer", u"qstylizer Documentation", author, "qstylizer", "One line description of project.", "Miscellaneous"), ] # -- Autodoc ------------------------------------------------------------------ # autodoc_default_flags = ["members", "undoc-members", "inherited-members"] autodoc_member_order = "bysource" def autodoc_skip(app, what, name, obj, skip, options): """Don"t skip __init__ method for autodoc.""" if name == "__init__": return False return skip # -- Setup -------------------------------------------------------------------- def setup(app): app.connect("autodoc-skip-member", autodoc_skip) qstylizer-0.2.4/doc/index.rst000066400000000000000000000004271471743624600162120ustar00rootroot00000000000000qstylizer ========= qstylizer is a Qt stylesheet builder utility for PyQt4, PyQt5, PySide and PySide2. .. toctree:: :maxdepth: 2 :glob: introduction tutorial api/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` qstylizer-0.2.4/doc/introduction.rst000066400000000000000000000070411471743624600176230ustar00rootroot00000000000000Introduction ============ *qstylizer* is a python package designed to help with the construction of Qt stylesheets. The typical way of setting a stylesheet in PyQt is like this: .. code-block:: python widget = QtWidgets.QWidget() widget.setStyleSheet(""" QTabBar { border-radius: 3px; background-color: green; } QTabBar:focus { border: 0px transparent black; background-color: red; } QTabBar::close-button { background: transparent; } """) This approach floods the python code with ugly multi-line strings everywhere. The *qstylizer* way: .. code-block:: python import qstylizer.style css = qstylizer.style.StyleSheet() css.QTabBar.setValues( borderRadius="3px", backgroundColor="green" ) css.QTabBar.focus.setValues( border="0px transparent black", backgroundColor="red" ) css.QTabBar.closeButton.background.setValue("transparent") widget = QtWidgets.QMainWindow() widget.setStyleSheet(css.toString()) *qstylizer* stores style rule objects in a dictionary hierarchy. The above example maps to this hierarchy:: , "background-color": , "focus": , "background-color": , } /> "close-button": , }/> } /> } /> A StyleRule object is basically just an ordered dictionary with the keys as the style rule property names and the values as the style rule property values. Any attempt to get a dictionary value in the instance will add a key and value into the dictionary (the style rule property:values). If *qstylizer* incorrectly determines that close-button is a pseudostate instead of a subcontrol, the colons can be specified in the key: .. code-block:: python css.QTabBar["::close-button"].background.setValue("transparent") Because of the hierarchical nature, the following are all valid: .. code-block:: python css.QTabBar.closeButton.background.setValue("transparent") css["QTabBar"].closeButton.background.setValue("transparent") css["QTabBar"]["close-button"].background.setValue("transparent") css["QTabBar"]["close-button"]["background"].setValue("transparent") css["QTabBar"]["::close-button"]["background"].setValue("transparent") css["QTabBar::close-button"].background.setValue("transparent") css["QTabBar::close-button"]["background"].setValue("transparent") How Does it Work? +++++++++++++++++ How does *qstylizer* determine what is a QClass, subcontrol, or pseudostate? The package itself stores a list of known options for each type. .. code-block:: python >>> qstylizer.style.rule_class("QTabBar") >>> qstylizer.style.rule_class("close-button") >>> qstylizer.style.rule_class("hover") Advantages ++++++++++ What are the advantages? Ease of use and cleaner code. There is no need to worry about scope operators, brackets, and semi-colons. qstylizer-0.2.4/doc/make.bat000066400000000000000000000014031471743624600157510ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=python -msphinx ) set SOURCEDIR=. set BUILDDIR=_build set SPHINXPROJ=qstylizer if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The Sphinx module was not found. Make sure you have Sphinx installed, echo.then set the SPHINXBUILD environment variable to point to the full echo.path of the 'sphinx-build' executable. Alternatively you may add the echo.Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd qstylizer-0.2.4/doc/tutorial.rst000066400000000000000000000103541471743624600167460ustar00rootroot00000000000000Tutorial ======== Creating a StyleSheet +++++++++++++++++++++ To create a stylesheet, create a :class:`qstylizer.style.StyleSheet` instance. .. code-block:: python >>> import qstylizer.style >>> css = qstylizer.style.StyleSheet() To add global properties, simply start setting values for any attribute. .. code-block:: python >>> css.color.setValue("green") >>> css.border.setValue("1px solid red") >>> print(css.toString()) color: green; border: 1px solid red; Here is how to create a style rule for `QTabBar::close-button` and set the `background` property to `transparent`: .. code-block:: python >>> css.QTabBar.closeButton.background.setValue("transparent") >>> print(css.toString()) * { color: green; border: 1px solid red; } QTabBar::close-button { background: transparent; } There are actually multiple ways to set a property value. All of the following statements below are equivalent and valid. Take your pick. .. code-block:: python >>> css.QTabBar.closeButton.background.setValue("transparent") >>> css["QTabBar"].closeButton.background.setValue("transparent") >>> css["QTabBar"]["close-button"].background.setValue("transparent") >>> css["QTabBar"]["close-button"]["background"].setValue("transparent") >>> css["QTabBar"]["::close-button"]["background"].setValue("transparent") >>> css["QTabBar::close-button"].background.setValue("transparent") >>> css["QTabBar::close-button"]["background"].setValue("transparent") Global scope vs "* {}" ++++++++++++++++++++++ Adding a sub-style rule will result in a different syntax for the global property values: .. code-block:: python >>> css.color.setValue("green") >>> css.border.setValue("1px solid red") >>> print(css.toString()) color: green; border: 1px solid red; >>> css.QWidget.backgroundColor.setValue("blue") >>> print(css.toString()) * { color: green; border: 1px solid red; } QWidget { background-color: blue; } Unknown Property Names ++++++++++++++++++++++ Any name can be used as an attribute with the use of strings and brackets. .. code-block:: python >>> css = qstylizer.style.StyleSheet() >>> css["QUnknownClass::unknown-subcontrol"]["unknown-prop"].setValue("none") >>> print(css.toString()) QUnknownClass::unknown-subcontrol { unknown-prop: none; } Not Operator (!) ++++++++++++++++ Here is an example of how to use the `!` operator: .. code-block:: python css.QTabBar["!focus"].background.setValue("none") Object Property +++++++++++++++ Here is an example of how to set an object property style rule: .. code-block:: python css['QLineEdit[echoMode="2"]'].background.setValue("none") Parser ++++++ An existing stylesheet can be converted to a StyleSheet instance as a starting point. This is handy if you need to change property values in an existing template stylesheet. .. code-block:: python >>> import qstylizer.parser >>> stylesheet = """ ... QTabBar { ... border-radius: 3px; ... background-color: green; ... } ... QTabBar:focus { ... border: 0px transparent black; ... background-color: red; ... } ... QTabBar::close-button { ... background: transparent; ... } ... """ >>> css = qstylizer.parser.parse(stylesheet) >>> print(css.QTabBar.focus.toString()) QTabBar:focus { border: 0px transparent black; background-color: red; } String Output +++++++++++++ The :meth:`qstylizer.style.StyleRule.toString()` function call with no parameters will just output the property:values of that style rule in css format. The *qstylizer.style.StyleRule.toString(recursive=True)* function call will output the style rule and all of the sub-style rules in its hierarchy. .. code-block:: python >>> print(css.QTabBar.toString()) QTabBar { border-radius: 3px; background-color: green; } >>> print(css.QTabBar.toString(recursive=True)) QTabBar { border-radius: 3px; background-color: green; } QTabBar:focus { border: 0px transparent black; background-color: red; } QTabBar::close-button { background: transparent; } qstylizer-0.2.4/doc_requirements.txt000066400000000000000000000001451471743624600177120ustar00rootroot00000000000000sphinx >= 1.2.2, < 2 sphinx_rtd_theme >= 0.1.6, < 1 sphinxcontrib-autoprogram >= 0.1.2, !=0.1.3, < 1 qstylizer-0.2.4/pytest.ini000066400000000000000000000000321471743624600156250ustar00rootroot00000000000000[pytest] testpaths = test qstylizer-0.2.4/qstylizer/000077500000000000000000000000001471743624600156475ustar00rootroot00000000000000qstylizer-0.2.4/qstylizer/__init__.py000066400000000000000000000000201471743624600177500ustar00rootroot00000000000000# coding: utf-8 qstylizer-0.2.4/qstylizer/descriptor/000077500000000000000000000000001471743624600200255ustar00rootroot00000000000000qstylizer-0.2.4/qstylizer/descriptor/__init__.py000066400000000000000000000000201471743624600221260ustar00rootroot00000000000000# coding: utf-8 qstylizer-0.2.4/qstylizer/descriptor/prop.py000066400000000000000000000163331471743624600213650ustar00rootroot00000000000000# coding: utf-8 import copy import qstylizer.descriptor.stylerule PROPERTY_VALUES = { "absolute", "active", "alignment", "alternate-base", "always", "auto", "base", "bold", "bottom", "bright-text", "button", "button-text", "center", "circle", "dark", "dashed", "decimal", "disabled", "disc", "dot-dash", "dot-dot-dash", "dotted", "double", "fixed", "groove", "highlight", "highlighted-text", "icon-size", "inset", "italic", "large", "left", "light", "line-through", "link", "link-visited", "lower-alpha", "lower-roman", "lowercase", "medium", "mid", "middle", "midlight", "native", "no-repeat", "none", "normal", "nowrap", "oblique", "off", "on", "outset", "overline", "pre", "pre-wrap", "relative", "repeat", "repeat-x", "repeat-xy", "repeat-y", "ridge", "right", "round", "scroll", "selected", "shadow", "small", "small-caps", "solid", "square", "static", "stretch", "sub", "super", "text", "top", "transparent", "underline", "upper-alpha", "upper-roman", "uppercase", "wave", "window", "window-text", "x-large", "xx-large", } class PropDescriptor(qstylizer.descriptor.stylerule.StyleRuleDescriptor): """Property descriptor.""" @property def rule_cls(self): import qstylizer.style return qstylizer.style.PropRule class PropParent(qstylizer.descriptor.stylerule.StyleRuleParent): """Property setter. Contains descriptors for all known properties. """ _descriptor_cls = PropDescriptor qtBackgroundRole = _descriptor_cls("-qt-background-role") qtBlockIndent = _descriptor_cls("-qt-block-indent") qtLineHeightType = _descriptor_cls("-qt-line-height-type") qtListIndent = _descriptor_cls("-qt-list-indent") qtListNumberPrefix = _descriptor_cls("-qt-list-number-prefix") qtListNumberSuffix = _descriptor_cls("-qt-list-number-suffix") qtParagraphType = _descriptor_cls("-qt-paragraph-type") qtStyleFeatures = _descriptor_cls("-qt-style-features") qtTableType = _descriptor_cls("-qt-table-type") qtUserState = _descriptor_cls("-qt-user-state") alternateBackgroundColor = _descriptor_cls("alternate-background-color") alignment = _descriptor_cls("alignment") background = _descriptor_cls("background") backgroundAttachment = _descriptor_cls("background-attachment") backgroundClip = _descriptor_cls("background-clip") backgroundColor = _descriptor_cls("background-color") backgroundImage = _descriptor_cls("background-image") backgroundOrigin = _descriptor_cls("background-origin") backgroundPosition = _descriptor_cls("background-position") backgroundRepeat = _descriptor_cls("background-repeat") border = _descriptor_cls("border") borderBottom = _descriptor_cls("border-bottom") borderBottomColor = _descriptor_cls("border-bottom-color") borderBottomLeftRadius = _descriptor_cls("border-bottom-left-radius") borderBottomRightRadius = _descriptor_cls("border-bottom-right-radius") borderBottomStyle = _descriptor_cls("border-bottom-style") borderBottomWidth = _descriptor_cls("border-bottom-width") borderColor = _descriptor_cls("border-color") borderImage = _descriptor_cls("border-image") borderLeft = _descriptor_cls("border-left") borderLeftColor = _descriptor_cls("border-left-color") borderLeftStyle = _descriptor_cls("border-left-style") borderLeftWidth = _descriptor_cls("border-left-width") borderRadius = _descriptor_cls("border-radius") borderRight = _descriptor_cls("border-right") borderRightColor = _descriptor_cls("border-right-color") borderRightStyle = _descriptor_cls("border-right-style") borderRightWidth = _descriptor_cls("border-right-width") borderStyle = _descriptor_cls("border-style") borderTop = _descriptor_cls("border-top") borderTopColor = _descriptor_cls("border-top-color") borderTopLeftRadius = _descriptor_cls("border-top-left-radius") borderTopRightRadius = _descriptor_cls("border-top-right-radius") borderTopStyle = _descriptor_cls("border-top-style") borderTopWidth = _descriptor_cls("border-top-width") borderWidth = _descriptor_cls("border-width") color = _descriptor_cls("color") comboboxPopup = _descriptor_cls("combobox-popup") float = _descriptor_cls("float") font = _descriptor_cls("font") fontFamily = _descriptor_cls("font-family") fontSize = _descriptor_cls("font-size") fontStyle = _descriptor_cls("font-style") fontVariant = _descriptor_cls("font-variant") fontWeight = _descriptor_cls("font-weight") height = _descriptor_cls("height") iconSize = _descriptor_cls("icon-size") image = _descriptor_cls("image") imagePosition = _descriptor_cls("image-position") lineHeight = _descriptor_cls("line-height") listStyle = _descriptor_cls("list-style") listStyleType = _descriptor_cls("list-style-type") margin = _descriptor_cls("margin") marginBottom = _descriptor_cls("margin-bottom") marginLeft = _descriptor_cls("margin-left") marginRight = _descriptor_cls("margin-right") marginTop = _descriptor_cls("margin-top") maxHeight = _descriptor_cls("max-height") maxWidth = _descriptor_cls("max-width") minHeight = _descriptor_cls("min-height") minWidth = _descriptor_cls("min-width") outline = _descriptor_cls("outline") outlineBottomLeftRadius = _descriptor_cls("outline-bottom-left-radius") outlineBottomRightRadius = _descriptor_cls("outline-bottom-right-radius") outlineColor = _descriptor_cls("outline-color") outlineOffset = _descriptor_cls("outline-offset") outlineRadius = _descriptor_cls("outline-radius") outlineStyle = _descriptor_cls("outline-style") outlineTopLeftRadius = _descriptor_cls("outline-top-left-radius") outlineTopRightRadius = _descriptor_cls("outline-top-right-radius") outlineWidth = _descriptor_cls("outline-width") padding = _descriptor_cls("padding") paddingBottom = _descriptor_cls("padding-bottom") paddingLeft = _descriptor_cls("padding-left") paddingRight = _descriptor_cls("padding-right") paddingTop = _descriptor_cls("padding-top") pageBreakAfter = _descriptor_cls("page-break-after") pageBreakBefore = _descriptor_cls("page-break-before") position = _descriptor_cls("position") selectionBackgroundColor = _descriptor_cls("selection-background-color") selectionColor = _descriptor_cls("selection-color") showDecorationSelected = _descriptor_cls("show-decoration-selected") spacing = _descriptor_cls("spacing") subcontrolOrigin = _descriptor_cls("subcontrol-origin") subcontrolPosition = _descriptor_cls("subcontrol-position") textAlign = _descriptor_cls("text-align") textDecoration = _descriptor_cls("text-decoration") textIndent = _descriptor_cls("text-indent") textTransform = _descriptor_cls("text-transform") textUnderlineStyle = _descriptor_cls("text-underline-style") verticalAlign = _descriptor_cls("vertical-align") whiteSpace = _descriptor_cls("white-space") width = _descriptor_cls("width") qstylizer-0.2.4/qstylizer/descriptor/pseudoprop.py000066400000000000000000000012141471743624600225750ustar00rootroot00000000000000# coding: utf-8 import qstylizer.descriptor.stylerule class PseudoPropDescriptor(qstylizer.descriptor.stylerule.StyleRuleDescriptor): """Pseudo-property descriptor.""" @property def rule_cls(self): import qstylizer.style return qstylizer.style.PseudoPropRule class PseudoPropParent(qstylizer.descriptor.stylerule.StyleRuleParent): """Pseudo-property setter. Contains descriptors for all known pseudo-properties. """ _descriptor_cls = PseudoPropDescriptor left = _descriptor_cls("left") right = _descriptor_cls("right") top = _descriptor_cls("top") bottom = _descriptor_cls("bottom") qstylizer-0.2.4/qstylizer/descriptor/pseudostate.py000066400000000000000000000042461471743624600227450ustar00rootroot00000000000000# coding: utf-8 import qstylizer.descriptor.stylerule class PseudoStateDescriptor(qstylizer.descriptor.stylerule.StyleRuleDescriptor): """Pseudo-state descriptor.""" @property def rule_cls(self): import qstylizer.style return qstylizer.style.PseudoStateRule class PseudoStateParent(qstylizer.descriptor.stylerule.StyleRuleParent): """Pseudostate setter. Contains descriptors for all known pseudostates. """ _descriptor_cls = PseudoStateDescriptor active = _descriptor_cls("active") adjoinsItem = _descriptor_cls("adjoins-item") alternate = _descriptor_cls("alternate") checked = _descriptor_cls("checked") closable = _descriptor_cls("closable") closed = _descriptor_cls("closed") default = _descriptor_cls("default") disabled = _descriptor_cls("disabled") editFocus = _descriptor_cls("edit-focus") editable = _descriptor_cls("editable") enabled = _descriptor_cls("enabled") exclusive = _descriptor_cls("exclusive") first = _descriptor_cls("first") flat = _descriptor_cls("flat") floatable = _descriptor_cls("floatable") focus = _descriptor_cls("focus") hasChildren = _descriptor_cls("has-children") hasSiblings = _descriptor_cls("has-siblings") horizontal = _descriptor_cls("horizontal") hover = _descriptor_cls("hover") indeterminate = _descriptor_cls("indeterminate") last = _descriptor_cls("last") maximized = _descriptor_cls("maximized") middle = _descriptor_cls("middle") minimized = _descriptor_cls("minimized") movable = _descriptor_cls("movable") nextSelected = _descriptor_cls("next-selected") noFrame = _descriptor_cls("no-frame") nonExclusive = _descriptor_cls("non-exclusive") off = _descriptor_cls("off") on = _descriptor_cls("on") onlyOne = _descriptor_cls("only-one") open = _descriptor_cls("open") pressed = _descriptor_cls("pressed") previousSelected = _descriptor_cls("previous-selected") readOnly = _descriptor_cls("read-only") selected = _descriptor_cls("selected") unchecked = _descriptor_cls("unchecked") vertical = _descriptor_cls("vertical") window = _descriptor_cls("window") qstylizer-0.2.4/qstylizer/descriptor/qclass.py000066400000000000000000000051141471743624600216660ustar00rootroot00000000000000# coding: utf-8 import qstylizer.descriptor.stylerule class ClassStyleDescriptor(qstylizer.descriptor.stylerule.StyleRuleDescriptor): """QClass descriptor.""" @property def rule_cls(self): import qstylizer.style return qstylizer.style.ClassRule class ClassStyleParent(qstylizer.descriptor.stylerule.StyleRuleParent): """QClass setter. Contains descriptors for all known QClasses. """ _descriptor_cls = ClassStyleDescriptor QAbstractScrollArea = _descriptor_cls("QAbstractScrollArea") QCheckBox = _descriptor_cls("QCheckBox") QColumnView = _descriptor_cls("QColumnView") QComboBox = _descriptor_cls("QComboBox") QDateEdit = _descriptor_cls("QDateEdit") QDateTimeEdit = _descriptor_cls("QDateTimeEdit") QDialog = _descriptor_cls("QDialog") QDialogButtonBox = _descriptor_cls("QDialogButtonBox") QDockWidget = _descriptor_cls("QDockWidget") QDoubleSpinBox = _descriptor_cls("QDoubleSpinBox") QFrame = _descriptor_cls("QFrame") QGroupBox = _descriptor_cls("QGroupBox") QHeaderView = _descriptor_cls("QHeaderView") QLabel = _descriptor_cls("QLabel") QLineEdit = _descriptor_cls("QLineEdit") QListView = _descriptor_cls("QListView") QListWidget = _descriptor_cls("QListWidget") QMainWindow = _descriptor_cls("QMainWindow") QMenu = _descriptor_cls("QMenu") QMenuBar = _descriptor_cls("QMenuBar") QMessageBox = _descriptor_cls("QMessageBox") QPlainTextEdit = _descriptor_cls("QPlainTextEdit") QProgressBar = _descriptor_cls("QProgressBar") QPushButton = _descriptor_cls("QPushButton") QRadioButton = _descriptor_cls("QRadioButton") QScrollArea = _descriptor_cls("QScrollArea") QScrollBar = _descriptor_cls("QScrollBar") QSizeGrip = _descriptor_cls("QSizeGrip") QSlider = _descriptor_cls("QSlider") QSpinBox = _descriptor_cls("QSpinBox") QSplitter = _descriptor_cls("QSplitter") QStatusBar = _descriptor_cls("QStatusBar") QTabBar = _descriptor_cls("QTabBar") QTabWidget = _descriptor_cls("QTabWidget") QTableView = _descriptor_cls("QTableView") QTableWidget = _descriptor_cls("QTableWidget") QTextBrowser = _descriptor_cls("QTextBrowser") QTextEdit = _descriptor_cls("QTextEdit") QTimeEdit = _descriptor_cls("QTimeEdit") QToolBar = _descriptor_cls("QToolBar") QToolButton = _descriptor_cls("QToolButton") QToolBox = _descriptor_cls("QToolBox") QToolTip = _descriptor_cls("QToolTip") QTreeView = _descriptor_cls("QTreeView") QTreeWidget = _descriptor_cls("QTreeWidget") QWidget = _descriptor_cls("QWidget") qstylizer-0.2.4/qstylizer/descriptor/stylerule.py000066400000000000000000000062121471743624600224300ustar00rootroot00000000000000# coding: utf-8 import copy class StyleRuleDescriptor(object): """StyleRule descriptor.""" def __init__(self, name): """Initialize the StyleRuleDescriptor instance. :param name: The attribute name of type string """ self.name = name def __get__(self, instance, *args, **kwargs): """Get the value from the StyleRule's ordered dict. If value doesn't exist, create a new StyleRule instance and add it to the StyleRule's ordered dict. :param instance: The StyleRule instance """ import qstylizer.style assert isinstance(instance, qstylizer.style.StyleRule) if instance.find_child_rule(self.name) is None: new_style = self.rule_cls( name=self.name, parent=instance, ) instance.set_child_rule(self.name, new_style) return instance.find_child_rule(self.name) def __set__(self, instance, value): """Set the value in the StyleRule's ordered dict. If the value is a StyleRule, simply add it to the ordered dict. Otherwise create a new StyleRule instance, set its value attribute to the value, and add it to the ordered dict. :param instance: The StyleRule instance :param value: The value to set in StyleRule instance """ if isinstance(value, self.rule_cls): value = copy.deepcopy(value) value._parent = instance instance.set_child_rule(self.name, value) else: new_style = self.rule_cls( name=self.name, parent=instance, ) new_style.setValue(value) instance.set_child_rule(self.name, new_style) @property def rule_cls(self): import qstylizer.style return qstylizer.style.StyleRule class StyleRuleParent(object): """StyleRule descriptor. Contains functions for getting all known attributes of the StyleRule. """ _descriptor_cls = StyleRuleDescriptor @classmethod def get_attributes(cls): """Get all of the settable attributes of the StyleRule. Loop through all base classes to gather all known attributes. Returns a dictionary with the attribute name as the key and descriptor as the value. """ attributes = {} for class_ in cls.__bases__: if not issubclass(class_, StyleRuleParent): continue attributes.update({ key: value for key, value in class_.__dict__.items() if isinstance(value, class_._descriptor_cls) }) attributes.update(class_.get_attributes()) attributes.update({ key: value for key, value in cls.__dict__.items() if isinstance(value, cls._descriptor_cls) }) return attributes @classmethod def get_attr_options(cls): """Get all of the attribute names of the StyleRule. Returns a set of all possible dashcase attribute names. """ return set( [value.name for value in cls.get_attributes().values()] ) qstylizer-0.2.4/qstylizer/descriptor/subcontrol.py000066400000000000000000000037411471743624600225760ustar00rootroot00000000000000# coding: utf-8 import qstylizer.descriptor.stylerule class SubControlDescriptor(qstylizer.descriptor.stylerule.StyleRuleDescriptor): """Subcontrol descriptor.""" @property def rule_cls(self): import qstylizer.style return qstylizer.style.SubControlRule class SubControlParent(qstylizer.descriptor.stylerule.StyleRuleParent): """Subcontrol setter. Contains descriptors for all known subcontrols. """ _descriptor_cls = SubControlDescriptor addLine = _descriptor_cls("add-line") addPage = _descriptor_cls("add-page") branch = _descriptor_cls("branch") chunk = _descriptor_cls("chunk") closeButton = _descriptor_cls("close-button") corner = _descriptor_cls("corner") downArrow = _descriptor_cls("down-arrow") downButton = _descriptor_cls("down-button") dropDown = _descriptor_cls("drop-down") floatButton = _descriptor_cls("float-button") groove = _descriptor_cls("groove") indicator = _descriptor_cls("indicator") handle = _descriptor_cls("handle") icon = _descriptor_cls("icon") item = _descriptor_cls("item") leftArrow = _descriptor_cls("left-arrow") leftCorner = _descriptor_cls("left-corner") menuArrow = _descriptor_cls("menu-arrow") menuButton = _descriptor_cls("menu-button") menuIndicator = _descriptor_cls("menu-indicator") rightArrow = _descriptor_cls("right-arrow") pane = _descriptor_cls("pane") rightCorner = _descriptor_cls("right-corner") scroller = _descriptor_cls("scroller") section = _descriptor_cls("section") separator = _descriptor_cls("separator") subLine = _descriptor_cls("sub-line") subPage = _descriptor_cls("sub-page") tab = _descriptor_cls("tab") tabBar = _descriptor_cls("tab-bar") tear = _descriptor_cls("tear") tearoff = _descriptor_cls("tearoff") text = _descriptor_cls("text") title = _descriptor_cls("title") upArrow = _descriptor_cls("up-arrow") upButton = _descriptor_cls("up-button") qstylizer-0.2.4/qstylizer/parser.py000066400000000000000000000017031471743624600175160ustar00rootroot00000000000000# coding: utf-8 import tinycss2 import qstylizer.style def parse(stylesheet): """Parse a stylesheet using tinycss2 and return a StyleSheet instance. :param stylesheet: A string of an existing stylesheet. """ parsed_stylesheet = tinycss2.parse_stylesheet( stylesheet, skip_comments=True, skip_whitespace=True ) css = qstylizer.style.StyleSheet() for node in parsed_stylesheet: if node.type == "error": raise ValueError("Cannot parse Stylesheet: " + node.message) selector = tinycss2.serialize(node.prelude).strip() declaration_list = tinycss2.parse_declaration_list( node.content, skip_comments=True, skip_whitespace=True ) for declaration in declaration_list: if declaration.type == "declaration": prop = declaration.name.strip() css[selector][prop] = tinycss2.serialize(declaration.value).strip() return css qstylizer-0.2.4/qstylizer/style.py000066400000000000000000000533701471743624600173710ustar00rootroot00000000000000# coding: utf-8 import re import copy import collections import inflection import qstylizer.descriptor.prop import qstylizer.descriptor.subcontrol import qstylizer.descriptor.pseudostate import qstylizer.descriptor.pseudoprop import qstylizer.descriptor.qclass import qstylizer.descriptor.stylerule QPROPERTIES = qstylizer.descriptor.prop.PropParent.get_attr_options() QSUBCONTROLS = qstylizer.descriptor.subcontrol.SubControlParent.get_attr_options() QPSEUDOSTATES = qstylizer.descriptor.pseudostate.PseudoStateParent.get_attr_options() QPSEUDOPROPS = qstylizer.descriptor.pseudoprop.PseudoPropParent.get_attr_options() QCLASSES = qstylizer.descriptor.qclass.ClassStyleParent.get_attr_options() class StyleRule( collections.OrderedDict, qstylizer.descriptor.prop.PropParent, qstylizer.descriptor.pseudoprop.PseudoPropParent ): """StyleRule Object. A dictionary containing nested Styles and property:value pairs. Example structure:: , "background-color": , "indicator": , "hover": , "border": } /> } /> } /> Output format:: { : ; : ; ... } Stylesheet output:: QCheckBox { color: red; background-color: black; } QCheckBox::indicator { border: 1px solid green; } QCheckBox::indicator:hover { background-color: green; border: 0px transparent black; } """ _split_regex = r"""\*|\[[A-Za-z0-9='"_:]+\]|\W*\w*""" @classmethod def split_selector(cls, selector): """Split the selector based on the _split_regex. Return a list of each component. Example:: name = "QObject::subcontrol:pseudostate" return value = ["QObject", "::subcontrol", ":pseudostate"] :param name: String name """ selector = selector.replace("-", "_") return re.findall(cls._split_regex, selector)[:-1] def __init__(self, name=None, value=None, parent=None): """Initialize the StyleRule dictionary. .. note:: All public variables will be put into ordered dictionary. :param name: The name of the StyleRule :param value: The property value :param parent: The parent StyleRule """ super(StyleRule, self).__init__() self._name = self._sanitize_key(name) if name else None self._parent = parent self._attributes = self.get_attributes() self._attr_options = self.get_attr_options() self._value = self._sanitize_value(value) self._child_rules = collections.OrderedDict() @staticmethod def _sanitize_key(key): """Strip the key of colons and replace underscores with dashes. :param key: A string variable """ key = str(key) if ( key and key[0] not in ["Q", "#", "[", " "] and key != inflection.camelize(key) and not key.startswith("qproperty-") ): key = inflection.underscore(key) if key and key[0] != "[": key = key.replace("not_", "!").replace(":", "").replace("_", "-") return key @staticmethod def _sanitize_value(value): """Strip the value of any semi-colons. :param value: A value of any type """ try: if type(value) in [str, unicode]: return value.replace(";", "") except NameError: if type(value) in [str, bytes]: return value.replace(";", "") return value def find_or_create_child_rule(self, name): """Find or create a child rule from a string key. If the key rule already exists, return the rule. If there is a comma in requested key, return a StyleRuleList object. Otherwise create rules from the style rule names in the key and return the top level rule or property. :param name: The dictionary key """ value = self.find_child_rule(name) if value is not None: return value if "," in name: rule_list = self.create_child_rule_list(name) return rule_list return self.create_child_rules(name) def find_child_rule(self, key): """Find rule from key. Return the sanitized key's hash value in the ordered dict. """ key = self._sanitize_key(key) return self.get(key) def create_child_rule_list(self, name): """Create a StyleRuleList object and add it to ordered dict. :param name: String name """ rule_list = StyleRuleList(name=name, parent=self) self.set_child_rule(name, rule_list) return rule_list def create_child_rules(self, selector): """Create child rules from selector string. Split the selector into individual components based on the _split_regex and recursively build the StyleRule hierarchy looping through the components. If selector is "QClass::subcontrol:pseudostate", curr_name is "QClass" and remaining is "::subcontrol:pseudostate" :param name: String to split """ curr_name = self.split_selector(selector)[0] remaining = selector.split(curr_name, 1)[-1].replace("-", "_") rule = self.find_child_rule(curr_name) if rule is None: rule = self.create_child_rule(curr_name) if remaining and remaining != curr_name: return rule.find_or_create_child_rule(remaining) return rule def create_child_rule(self, name): """Create child rule from name. Determine subclass from name, create an instance of the subclass, then add it to ordered dict. :param name: String name """ class_ = rule_class(name) rule = class_(name=name, parent=self) self.set_child_rule(name, rule) return rule def set_child_rule(self, key, value, **kwargs): """Set rule in ordered dictionary.""" key = self._sanitize_key(key) if not isinstance(value, StyleRule): value = self._sanitize_value(value) value = PropRule(name=key, value=value, parent=self) self._add_child_rule(value) return super(StyleRule, self).__setitem__(key, value, **kwargs) def _add_child_rule(self, rule): """Add a rule to the _child_rules dictionary. :param rule: A StyleRule object. """ if rule.selector not in self._child_rules: self._child_rules[rule.selector] = rule if self._parent is not None: self._parent._add_child_rule(rule) @property def selector(self): """Get the selector. Example:: Object::subcontrol:pseudostate """ if self._parent is None: return self.name if self.name else "" return self._parent.selector + self.scope_operator + self.name @property def name(self): """Return the name of the StyleRule (eg. "QCheckBox"). Strip off the scope operator if it exists in name. """ if (self._name and self.scope_operator and self._name.startswith(self.scope_operator)): return self._name.split(self.scope_operator, 1)[-1] return self._name @name.setter def name(self, name): self._name = self._sanitize_key(name) @property def parent(self): return self._parent @property def scope_operator(self): """Get the scope operator. The scope operator is the "::" or ":" that is printed in front of the name of the StyleRule in the selector. Subclasses are expected to define the scope operator or else it will try to guess it based on its position in the hierarchy. """ if self.is_top_level(): return "" elif self.is_leaf(): return ":" return "::" def is_leaf(self): """Determine if StyleRule is a leaf. StyleRule is a leaf its child rules dictionary contains only PropRules. """ for rule in self._child_rules.values(): if not isinstance(rule, PropRule): return False return True def is_top_level(self): """Determine if StyleRule is top level. StyleRule is top level if its parent is of the StyleSheet class. """ return isinstance(self._parent, StyleSheet) def _to_string_recursive(self): """Convert all child rules into a single stirng in css format. Loop through all of the rules and generate a stylesheet string. """ stylesheet = self.toString(recursive=False) for rule in self._child_rules.values(): stylesheet += rule.toString(recursive=False) return stylesheet def _to_string(self, recursive=False): """Convert to a single string in css format. :param recursive: Output all of the sub-style rules. """ if recursive: return self._to_string_recursive() rule_template = "{selector} {{\n{properties}}}\n" prop_template = " {}: {};\n" properties = "" sheet = "" selector = self.selector for key, rule in self.items(): if rule.value is not None: properties += prop_template.format(key, rule.value) if properties: sheet = rule_template.format(**locals()) return sheet def toString(self, *args, **kwargs): """Convert to a single string in css format. Use camelcase for function name to match PyQt/PySide. """ return self._to_string(*args, **kwargs) def _set_values(self, *args, **kwargs): """Set property values in the style rule.""" for key, value in kwargs.items(): if "-" in key: self.__getitem__(key).setValue(value) else: self.__getattribute__(key).setValue(value) def update(self, *args, **kwargs): if isinstance(args[0], StyleRule): for key, child_rule in args[0]._child_rules.items(): rule = self.find_or_create_child_rule(key) for k, v in child_rule.items(): if isinstance(v, StyleRule): v = copy.deepcopy(v) v._parent = rule rule[k] = v def setValues(self, *args, **kwargs): """Set property values in the style rule. Use camelcase for function name to match PyQt/PySide. """ self._set_values(*args, **kwargs) def _set_value(self, value): """Set property value.""" self._value = self._sanitize_value(value) def setValue(self, value): """Set property value. Use camelcase for function name to match PyQt/PySide. """ self._set_value(value) @property def value(self): return self._value def __getitem__(self, key): """Override the retrieving of a value from dictionary. Find or create rule in the key's hash location. :param key: The dictionary key """ return self.find_or_create_child_rule(key) def __delattr__(self, name): """Override the deletion of an attribute. If attribute starts with an underscore, delete the attribute from the object's __dict__ otherwise delete it from the ordered dict. :param name: String name of attribute to delete """ if name in self.__dict__: return super(StyleRule, self).__delattr__(name) return self.__delitem__(name) def __setattr__(self, name, val): """Override the setting of an attribute. If name is in the pre-defined attributes, call the attribute's descriptor's __set__ function. Otherwise, add it to ordered dict as-is. :param name: The attribute name :param val: The value to set """ if name.startswith("_"): return super(StyleRule, self).__setattr__(name, val) elif name in self._attributes: return self._attributes[name].__set__(self, val) return self.set_child_rule(name, val) def __setitem__(self, key, value, **kwargs): """Override the setting of an attribute in ordered dict. If key is in pre-defined attributes, call attribute's descriptor's __set__ function. Otherwise add the value to ordered dict as-is. :param key: The hash key of the ordered dict :param value: The value to map to hash key """ if key in self._attr_options: if "-" in key: key = key.replace("-", "_") key = inflection.camelize(key) key = key[0].lower() + key[1:] try: return self._attributes[key].__set__(self, value) except KeyError: pass return self.set_child_rule(key, value, **kwargs) def __deepcopy__(self, memo): """Override deepcopy. Make a deepcopy of all member attributes as well as all rule rules in ordered dictionary. """ cls = self.__class__ result = cls.__new__(cls) result._name = self._name result._value = self._value result._parent = None result._child_rules = collections.OrderedDict() memo[id(self)] = result for k, v in self.__dict__.items(): if k in ("_child_rules",): continue setattr(result, k, copy.deepcopy(v, memo)) result.clear() for k, v in self.items(): if isinstance(v, StyleRule): v = copy.deepcopy(v, memo) v._parent = result result.set_child_rule(k, v) result._parent = self._parent return result def __repr__(self, *args, **kwargs): """Set the representation to look like xml syntax.""" value_attr = "" name_attr = "" if self.value is not None: value_attr = "value={0!r} ".format(self.value) if self.name is not None: name_attr = "name={0!r} ".format(self.name) return "<{0} {1}{2}/>".format( self.__class__.__name__, name_attr, value_attr ) def __str__(self): """Call toString if StyleRule is cast to string.""" return self.toString() class StyleSheet(StyleRule, qstylizer.descriptor.qclass.ClassStyleParent): """The StyleSheet definition. Contains descriptors for all class and property options. """ def is_global_scope(self): """Determine if stylesheet is global scope. A StyleSheet is global scope if it has no rules. Resulting string should contain no brackets. :: background-color: red; border: none; """ return self.is_leaf() def _to_string_recursive(self): """Convert all child rules into a single stirng in css format. Loop through all of the rules and generate a stylesheet string. """ stylesheet = self.toString(recursive=False) for key, rule in self._child_rules.items(): if key == "*": continue stylesheet += rule.toString(recursive=False) return stylesheet def _to_string(self, recursive=True): """Return the selector and properties as a single string. :param recursive: Loop through all rules to generate a stylesheet. """ if recursive: return self._to_string_recursive() rule_template = "{selector} {{\n{properties}}}\n" prop_template = " {}: {};\n" selector = self.selector if self.is_global_scope(): rule_template = "{properties}" prop_template = "{}: {};\n" else: selector = "*" properties = "" sheet = "" for key, value in self.items(): # Output the "*" property values if applicable. if key == "*": for global_key, global_value in self.__getitem__("*").items(): if not isinstance(global_value, StyleRule): properties += prop_template.format( global_key, global_value ) elif global_value.value is not None: properties += prop_template.format( global_key, global_value.value ) if not isinstance(value, StyleRule): properties += prop_template.format(key, value) elif value.value is not None: properties += prop_template.format(key, value.value) if properties: sheet = rule_template.format(**locals()) return sheet @property def name(self): """Return the name of the StyleSheet.""" return self._name class ClassRule( StyleRule, qstylizer.descriptor.subcontrol.SubControlParent, qstylizer.descriptor.pseudostate.PseudoStateParent ): """The ClassRule definition. Example class rule name: "QCheckBox". Contains descriptors for all subcontrols and pseudostates. """ class ObjectRule(ClassRule): """The ObjectRule definition. Example object rule name: "#objectName". Inherits from ClassRule. Only difference is "#" is the scope operator. """ @property def scope_operator(self): return "#" class ChildClassRule(ClassRule): """The ChildClassRule definition. Example object rule name: " QFrame". Inherits from ClassRule. :: QWidget QFrame { property: value } """ @property def scope_operator(self): return " " class ObjectPropRule(ClassRule): """The ObjectPropRule definition. Example object property rule name: "[echoMode="2"]". """ @property def scope_operator(self): return "" class StyleRuleList(StyleRule): """The StyleRuleList definition. Example rule list name: "QCheckBox, QComboBox". """ @staticmethod def _sanitize_key(key): """Strip the key of newlines only.""" return str(key).replace("\n", "") def _create_child_rules_in_parent(self, name, val): """Find or create value in parent StyleRule Will loop through all components in name separated by a comma and set the property in each of the rules in the parent StyleRule. :param name: The attribute name :param val: The value """ rule_names = self.name.split(",") for rule_name in rule_names: self._parent.find_or_create_child_rule(rule_name).__setattr__( name, val ) return None @property def scope_operator(self): return "" def __setattr__(self, name, val): """Override the setting of an attribute. :param name: The attribute name :param val: The value to set """ if name.startswith("_"): return super(StyleRule, self).__setattr__(name, val) return self._create_child_rules_in_parent(name, val) def __setitem__(self, key, value, **kwargs): """Override the setting of a value in ordered dict. :param key: The hash key of the ordered dict :param value: The value to map to hash key """ return self._create_child_rules_in_parent(key, value) @property def name(self): """Return the name with no spaces.""" return self._name.replace(" ", "") class SubControlRule(StyleRule, qstylizer.descriptor.pseudostate.PseudoStateParent): """The SubControlRule definition. Example subcontrol name: "::indicator". """ @property def scope_operator(self): return "::" class PseudoStateRule(SubControlRule): """The PseudoStateRule definition. Example pseudostate name: ":hover". """ @property def scope_operator(self): return ":" class PseudoPropRule(PseudoStateRule): """The PseudoPropRule definition. The PseudoPropRule covers PseudoStates and properties that have the same name like "top", "bottom", "left", and "right". It is basically a PseudoStateRule that also stores a property value. In the following example, *top* is the PseudoPropRule. .. code-block:: python >>> css.QWidget.tab.top = "0" >>> css.QWidget.tab.top.color = "green" >>> print(css.toString()) QWidget::tab { top: 0; } QWidget::tab:top { color: green; } """ class PropRule(StyleRule): """The PropRule definition. Example prop rule name: "background-color". """ def rule_class(name): """Determine StyleRule subclass from string name. :param name: name of type string """ if "!" in name: name = name.replace("!", "") name = name[0].lower() + name[1:] class_ = StyleRule if name.startswith("::") or name in QSUBCONTROLS: class_ = SubControlRule elif name.startswith(":") or name in QPSEUDOSTATES: class_ = PseudoStateRule elif name.startswith("#"): class_ = ObjectRule elif name.startswith(" "): class_ = ChildClassRule elif name in QCLASSES or name.startswith("Q"): class_ = ClassRule elif "=" in name: class_ = ObjectPropRule return class_ qstylizer-0.2.4/requirements.txt000066400000000000000000000000551471743624600170650ustar00rootroot00000000000000tinycss2 >= 0.5, < 2 inflection > 0.3.0, < 1 qstylizer-0.2.4/setup.cfg000066400000000000000000000023241471743624600154230ustar00rootroot00000000000000[metadata] name = qstylizer author = Brett Lambright summary = Stylesheet Generator for PyQt{4-5}/PySide{1-2} description_file = README.rst license = MIT home_page = https://github.com/blambright/qstylizer classifier = Development Status :: 4 - Beta License :: OSI Approved :: MIT License Intended Audience :: Developers Operating System :: OS Independent Programming Language :: Python Environment :: X11 Applications :: Qt Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.9 [pbr] skip_changelog = True skip_authors = True [files] packages = qstylizer [extras] doc = sphinx >= 1.2.2, < 2 sphinx_rtd_theme >= 0.1.6, < 1 sphinxcontrib-autoprogram >= 0.1.2, !=0.1.3, < 1 test = pytest >= 6, < 8 pytest-mock >= 3, < 4 pytest-catchlog >= 1, < 2 test27 = pytest >= 4, < 5 pytest-mock >= 2, < 3 pytest-catchlog >= 1, < 2 [aliases] test = pytest [build_sphinx] config-dir = doc source-dir = doc build-dir = build/doc builder = html all_files = 1 [bdist_wheel] universal = 1 qstylizer-0.2.4/setup.py000066400000000000000000000001471471743624600153150ustar00rootroot00000000000000#!/usr/bin/env python from setuptools import setup setup( setup_requires=['pbr'], pbr=True, )qstylizer-0.2.4/test/000077500000000000000000000000001471743624600145605ustar00rootroot00000000000000qstylizer-0.2.4/test/conftest.py000066400000000000000000000003441471743624600167600ustar00rootroot00000000000000# coding: utf-8 import pytest @pytest.fixture def css(): import qstylizer.style return qstylizer.style.StyleSheet() @pytest.fixture def style_class(): import qstylizer.style return qstylizer.style.StyleRule qstylizer-0.2.4/test/integration/000077500000000000000000000000001471743624600171035ustar00rootroot00000000000000qstylizer-0.2.4/test/integration/test_integration.py000066400000000000000000000264321471743624600230460ustar00rootroot00000000000000# coding: utf-8 import pytest import textwrap def test_style(css): assert css._parent is None assert css.QWidget == css.QWidget.item._parent def test_style_recursive(css): css["test"].setValue(3) assert css["test"].value == 3 css.QWidget.color.setValue("green") assert css.QWidget.color.value == "green" assert css["QWidget"]["color"].value == "green" css["QWidget"]["color"].setValue("blue") assert css["QWidget"]["color"].value == "blue" assert css.QWidget.color.value == "blue" def test_delete_style(css): css.QWidget.color.setValue("blue") assert "color" in css.QWidget.keys() del css.QWidget.color assert "color" not in css.QWidget.keys() css.QWidget["color"].setValue("blue") assert "color" in css.QWidget.keys() del css.QWidget["color"] assert "color" not in css.QWidget.keys() def test_subcontrol(css): import qstylizer.style assert type(css.QWidget.item) == qstylizer.style.SubControlRule def test_pseudo_state(css): import qstylizer.style assert type(css.QWidget.item.selected) == qstylizer.style.PseudoStateRule def test_selector(css): assert css.QCheckBox.indicator.selector == "QCheckBox::indicator" assert css.QCheckBox.indicator.unchecked.selector == "QCheckBox::indicator:unchecked" assert css.QCheckBox.indicator.unchecked.hover.selector == "QCheckBox::indicator:unchecked:hover" assert css["Object"]["subcontrol"]["pseudostate"].selector == "Object::subcontrol:pseudostate" assert css["Object::subcontrol:pseudostate"].selector == "Object::subcontrol:pseudostate" assert css.QLineEdit['[echoMode="2"]'].selector == "QLineEdit[echoMode=\"2\"]" assert css["Object"]["::subcontrol"][":pseudostate"].selector == "Object::subcontrol:pseudostate" assert css["QWidget#objectName"].selector == "QWidget#objectName" def test_style_list(css): css["QCheckBox, QLineEdit, QFrame"].border = "none" css["QWidget,#objectName"].border = "none" css["*,\nQCheckBox::subcontrol:pseudostate"].margin = "none" assert "QCheckBox" in css.keys() assert "QLineEdit" in css.keys() assert "QWidget" in css.keys() assert "#objectName" in css.keys() assert "border" in css.QWidget.keys() assert "border" in css.QLineEdit.keys() assert "margin" in css["QCheckBox::subcontrol:pseudostate"].keys() assert css.toString() == textwrap.dedent( """ * { margin: none; } QCheckBox { border: none; } QLineEdit { border: none; } QFrame { border: none; } QWidget { border: none; } #objectName { border: none; } QCheckBox::subcontrol:pseudostate { margin: none; } """ )[1:] def test_style_style(css): css.QCheckBox.indicator.unchecked.hover.border.setValue("none") css.QCheckBox.indicator.unchecked.hover.color.setValue("green") assert css.QCheckBox.indicator.unchecked.hover.toString() == \ "QCheckBox::indicator:unchecked:hover {\n border: none;\n color: green;\n}\n" def test_to_string_recursive(css): css.border.setValue("none") css.QFrame.border.setValue("1px solid green") css.QFrame.color.setValue("green") css.QCheckBox.border.setValue("1px solid green") css.QCheckBox.color.setValue("green") css.QCheckBox.indicator.backgroundColor.setValue("red") css.QCheckBox.indicator.unchecked.border.setValue("none") css.QCheckBox.indicator.unchecked.backgroundColor.setValue("rgb(0,20,0)") css.QCheckBox.indicator.unchecked.hover.backgroundColor.setValue("purple") css.QLineEdit['[echoMode="2"]']["lineedit-password-character"].setValue(9679) css["QCheckBox::indicator:unchecked"].margin.setValue(0) assert css.toString() == textwrap.dedent( """ * { border: none; } QFrame { border: 1px solid green; color: green; } QCheckBox { border: 1px solid green; color: green; } QCheckBox::indicator { background-color: red; } QCheckBox::indicator:unchecked { border: none; background-color: rgb(0,20,0); margin: 0; } QCheckBox::indicator:unchecked:hover { background-color: purple; } QLineEdit[echoMode="2"] { lineedit-password-character: 9679; } """ )[1:] def test_empty_style(css): css.QCheckBox.indicator.border.setValue("none") assert css.QCheckBox.toString() == "" def test_subcontrol_options(): import qstylizer.descriptor.subcontrol assert 'add-line' in qstylizer.descriptor.subcontrol.SubControlParent.get_attr_options() def test_pseudostate_options(): import qstylizer.descriptor.pseudostate assert 'minimized' in qstylizer.descriptor.pseudostate.PseudoStateParent.get_attr_options() def test_prop_semicolon(css): css.QComboBox.color.setValue("red;") assert css.QComboBox.color.value == "red" # def test_deepcopy(css): # import copy # css.QCheckBox.indicator.hover.border.setValue("none" # css.QCheckBox.indicator.backgroundColor.setValue("red" # indicator.setValue(copy.deepcopy(css.QCheckBox.indicator) # indicator.color.setValue("yellow" # indicator.hover.border.setValue("1px solid green" # assert indicator is not css.QCheckBox.indicator # assert "color" in indicator.keys() # assert "color" not in css.QCheckBox.indicator.keys() # assert css.QCheckBox.indicator.hover.border == "none" # assert indicator.hover.border == "1px solid green" # # # def test_assign_subcontrol(css): # import qstylizer.style # subcontrol.setValue(qstylizer.style.SubControlRule("indicator") # subcontrol.backgroundColor.setValue("red" # subcontrol.unchecked.border.setValue("none" # subcontrol.unchecked.backgroundColor.setValue("rgb(0,20,0)" # subcontrol.unchecked.hover.backgroundColor.setValue("purple" # # css.QComboBox.indicator.setValue(subcontrol # css.QCheckBox.indicator.setValue(subcontrol # # assert css.QCheckBox.indicator is not css.QComboBox.indicator # assert css.QCheckBox.indicator.selector == "QCheckBox::indicator" # assert css.QComboBox.indicator.selector == "QComboBox::indicator" def test_child_class_style(css): css.QWidget.color.setValue("red") css["QWidget QFrame"].backgroundColor.setValue("green") css.QFrame.color.setValue("black") assert css.toString() == textwrap.dedent( """ QWidget { color: red; } QWidget QFrame { background-color: green; } QFrame { color: black; } """ )[1:] def test_unscoped_style(css): css.backgroundColor.setValue("red") css.border.setValue("none") assert css.toString() == textwrap.dedent( """ background-color: red; border: none; """ )[1:] def test_global_style(css): css.backgroundColor.setValue("red") css.border.setValue("none") css.QWidget.indicator.border.setValue("1px solid green") assert css.toString() == textwrap.dedent( """ * { background-color: red; border: none; } QWidget::indicator { border: 1px solid green; } """ )[1:] def test_not_operator(css): import qstylizer.style css.QWidget.indicator["!selected"].color.setValue("green") assert type(css.QWidget.indicator["!selected"]) == qstylizer.style.PseudoStateRule assert css.toString() == textwrap.dedent( """ QWidget::indicator:!selected { color: green; } """ )[1:] def test_pseudostate_prop_same_name(css): css.QWidget.top.setValue(0) css.QWidget.right.setValue(0) css.QWidget.bottom.setValue(0) css.QWidget.left.setValue(0) css.QTabBar.tab.top.color.setValue("red") assert css.toString() == textwrap.dedent( """ QWidget { top: 0; right: 0; bottom: 0; left: 0; } QTabBar::tab:top { color: red; } """ )[1:] def test_pseudoprop_set(css): css.QWidget.tab.top.setValue("0") css.QWidget.tab.top.color.setValue("green") assert css.toString() == textwrap.dedent( """ QWidget::tab { top: 0; } QWidget::tab:top { color: green; } """ )[1:] def test_add_child_rule(css): css.QCheckBox.indicator.backgroundColor.setValue("red") assert len(css._child_rules) == 3 assert css.QCheckBox in css._child_rules.values() assert css.QCheckBox.indicator in css._child_rules.values() def test_set_values(css): css.QToolButton.setValues( border="1px transparent lightblue", borderRadius="3px", margin="1px", padding="3px", ) assert css.QToolButton.border.value == "1px transparent lightblue" def test_global_object_prop(css): css["*"].color.setValue("green") css["*[test=\"2\"]"].color.setValue("red") assert css.toString() == textwrap.dedent( """ * { color: green; } *[test="2"] { color: red; } """ )[1:] def test_global_scope(css): css["*"].color.setValue("red") css.backgroundColor.setValue("green") assert css.toString() == textwrap.dedent( """ * { color: red; background-color: green; } """ )[1:] def test_update_overwrite(): import qstylizer.parser style1 = """ QWidget { background-color: blue; } """ style2 = """ QWidget { background-color: red; } """ qss1 = qstylizer.parser.parse(style1) qss2 = qstylizer.parser.parse(style2) qss1.update(qss2) assert qss1.toString() == qss2.toString() def test_update_overwrite_and_append(): import qstylizer.parser style1 = """ QWidget { background-color: blue; } QMenuBar { color: yellow; } """ style2 = """ QWidget { background-color: red; } QTabBar { color: green; } """ qss1 = qstylizer.parser.parse(style1) qss2 = qstylizer.parser.parse(style2) qss1.update(qss2) assert qss1.toString() == textwrap.dedent( """ QWidget { background-color: red; } QMenuBar { color: yellow; } QTabBar { color: green; } """ )[1:] def test_update_nested(): import qstylizer.parser style1 = """ QWidget { color: yellow; } QWidget:item { color: white; } """ style2 = """ QWidget { background-color: red; } QWidget:item:selected { background-color: blue; } """ qss1 = qstylizer.parser.parse(style1) qss2 = qstylizer.parser.parse(style2) qss1.update(qss2) assert qss1.toString() == textwrap.dedent( """ QWidget { color: yellow; background-color: red; } QWidget:item { color: white; } QWidget:item:selected { background-color: blue; } """ )[1:] assert qss1.QWidget.color.value == "yellow" qstylizer-0.2.4/test/unit/000077500000000000000000000000001471743624600155375ustar00rootroot00000000000000qstylizer-0.2.4/test/unit/test_style.py000066400000000000000000000220511471743624600203100ustar00rootroot00000000000000 import pytest import qstylizer.style @pytest.mark.parametrize( "selector, expected", [ ( "QComboBox", ["QComboBox"] ), ( "QComboBox:pseudostate", ["QComboBox", ":pseudostate"] ), ( "QComboBox::indicator:pseudostate", ["QComboBox", "::indicator", ":pseudostate"] ), ( "QWidget[echoMode=2]", ["QWidget", "[echoMode=2]"] ), ( 'QWidget[custom_prop="1"]', ["QWidget", '[custom_prop="1"]'] ), ( "QWidget#objectName", ["QWidget", "#objectName"] ), ( "QWidget QFrame", ["QWidget", " QFrame"] ), ( "#objectName", ["#objectName"] ), ( "::pseudostate", ["::pseudostate"] ), ( "QTreeView::branch:!has-children:adjoins-item", ["QTreeView", "::branch", ":!has_children", ":adjoins_item"] ), ( '*[custom_prop="custom_value:1"]', ["*", '[custom_prop="custom_value:1"]'] ) ], ids=[ "with-single-name", "with-pseudostate", "with-indicator-and-pseudostate", "with-object-property", "with-custom-object-property", "with-class-and-object", "with-child-class", "with-object", "with-only-pseudostate", "with-not-operator-and-dashes", "with-global-custom-prop" ] ) def test_split_selector(css, selector, expected): assert css.split_selector(selector) == expected @pytest.mark.parametrize( "key, expected", [ ("background-color", "background-color"), (":checked", "checked"), ("background_color", "background-color"), (12345, "12345"), ("QWidget", "QWidget"), ("Custom", "Custom"), ("CustomTabBar", "CustomTabBar"), ("qproperty-drawBase", "qproperty-drawBase"), ], ids=[ "with-normal-key", "with-semicolon", "with-underscore", "with-nonstring", "with-qclassname", "with-capital", "with-camelize", "with-qproperty", ] ) def test_sanitize_key(css, key, expected): assert css._sanitize_key(key) == expected @pytest.mark.parametrize( "value, expected", [ ("1px solid #000000", "1px solid #000000"), ("1px solid #000000;", "1px solid #000000"), (100, 100), (u"green;", u"green"), ], ids=[ "with-normal-value", "with-semicolon", "with-nonstring", "with-unicode", ] ) def test_sanitize_value(css, value, expected): assert css._sanitize_value(value) == expected @pytest.mark.parametrize( "name, " "found_value, " "expected, " "rule_list_call_count, " "rules_call_count", [ ( "QComboBox, QCheckBox", None, "StyleRuleList", 1, 0 ), ( "QComboBox", "Value", "Value", 0, 0 ), ( "QComboBox", None, "Value", 0, 1 ), ], ids=[ "with-rule-list", "with-existing-class", "with-new-class", ] ) def test_find_or_create_child_rule( mocker, css, style_class, name, found_value, expected, rule_list_call_count, rules_call_count ): mocked_find_child_rule = mocker.patch.object( style_class, "find_child_rule", return_value=found_value ) mocked_create_child_rule_list = mocker.patch.object( style_class, "create_child_rule_list", return_value="StyleRuleList" ) mocked_create_child_rules = mocker.patch.object( style_class, "create_child_rules", return_value=expected ) assert css.find_or_create_child_rule(name) == expected mocked_find_child_rule.assert_called_once_with(name) assert mocked_create_child_rule_list.call_count == rule_list_call_count assert mocked_create_child_rules.call_count == rules_call_count def test_find_child_rule(mocker, style_class, css): key = "KEY" value = "VALUE" mocked_sanitize_key = mocker.patch.object( style_class, "_sanitize_key", return_value=key ) mocked_get = mocker.patch.object( style_class, "get", return_value=value ) assert css.find_child_rule(key) == value mocked_sanitize_key.assert_called_once_with(key) mocked_get.assert_called_once_with(key) def test_create_child_rule_list(mocker, style_class, css): import qstylizer.style style_list = "StyleListInstance" name = "test" mocker.patch.object( qstylizer.style, "StyleRuleList", return_value=style_list ) mocked_set_child_rule = mocker.patch.object(style_class, "set_child_rule") assert css.create_child_rule_list(name) == style_list mocked_set_child_rule.assert_called_once_with(name, style_list) @pytest.mark.parametrize( "selector, " "curr_name, " "find_or_create_child_rule_call_count, ", [ ( "QComboBox", "QComboBox", 0, ), ( "QComboBox::indicator", "QComboBox", 1, ), ], ids=[ "with-single-style", "with-multiple-style", ] ) def test_create_child_rules( mocker, style_class, css, selector, curr_name, find_or_create_child_rule_call_count ): mocked_style = mocker.MagicMock() mocked_split_selector = mocker.patch.object( style_class, "split_selector", return_value=[curr_name] ) mocked_find_child_rule = mocker.patch.object( style_class, "find_child_rule", return_value=mocked_style ) mocker.patch.object( style_class, "create_child_rule", return_value=mocked_style ) mocker.patch.object( style_class, "find_or_create_child_rule", return_value=mocked_style ) css.create_child_rules(selector) mocked_split_selector.assert_called_once_with(selector) mocked_find_child_rule.assert_called_once_with(curr_name) assert ( mocked_style.find_or_create_child_rule.call_count == find_or_create_child_rule_call_count ) def test_create_child_rule(mocker, style_class, css): import qstylizer.style name = "::indicator" style = mocker.MagicMock() class_ = mocker.MagicMock(return_value=style) mocked_subclass_function = mocker.patch.object( qstylizer.style, "rule_class", return_value=class_ ) mocked_add_value = mocker.patch.object( style_class, "set_child_rule" ) css.create_child_rule(name) mocked_add_value.assert_called_with(name, style) mocked_subclass_function.assert_called_once_with(name) class_.assert_called_with(name=name, parent=css) def test_set_child_rule(css): rule = qstylizer.style.ClassRule("QUnknown") css.set_child_rule("QUnknown", rule) assert rule in css.values() css.clear() css.set_child_rule("Test", 20) assert isinstance(list(css.values())[0], qstylizer.style.PropRule) assert list(css.values())[0].value == 20 def test_add_child_rule(css): rule = qstylizer.style.ClassRule("QUnknown") css._add_child_rule(rule) assert rule in css._child_rules.values() def test_selector(): rule = qstylizer.style.ClassRule("QCheckBox") child_rule = qstylizer.style.SubControlRule("indicator") child_rule._parent = rule assert child_rule.selector == "QCheckBox::indicator" assert rule.selector == "QCheckBox" def test_name(): rule = qstylizer.style.SubControlRule("indicator") rule._name = "::indicator" assert rule.name == "indicator" def test_scope_operator(css): rule = css["aaaaa"] child_rule = css["aaaaa"]["bbbbb"] child_child_rule = css["aaaaa"]["bbbbb"]["ccccc"] assert rule.scope_operator == "" assert child_rule.scope_operator == "::" assert child_child_rule.scope_operator == ":" def test_is_leaf(css): assert css["aaaaa"].is_leaf() assert css["bbbbb"]["ccccc"].is_leaf() assert not css["bbbbb"].is_leaf() def test_is_top_level(css): assert css["aaaaa"].is_top_level() assert not css["bbbbb"]["ccccc"].is_top_level() assert css["bbbbb"].is_top_level() @pytest.mark.parametrize( "name, expected", [ ("indicator", "SubControlRule"), ("has-children", "PseudoStateRule"), ("::subcontrol", "SubControlRule"), (":pseudostate", "PseudoStateRule"), ("#objectName", "ObjectRule"), ("[echoMode=2]", "ObjectPropRule"), (" QFrame", "ChildClassRule"), ("QObject", "ClassRule") ], ids=[ "with-known-subcontrol", "with-known-pseudostate", "with-subcontrol-scope-op", "with-pseudostate-scope-op", "with-object", "with-object-property", "with-child-class", "with-known-class", ] ) def test_style_child_rule_class(mocker, style_class, css, name, expected): import qstylizer.style assert qstylizer.style.rule_class(name).__name__ == expected qstylizer-0.2.4/tox.ini000066400000000000000000000002161471743624600151130ustar00rootroot00000000000000[tox] envlist = py27,py35,py39 [testenv] command=pytest [testenv:py27] extras=test27 [testenv:py35] extras=test [testenv:py39] extras=test