{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Qt-Material\n",
    "\n",
    "This is another stylesheet for **PySide6**, **PySide2**, **PyQt5** and **PyQt6**, which looks like Material Design (close enough).\n",
    "\n",
    "![GitHub top language](https://img.shields.io/github/languages/top/un-gcpds/qt-material)\n",
    "![PyPI - License](https://img.shields.io/pypi/l/qt-material)\n",
    "![PyPI](https://img.shields.io/pypi/v/qt-material)\n",
    "![PyPI - Status](https://img.shields.io/pypi/status/qt-material)\n",
    "![PyPI - Python Version](https://img.shields.io/pypi/pyversions/qt-material)\n",
    "![GitHub last commit](https://img.shields.io/github/last-commit/un-gcpds/qt-material)\n",
    "![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/UN-GCPDS/qt-material)\n",
    "[![Documentation Status](https://readthedocs.org/projects/qt-material/badge/?version=latest)](https://qt-material.readthedocs.io/en/latest/?badge=latest)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There is some custom dark themes:\n",
    "![dark](_images/dark.gif)\n",
    "And light:\n",
    "![light](_images/light.gif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Navigation\n",
    "\n",
    "  * [Install](#install)\n",
    "  * [Usage](#usage)\n",
    "  * [Themes](#themes)\n",
    "  * [Custom colors](#custom-colors)\n",
    "  * [Usage](#usage)\n",
    "  * [Light themes](#light-themes)\n",
    "  * [Environ variables](#environ-variables)\n",
    "  * [Alternative QPushButtons and custom fonts](#alternative-qpushbuttons-and-custom-fonts)\n",
    "  * [Custom stylesheets](#custom-stylesheets)\n",
    "  * [Run examples](#run-examples)\n",
    "  * [New themes](#new-themes)\n",
    "  * [Change theme in runtime](#change-theme-in-runtime)\n",
    "  * [Export theme](#export-theme)\n",
    "  * [Density scale](#density-scale)\n",
    "  * [Troubleshoots](#troubleshoots)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Install"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pip install qt-material"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Usage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "from PySide6 import QtWidgets\n",
    "# from PySide2 import QtWidgets\n",
    "# from PyQt5 import QtWidgets\n",
    "from qt_material import apply_stylesheet\n",
    "\n",
    "# create the application and the main window\n",
    "app = QtWidgets.QApplication(sys.argv)\n",
    "window = QtWidgets.QMainWindow()\n",
    "\n",
    "# setup stylesheet\n",
    "apply_stylesheet(app, theme='dark_teal.xml')\n",
    "\n",
    "# run\n",
    "window.show()\n",
    "app.exec_()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Themes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:root:qt_material must be imported after PySide or PyQt!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "['dark_amber.xml',\n",
       " 'dark_blue.xml',\n",
       " 'dark_cyan.xml',\n",
       " 'dark_lightgreen.xml',\n",
       " 'dark_pink.xml',\n",
       " 'dark_purple.xml',\n",
       " 'dark_red.xml',\n",
       " 'dark_teal.xml',\n",
       " 'dark_yellow.xml',\n",
       " 'light_amber.xml',\n",
       " 'light_blue.xml',\n",
       " 'light_cyan.xml',\n",
       " 'light_cyan_500.xml',\n",
       " 'light_lightgreen.xml',\n",
       " 'light_pink.xml',\n",
       " 'light_purple.xml',\n",
       " 'light_red.xml',\n",
       " 'light_teal.xml',\n",
       " 'light_yellow.xml']"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from qt_material import list_themes\n",
    "\n",
    "list_themes()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Custom colors\n",
    "\n",
    "[Color Tool](https://material.io/resources/color/) is the best way to generate new themes, just choose colors and export as `Android XML`, the theme file must look like:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "<!--?xml version=\"1.0\" encoding=\"UTF-8\"?-->\n",
    "<resources>\n",
    "<color name=\"primaryColor\">#00e5ff</color>\n",
    "<color name=\"primaryLightColor\">#6effff</color>\n",
    "<color name=\"secondaryColor\">#f5f5f5</color>\n",
    "<color name=\"secondaryLightColor\">#ffffff</color>\n",
    "<color name=\"secondaryDarkColor\">#e6e6e6</color>\n",
    "<color name=\"primaryTextColor\">#000000</color>\n",
    "<color name=\"secondaryTextColor\">#000000</color>\n",
    "</resources>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Save it as `my_theme.xml` or similar and apply the style sheet from Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "apply_stylesheet(app, theme='dark_teal.xml')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Light themes\n",
    "Light themes will need to add `invert_secondary` argument as `True`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "apply_stylesheet(app, theme='light_red.xml', invert_secondary=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Environ variables\n",
    "\n",
    "There is a environ variables related to the current theme used, these variables are for **consult purpose only**.\n",
    "\n",
    "\n",
    "| Environ variable               | Description                              | Example        |\n",
    "|--------------------------------|------------------------------------------|----------------|\n",
    "| QTMATERIAL_PRIMARYCOLOR        | Primary color                            | #2979ff        |\n",
    "| QTMATERIAL_PRIMARYLIGHTCOLOR   | A bright version of the primary color    | #75a7ff        |\n",
    "| QTMATERIAL_SECONDARYCOLOR      | Secondary color                          | #f5f5f5        |\n",
    "| QTMATERIAL_SECONDARYLIGHTCOLOR | A bright version of the secondary color  | #ffffff        |\n",
    "| QTMATERIAL_SECONDARYDARKCOLOR  | A dark version of the primary color      | #e6e6e6        |\n",
    "| QTMATERIAL_PRIMARYTEXTCOLOR    | Color for text over primary background   | #000000        |\n",
    "| QTMATERIAL_SECONDARYTEXTCOLOR  | Color for text over secondary background | #000000        |\n",
    "| QTMATERIAL_THEME               | Name of theme used                       | light_blue.xml |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Alternative QPushButtons and custom fonts\n",
    "\n",
    "There is an `extra` argument for accent colors and custom fonts. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "extra = {\n",
    "\n",
    "    # Button colors\n",
    "    'danger': '#dc3545',\n",
    "    'warning': '#ffc107',\n",
    "    'success': '#17a2b8',\n",
    "\n",
    "    # Font\n",
    "    'font_family': 'Roboto',\n",
    "}\n",
    "\n",
    "apply_stylesheet(app, 'light_cyan.xml', invert_secondary=True, extra=extra)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The accent colors are applied to `QPushButton` with the corresponding `class`  property:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pushButton_danger.setProperty('class', 'danger')\n",
    "pushButton_warning.setProperty('class', 'warning')\n",
    "pushButton_success.setProperty('class', 'success')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![extra](_images/extra.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Custom stylesheets\n",
    "\n",
    "Custom changes can be performed by overwriting the stylesheets, for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "QPushButton {{\n",
    "  color: {QTMATERIAL_SECONDARYCOLOR};\n",
    "  text-transform: none;\n",
    "  background-color: {QTMATERIAL_PRIMARYCOLOR};\n",
    "}}\n",
    "\n",
    ".big_button {{\n",
    "  height: 64px;\n",
    "}}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then, the current stylesheet can be extended just with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "apply_stylesheet(app, theme='light_blue.xml', css_file='custom.css')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The stylesheet can also be changed on runtime by:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stylesheet = app.styleSheet()\n",
    "with open('custom.css') as file:\n",
    "    app.setStyleSheet(stylesheet + file.read().format(**os.environ))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And the class style can be applied with the `setProperty` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "self.main.pushButton.setProperty('class', 'big_button')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![extra](_images/custom.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run examples\n",
    "A window with almost all widgets (see the previous screenshots) are available to test all themes and **create new ones**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "git clone https://github.com/UN-GCPDS/qt-material.git\n",
    "cd qt-material\n",
    "python setup.py install\n",
    "cd examples/full_features\n",
    "python main.py --pyside6"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![theme](_images/theme.gif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## New themes\n",
    "\n",
    "Do you have a custom theme? it looks good? create a [pull request](https://github.com/UN-GCPDS/qt-material/pulls) in [themes folder](https://github.com/UN-GCPDS/qt-material/tree/master/qt_material/themes>)  and share it with all users.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Change theme in runtime\n",
    "\n",
    "There is a `qt_material.QtStyleTools` class that must be inherited along to `QMainWindow` for change themes in runtime using the `apply_stylesheet()` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RuntimeStylesheets(QMainWindow, QtStyleTools):\n",
    "    \n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.main = QUiLoader().load('main_window.ui', self)\n",
    "        \n",
    "        self.apply_stylesheet(self.main, 'dark_teal.xml')\n",
    "        # self.apply_stylesheet(self.main, 'light_red.xml')\n",
    "        # self.apply_stylesheet(self.main, 'light_blue.xml')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![run](_images/runtime.gif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Integrate stylesheets in a menu\n",
    "\n",
    "A custom _stylesheets menu_ can be added to a project for switching across all default available themes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RuntimeStylesheets(QMainWindow, QtStyleTools):\n",
    "    \n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.main = QUiLoader().load('main_window.ui', self)\n",
    "        \n",
    "        self.add_menu_theme(self.main, self.main.menuStyles)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![menu](_images/runtime_menu.gif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create new themes\n",
    "\n",
    "A simple interface is available to modify a theme in runtime, this feature can be used to create a new theme, the theme file is created in the main directory as `my_theme.xml`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RuntimeStylesheets(QMainWindow, QtStyleTools):\n",
    "    \n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.main = QUiLoader().load('main_window.ui', self)\n",
    "        \n",
    "        self.show_dock_theme(self.main)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![dock](_images/runtime_dock.gif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A full set of examples are available in the [exmaples directory](https://github.com/UN-GCPDS/qt-material/blob/master/examples/runtime/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Export theme\n",
    "\n",
    "This feature able to use ```qt-material``` themes into ```Qt``` implementations using only local files."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qt_material import export_theme\n",
    "\n",
    "extra = {\n",
    "\n",
    "    # Button colors\n",
    "    'danger': '#dc3545',\n",
    "    'warning': '#ffc107',\n",
    "    'success': '#17a2b8',\n",
    "\n",
    "    # Font\n",
    "    'font_family': 'monoespace',\n",
    "    'font_size': '13px',\n",
    "    'line_height': '13px',\n",
    "\n",
    "    # Density Scale\n",
    "    'density_scale': '0',\n",
    "\n",
    "    # environ\n",
    "    'pyside6': True,\n",
    "    'linux': True,\n",
    "\n",
    "}\n",
    "\n",
    "export_theme(theme='dark_teal.xml', \n",
    "             qss='dark_teal.qss', \n",
    "             rcc='resources.rcc',\n",
    "             output='theme', \n",
    "             prefix='icon:/', \n",
    "             invert_secondary=False, \n",
    "             extra=extra,\n",
    "            )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This script will generate both ```dark_teal.qss``` and ```resources.rcc``` and a folder with all theme icons called ```theme```.\n",
    "\n",
    "The files generated can be integrated into a ```PySide6``` application just with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "\n",
    "from PySide6 import QtWidgets\n",
    "from PySide6.QtCore import QDir\n",
    "from __feature__ import snake_case, true_property\n",
    "\n",
    "# Create application\n",
    "app = QtWidgets.QApplication(sys.argv)\n",
    "\n",
    "# Load styles\n",
    "with open('dark_teal.qss', 'r') as file:\n",
    "    app.style_sheet = file.read()\n",
    "\n",
    "# Load icons\n",
    "QDir.add_search_path('icon', 'theme')\n",
    "\n",
    "# App\n",
    "window = QtWidgets.QMainWindow()\n",
    "checkbox = QtWidgets.QCheckBox(window)\n",
    "checkbox.text = 'CheckBox'\n",
    "window.show()\n",
    "app.exec()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This files can also be used into non ```Python``` environs like ```C++```."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Density scale\n",
    "\n",
    "The ``extra`` arguments also include an option to set the **density scale**, by default is ```0```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "extra = {\n",
    "    \n",
    "    # Density Scale\n",
    "    'density_scale': '-2',\n",
    "}\n",
    "\n",
    "apply_stylesheet(app, 'default', invert_secondary=False, extra=extra)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![dock](_images/density/density.gif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Troubleshoots"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### QMenu\n",
    "\n",
    "`QMenu` has multiple rendering for each Qt backend, and for each operating system. Even can be related with the style, like [fusion](https://doc.qt.io/qt-5/qtquickcontrols2-fusion.html). Then, the `extra` argument also supports`QMenu` parameters to configure this widgest for specific combinations. This options are not affected by **density scale**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "extra['QMenu'] = {\n",
    "    'height': 50,\n",
    "    'padding': '50px 50px 50px 50px',  # top, right, bottom, left\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
