├── docs
├── CNAME
├── favicon.ico
├── img
│ ├── card.png
│ ├── form.png
│ ├── grid.png
│ ├── chart.png
│ ├── index.png
│ ├── social.png
│ ├── table.png
│ └── autocomplete.png
├── hero.html
├── login.html
├── gallery
│ ├── card.html
│ ├── form.html
│ └── chart.html
├── gallery.html
├── index.html
└── flask.html
├── sample-apps
├── pypi-analytics
│ ├── requirements.txt
│ ├── Procfile
│ ├── Makefile
│ └── main.py
└── websockets
│ ├── requirements.txt
│ ├── Procfile
│ ├── Makefile
│ └── main.py
├── .vscode
└── settings.json
├── generator
├── docs
│ ├── login.py
│ ├── gallery.py
│ ├── index.py
│ ├── flask.py
│ ├── about.py
│ ├── component_reference.py
│ ├── common
│ │ └── components.py
│ └── playground.py
├── gallery
│ ├── chart.py
│ ├── card.py
│ ├── grid.py
│ ├── table.py
│ └── form.py
├── screenshot.py
├── generate.py
└── includes
│ └── advanced.py.txt
├── integration_tests
└── flask_test.py
├── src
└── pyvibe
│ └── component_interface.py
├── pyproject.toml
├── LICENSE
├── .github
└── workflows
│ └── python-publish.yml
├── Makefile
├── CONTRIBUTING.md
├── .gitignore
└── README.md
/docs/CNAME:
--------------------------------------------------------------------------------
1 | www.pyvibe.com
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/img/card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/card.png
--------------------------------------------------------------------------------
/docs/img/form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/form.png
--------------------------------------------------------------------------------
/docs/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/grid.png
--------------------------------------------------------------------------------
/docs/img/chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/chart.png
--------------------------------------------------------------------------------
/docs/img/index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/index.png
--------------------------------------------------------------------------------
/docs/img/social.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/social.png
--------------------------------------------------------------------------------
/docs/img/table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/table.png
--------------------------------------------------------------------------------
/docs/img/autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pycob/pyvibe/HEAD/docs/img/autocomplete.png
--------------------------------------------------------------------------------
/sample-apps/pypi-analytics/requirements.txt:
--------------------------------------------------------------------------------
1 | pyvibe
2 | pandas
3 | pycob
4 | gunicorn
5 | plotly
--------------------------------------------------------------------------------
/sample-apps/websockets/requirements.txt:
--------------------------------------------------------------------------------
1 | pyvibe
2 | pycob
3 | flask
4 | flask-sock
5 | gunicorn
6 |
--------------------------------------------------------------------------------
/sample-apps/websockets/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
2 |
--------------------------------------------------------------------------------
/sample-apps/pypi-analytics/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "lldb.library": "/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Versions/A/LLDB"
3 | }
--------------------------------------------------------------------------------
/generator/docs/login.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | from .common.components import navbar, footer
3 |
4 | page = pv.Page("Login", navbar=navbar, footer=footer)
5 |
6 | page.add_header("Login")
7 | page.add_text("You can create your own login forms or link to third-party auth providers")
--------------------------------------------------------------------------------
/generator/gallery/chart.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | import plotly.express as px
3 |
4 | df = px.data.gapminder().query("country=='Canada'")
5 |
6 | page = pv.Page("Chart Example")
7 |
8 | page.add_header("Chart Example")
9 |
10 | fig = px.bar(df, x='year', y='pop')
11 |
12 | page.add_plotlyfigure(fig)
--------------------------------------------------------------------------------
/integration_tests/flask_test.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | import pyvibe as pv
3 |
4 | app = Flask(__name__)
5 |
6 | @app.route('/')
7 | def index():
8 | page = pv.Page('Home')
9 | page.add_header('Hello World')
10 | return page.to_html()
11 |
12 | if __name__ == '__main__':
13 | app.run(debug=True)
--------------------------------------------------------------------------------
/generator/gallery/card.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 |
3 | page = pv.Page("Card Example")
4 |
5 | page.add_header("Card Example")
6 |
7 | with page.add_card() as card:
8 | card.add_header("Card Header")
9 | card.add_text("This is a card. You can put most types of content in a card.")
10 | card.add_link("Learn more", "https://pycob.com")
11 |
12 |
--------------------------------------------------------------------------------
/src/pyvibe/component_interface.py:
--------------------------------------------------------------------------------
1 | import json
2 | from abc import ABC, abstractmethod
3 |
4 | # Base class for all components
5 | class Component:
6 | @abstractmethod
7 | def to_html(self) -> str:
8 | pass
9 |
10 | def _repr_html_(self):
11 | return self.to_html()
12 |
13 | def to_json(self) -> str:
14 | return json.dumps(self, default=lambda o: o.__dict__,
15 | sort_keys=True, indent=4)
16 |
17 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "pyvibe"
3 | version = "0.0.5"
4 | authors = [
5 | { name="Zain Hoda", email="zain@pycob.com" },
6 | ]
7 | description = "Easily create styled web pages with Python"
8 | readme = "README.md"
9 | requires-python = ">=3.7"
10 | classifiers = [
11 | "Programming Language :: Python :: 3",
12 | "License :: OSI Approved :: MIT License",
13 | "Operating System :: OS Independent",
14 | ]
15 |
16 | [project.urls]
17 | "Homepage" = "https://github.com/pycob/pyvibe"
18 | "Bug Tracker" = "https://github.com/pycob/pyvibe/issues"
--------------------------------------------------------------------------------
/generator/gallery/grid.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | import plotly.express as px
3 |
4 | df = px.data.gapminder().query("country=='Canada'")
5 | df = df.sort_values(by="year")
6 |
7 | page = pv.Page("Grid Example")
8 |
9 | page.add_header("Grid Example")
10 |
11 | with page.add_container(grid_columns=2) as container:
12 | container.add_plotlyfigure(px.bar(df, x='year', y='pop'))
13 | container.add_plotlyfigure(px.line(df, x='year', y='lifeExp'))
14 | container.add_plotlyfigure(px.bar(df, x='year', y='gdpPercap'))
15 | container.add_plotlyfigure(px.bar(df, x='year', y='pop'))
--------------------------------------------------------------------------------
/sample-apps/pypi-analytics/Makefile:
--------------------------------------------------------------------------------
1 | # define the name of the virtual environment directory
2 | VENV := venv
3 |
4 | # default target, when make executed without arguments
5 | all: venv run
6 |
7 | $(VENV)/bin/activate: requirements.txt
8 | python3 -m venv $(VENV)
9 | ./$(VENV)/bin/pip install -r requirements.txt
10 |
11 | # venv is a shortcut target
12 | venv: $(VENV)/bin/activate
13 |
14 | run: venv
15 | ./$(VENV)/bin/python3 main.py
16 |
17 | deploy: venv
18 | ./$(VENV)/bin/python3 -m pycob.deploy
19 |
20 | clean:
21 | rm -rf $(VENV)
22 | find . -type f -name '*.pyc' -delete
23 |
24 | .PHONY: all venv run clean deploy
--------------------------------------------------------------------------------
/sample-apps/websockets/Makefile:
--------------------------------------------------------------------------------
1 | # define the name of the virtual environment directory
2 | VENV := venv
3 |
4 | # default target, when make executed without arguments
5 | all: venv run
6 |
7 | $(VENV)/bin/activate: requirements.txt
8 | python3 -m venv $(VENV)
9 | ./$(VENV)/bin/pip install -r requirements.txt
10 |
11 | # venv is a shortcut target
12 | venv: $(VENV)/bin/activate
13 |
14 | run: venv
15 | ./$(VENV)/bin/python3 main.py
16 |
17 | deploy: venv
18 | ./$(VENV)/bin/python3 -m pycob.deploy
19 |
20 | clean:
21 | rm -rf $(VENV)
22 | find . -type f -name '*.pyc' -delete
23 |
24 | .PHONY: all venv run clean deploy
--------------------------------------------------------------------------------
/generator/gallery/table.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | import plotly.express as px
3 |
4 | df = px.data.gapminder().query("country=='Canada'")
5 |
6 | page = pv.Page("Table Example")
7 |
8 | page.add_header("Table Example")
9 |
10 | actions = [
11 | # This button uses the year for the row as a parameter in the URL
12 | pv.Rowaction("Edit", "#/edit?year={year}", "edit", open_in_new_window=False),
13 | # This button uses the country for the row as a parameter in the URL
14 | pv.Rowaction("{iso_alpha}", "#/country?year={year}&country={country}", open_in_new_window=False)
15 | ]
16 |
17 | page.add_pandastable(df, action_buttons=actions)
18 |
--------------------------------------------------------------------------------
/generator/gallery/form.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 |
3 | page = pv.Page("Form Example")
4 |
5 |
6 | # Here we put the form inside a card so it is easier to see.
7 | with page.add_card() as card:
8 | card.add_header("Form Example")
9 |
10 | with card.add_form() as form:
11 | form.add_formtext(label="Name", name="name", placeholder="Enter your name")
12 | form.add_formemail(label="Email", name="email", placeholder="Enter your email")
13 | form.add_formpassword(label="Password", name="password", placeholder="Enter your password")
14 | form.add_formtextarea(label="Message", name="message", placeholder="Enter your message")
15 | form.add_formsubmit(label="Send")
16 |
--------------------------------------------------------------------------------
/generator/docs/gallery.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | from .common.components import navbar, footer, marketing_banner, gallery_grid, all_layouts
3 |
4 | page = pv.Page("Gallery", navbar=navbar, footer=None)
5 | page.add_header("Gallery")
6 |
7 | page.add_text("Here are some examples of what you can create with PyVibe.")
8 |
9 | page.add_component(gallery_grid(all_layouts))
10 |
11 | page.add_text("PyVibe was spun out of Pycob. We are in the process of transitioning Pycob apps to PyVibe.")
12 | page.add_link("See additional examples on Pycob", "https://www.pycob.com/gallery?tag=Featured")
13 | page.add_text("Note: Some of the examples on Pycob are not yet compatible with PyVibe but should give you some examples of the layout possibilities.")
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Zain Hoda
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will upload a Python Package using Twine when a release is created
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
3 |
4 | # This workflow uses actions that are not certified by GitHub.
5 | # They are provided by a third-party and are governed by
6 | # separate terms of service, privacy policy, and support
7 | # documentation.
8 |
9 | name: Upload Python Package
10 |
11 | on:
12 | release:
13 | types: [published]
14 |
15 | permissions:
16 | contents: read
17 |
18 | jobs:
19 | deploy:
20 |
21 | runs-on: ubuntu-latest
22 |
23 | steps:
24 | - uses: actions/checkout@v3
25 | - name: Set up Python
26 | uses: actions/setup-python@v3
27 | with:
28 | python-version: '3.x'
29 | - name: Install dependencies
30 | run: |
31 | python -m pip install --upgrade pip
32 | pip install build
33 | - name: Build package
34 | run: python -m build
35 | - name: Publish package
36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
37 | with:
38 | user: __token__
39 | password: ${{ secrets.PYPI_API_TOKEN }}
40 |
--------------------------------------------------------------------------------
/generator/screenshot.py:
--------------------------------------------------------------------------------
1 | from selenium import webdriver
2 | from selenium.webdriver.chrome.options import Options
3 |
4 | # Get the current directory
5 | import os
6 | dir_path = os.path.dirname(os.path.realpath(__file__))
7 | print("dir_path = ", dir_path)
8 |
9 | # Remove the last folder from the path
10 | dir_path = os.path.dirname(dir_path)
11 | print("dir_path = ", dir_path)
12 |
13 | global driver
14 | driver = None
15 |
16 | def take_screenshot(name: str):
17 | global driver
18 | if driver is None:
19 | options = Options()
20 | # options.add_argument("--headless")
21 | options.add_argument("window-size=1024,768")
22 | options.add_argument("--hide-scrollbars")
23 | # Here Chrome will be used
24 | driver = webdriver.Chrome('/usr/local/bin/chromedriver', options=options)
25 |
26 | # URL of website
27 | url = "file://" + dir_path + "/docs/gallery/" + name + ".html"
28 |
29 | # Opening the website
30 | driver.get(url)
31 | driver.save_screenshot(f"docs/img/{name}.png")
32 |
33 | # Get all the html files in the dir_path + "/docs/gallery" folder
34 | import glob
35 | files = glob.glob(dir_path + "/docs/gallery/*.html")
36 | for file in files:
37 | # Get the name of the file
38 | name = os.path.basename(file)
39 | # Remove the .html extension
40 | name = os.path.splitext(name)[0]
41 | print("screenshotting = ", name)
42 | take_screenshot(name)
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # define the name of the virtual environment directory
2 | VENV := venv
3 |
4 | all: venv docs/index.html server
5 |
6 | venv: $(VENV)/bin/activate
7 |
8 | flask:
9 | ./$(VENV)/bin/pip install flask
10 | ./$(VENV)/bin/python3 integration_tests/flask_test.py
11 |
12 | docs/index.html: src/pyvibe/__init.py___ generator/generate.py
13 | ./$(VENV)/bin/pip install pandas
14 | ./$(VENV)/bin/pip install plotly
15 | ./$(VENV)/bin/python3 generator/generate.py
16 |
17 | # pdoc: src/pyvibe/__init.py___
18 | # ./$(VENV)/bin/pip install pdoc
19 | # ./$(VENV)/bin/pdoc pyvibe --logo https://cdn.pycob.com/pycob_hex.png --logo-link https://www.pyvibe.com --no-show-source -e pycob=https://github.com/pycob/pyvibe/tree/main/src/pyvibe/ -n --docformat google -o docs/pdoc/
20 |
21 | # Build init.py when generate.swift changes
22 | src/pyvibe/__init.py___: generator/generate.swift
23 | swift generator/generate.swift
24 |
25 | # Rebuild the virtual environment when init.py changes
26 | $(VENV)/bin/activate: src/pyvibe/__init.py___
27 | python3 -m venv $(VENV)
28 | ./$(VENV)/bin/pip install --upgrade pip
29 | ./$(VENV)/bin/pip install .
30 |
31 | screenshot:
32 | ./$(VENV)/bin/pip install selenium
33 | sudo ./$(VENV)/bin/python3 generator/screenshot.py
34 |
35 | server:
36 | open http://localhost:8000
37 | ./$(VENV)/bin/python -m http.server -d docs
38 |
39 | clean:
40 | rm -rf $(VENV)
41 | find . -type f -name '*.pyc' -delete
42 |
43 | .PHONY: all clean flask screenshot server
44 |
--------------------------------------------------------------------------------
/generator/docs/index.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | from .common.components import footer, marketing_banner, gallery_grid, featured_layouts
3 |
4 | page = pv.Page("PyVibe: Easily create styled web pages with Python", navbar=None, footer=footer, image="https://www.pyvibe.com/img/social.png")
5 |
6 | container_outer = page.add_container(grid_columns=2)
7 |
8 | container_inner1 = container_outer.add_container(classes="text-center")
9 | container_inner1.add_header("PyVibe", size=9)
10 | container_inner1.add_image("https://cdn.pycob.com/pyvibe.svg", 'logo', classes='w-1/2 mx-auto')
11 | container_inner1.add_header("Easily create styled web pages with Python")
12 | container_inner1.add_link("Learn more", "about.html")
13 |
14 | container_inner2 = container_outer.add_container()
15 | container_inner2.add_code("""import pyvibe as pv
16 |
17 | page = pv.Page()
18 |
19 | page.add_header("Welcome to PyVibe!")
20 |
21 | page.add_text("PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.")
22 | """, prefix="", header="Example")
23 |
24 | with container_inner2.add_card() as card:
25 | card.add_header("Welcome to PyVibe!")
26 | card.add_text("PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.")
27 |
28 | page.add_header("Examples", 2)
29 | page.add_component(gallery_grid(featured_layouts))
30 |
31 | page.add_html(marketing_banner)
--------------------------------------------------------------------------------
/generator/docs/flask.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | from .common.components import navbar, footer, marketing_banner
3 |
4 | page = pv.Page("Flask", navbar=navbar, footer=footer)
5 |
6 | page.add_header("Flask")
7 |
8 | page.add_header("Simple Example", size=4)
9 | page.add_text("PyVibe can be used with Flask. This is a simple example of how to use PyVibe with Flask.")
10 |
11 | page.add_text("First, create a new file called app.py and add the following code:")
12 |
13 | page.add_code("""from flask import Flask
14 | import pyvibe as pv
15 |
16 | app = Flask(__name__)
17 |
18 | @app.route('/')
19 | def index():
20 | page = pv.Page('Home')
21 | page.add_header('Hello World')
22 | return page.to_html()
23 |
24 | if __name__ == '__main__':
25 | app.run(debug=True)
26 | """, prefix="")
27 |
28 | page.add_text("Then, run the following command in your terminal:")
29 | page.add_code("python app.py", prefix="%")
30 |
31 | page.add_section("extended-example", "Extended Example")
32 | page.add_header("Extended Example", size=4)
33 |
34 | page.add_image("https://storage.googleapis.com/img.pycob.com/screenshot/pypi-analytics.png", "PyPi Analytics", classes="shadow-lg w-full md:w-2/5")
35 | page.add_link("Live App", "https://pypi-analytics.pycob.app")
36 | page.add_link("Source Code on GitHub", "https://github.com/pycob/pyvibe/blob/main/sample-apps/pypi-analytics/main.py")
37 |
38 | page.add_emgithub("https://github.com/pycob/pyvibe/blob/main/sample-apps/pypi-analytics/main.py")
39 |
40 | page.add_html(marketing_banner)
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to PyVibe
2 |
3 | ## Architecture
4 | The [`generator`](generator) directory contains the source assets. Everything else is generated from there.
5 |
6 | ```mermaid
7 | ---
8 | title: Code Generation Flow
9 | ---
10 | flowchart TD
11 | advanced(generator/includes/advanced.py.txt) --> generate.swift[[generator/generate.swift]]
12 | generate.swift --> spec(spec/spec.json)
13 | generate.swift --> init(src/__init__.py)
14 | spec --> generate.py[[generator/generate.py]]
15 | generate.py --> docs_html(docs/*.html)
16 | generate.py --> gallery_html(docs/gallery/*.html)
17 | gallery_html --> screenshot.py[[screenshot.py]]
18 | screenshot.py --> img(img/*.png)
19 | ```
20 |
21 | Due to the architecture above, pull requests should be submitted only to files in the `/generator` subdirectory since everything else is generated from those files
22 |
23 | ## Why Code Generation?
24 | We want to minimize the need to have to search through documentation. Our goal is to be able to rely on autocomplete. To that end, we want to be able to attach `.add_*` methods to all appropriate components so that the list of available components in the current context is available. In order to minimize copy/paste, it's easiest to just generate the library code from a central definition.
25 |
26 | ## Why Swift?
27 | Very strongly typed languages with exhaustive enum checking makes code generation a lot more robust. Of the languages that would suit this criteria, Swift is probably the most readable for Python developers.
--------------------------------------------------------------------------------
/generator/generate.py:
--------------------------------------------------------------------------------
1 | print("Generating from Python...")
2 | import pyvibe as pv
3 | from os.path import dirname, basename, isfile, join
4 | import glob
5 | from docs.common.components import navbar, footer
6 |
7 | modules = glob.glob(join(dirname(__file__)+"/docs", "*.py"))
8 | files = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
9 |
10 | for file in files:
11 | print("Importing file: " + file)
12 | exec(f"from docs import {file}")
13 |
14 | # exec(f"{file}.page.add_header('Python Source')")
15 | # exec(f"{file}.page.add_text('This is the source code for the current page.')")
16 | # exec(f"{file}.page.add_emgithub('https://github.com/pycob/pyvibe/blob/main/generator/docs/{file}.py')")
17 | # exec(f"{file}.page.add_link('View Source', 'https://github.com/pycob/pyvibe/blob/main/generator/docs/{file}.py')")
18 |
19 | print(f"Writing to {file}.html")
20 | exec(f"with open('docs/{file}.html', 'w') as f: f.write({file}.page.to_html())")
21 |
22 | gallery_modules = glob.glob(join(dirname(__file__)+"/gallery", "*.py"))
23 | gallery_files = [ basename(f)[:-3] for f in gallery_modules if isfile(f) and not f.endswith('__init__.py')]
24 |
25 | for file in gallery_files:
26 | print("Importing file: " + file)
27 | exec(f"from gallery import {file}")
28 |
29 | exec(f"{file}.page.add_header('Python Source')")
30 | exec(f"{file}.page.add_text('This is the source code for the current page.')")
31 | exec(f"{file}.page.add_emgithub('https://github.com/pycob/pyvibe/blob/main/generator/gallery/{file}.py')")
32 | exec(f"{file}.page.add_link('View Source', 'https://github.com/pycob/pyvibe/blob/main/generator/gallery/{file}.py')")
33 |
34 | exec(f'pg = pv.Page({file}.page.title, navbar=navbar, components={file}.page.components)')
35 |
36 | print(f"Writing to {file}.html")
37 | exec(f"with open('docs/gallery/{file}.html', 'w') as f: f.write(pg.to_html())")
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 | .DS_Store
131 | **/*api_key*
132 | **/venv
133 | **/tmp
134 |
--------------------------------------------------------------------------------
/generator/docs/about.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | from .common.components import navbar, footer, marketing_banner, gallery_grid, featured_layouts
3 |
4 | page = pv.Page("PyVibe", navbar=navbar, footer=footer, image="./img/social.png")
5 |
6 | page.add_header("Easily create styled web pages with Python")
7 |
8 | page.add_code("""import pyvibe as pv
9 |
10 | page = pv.Page()
11 |
12 | page.add_header("Welcome to PyVibe!")
13 |
14 | page.add_text("PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.")
15 | """, prefix="", header="Example")
16 |
17 | with page.add_card() as card:
18 | card.add_header("Welcome to PyVibe!")
19 | card.add_text("PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.")
20 |
21 | page.add_link("See All Components", "component_reference.html")
22 | page.add_link("Interactive Playground", "playground.html")
23 | page.add_text("")
24 |
25 | page.add_header("What is PyVibe?", 3)
26 | page.add_text("PyVibe is a Python library for creating web pages. It is designed to be a quick way for Python developers to build front-ends.")
27 | page.add_text("PyVibe uses a component-based approach to building web pages. This means that you can create a page by combining components together.")
28 |
29 | page.add_header("How do I use PyVibe?", 3)
30 | page.add_text("PyVibe is a Python library that simplifies UI development for web apps by providing semantic Python components that compile into HTML and can be used with any web framework.")
31 | page.add_text("Fundamentally, PyVibe returns an HTML string that can be used with:")
32 |
33 | with page.add_list() as list:
34 | list.add_listitem("Static Pages: Like the one you're viewing now", is_checked=True)
35 | list.add_listitem("Flask: Inside a Flask function", is_checked=True)
36 | list.add_listitem("Pyodide: For dynamic client-side rendered pages", is_checked=True)
37 |
38 | page.add_text("")
39 |
40 | page.add_header("What can you build with PyVibe?", 3)
41 | page.add_component(gallery_grid(featured_layouts))
42 | page.add_link("See more examples", "/gallery.html")
43 |
44 | page.add_header("Designed for Autocomplete", 3)
45 | page.add_text("PyVibe is designed to be used with autocomplete. This means that you can type page.add_ and autocomplete will show you all the components that you can add to your page along with documentation about the component.")
46 | page.add_html('
')
47 |
48 | page.add_header("Themes", 3)
49 | page.add_text('PyVibe is meant to be a generic framework. While the default theme uses Flowbite, which are components that use TailwindCSS, we envision including many themes and CSS frameworks in the future.')
50 |
51 | page.add_header("How does PyVibe compare to Streamlit, Plotly Dash, Pynecone, Anvil, NiceGUI, etc?", 3)
52 | page.add_text("PyVibe is not a web server -- it produces styled HTML that can be used with any web server.")
53 |
54 | page.add_html(marketing_banner)
--------------------------------------------------------------------------------
/sample-apps/websockets/main.py:
--------------------------------------------------------------------------------
1 | import pyvibe as pv
2 | from flask import Flask, render_template
3 | from flask_sock import Sock
4 |
5 | app = Flask(__name__)
6 | sock = Sock(app)
7 |
8 | class WebSocketReceiverComponent(pv.Component):
9 | def __init__(self, path_to_websocket: str):
10 | self.path_to_websocket = path_to_websocket
11 |
12 | def to_html(self):
13 | return """
14 |
{project_name}")
53 |
54 | if not subtitle:
55 | with page.add_form(action="/project_detail") as form:
56 | form.add_formhidden(name="project_name", value=project_name)
57 | form.add_formhidden(name="compare_to", value=compare_to if compare_to else "")
58 | form.add_formtext(name="subtitle", label="Subtitle", placeholder="Enter a subtitle")
59 | form.add_formsubmit("Update Subtitle")
60 | else:
61 | page.add_header(f"{subtitle}", size=4)
62 |
63 | if compare_to:
64 | project_detail = pypi_projects_by_month[pypi_projects_by_month['pypi_project'].isin([project_name, compare_to])]
65 | else:
66 | project_detail = pypi_projects_by_month[pypi_projects_by_month['pypi_project'] == project_name]
67 |
68 | fig = px.line(project_detail.sort_values(["month", "pypi_project"]), x="month", y="avg_downloads_per_day", line_group="pypi_project", color="pypi_project")
69 |
70 | page.add_plotlyfigure(fig)
71 |
72 | if not compare_to:
73 | with page.add_card() as card:
74 | with card.add_form(action="/project_detail") as form:
75 | form.add_formhidden(name="project_name", value=project_name)
76 | form.add_formtext(label="Compare To", name="compare_to", placeholder="Enter a project name")
77 | form.add_formsubmit("Analyze")
78 |
79 | return page.to_html()
80 |
81 | @app.route('/view_source')
82 | def view_source() -> str:
83 | page = pv.Page("View Source")
84 | page.add_header("View Source")
85 |
86 | # Read the source code
87 | with open(__file__, 'r') as f:
88 | source = f.read()
89 |
90 | # Add the source code to the page
91 | page.add_codeeditor(source, language="python")
92 |
93 | return page.to_html()
94 |
95 | # RUN APP
96 | if __name__ == '__main__':
97 | app.run(debug=True)
98 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PyVibe
2 |
3 | https://www.pyvibe.com/ | [Examples](https://www.pyvibe.com/gallery.html) | [Use With Flask](https://www.pyvibe.com/flask.html) | [Live Playground](https://www.pyvibe.com/playground.html) | [Component Reference](https://www.pyvibe.com/component_reference.html) |
4 |
5 | 
6 |
7 | ## Easily create styled web pages with Python
8 |
9 |
10 | ```python
11 | import pyvibe as pv
12 |
13 | page = pv.Page()
14 |
15 | page.add_header("Welcome to PyVibe!")
16 |
17 | page.add_text("PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.")
18 | ```
19 |
20 | [See all components](https://www.pyvibe.com/component_reference.html)
21 |
22 | [Interactive playground](https://www.pyvibe.com/playground.html)
23 |
24 | ## What is PyVibe?
25 |
26 | PyVibe is a Python library for creating web pages. It is designed to be a quick way for Python developers to build front-ends.
27 |
28 | PyVibe uses a component-based approach to building web pages. This means that you can create a page by combining components together.
29 |
30 | ## How do I use PyVibe?
31 |
32 | PyVibe is a Python library that simplifies UI development for web apps by providing semantic Python components that compile into HTML and can be used with any web framework.
33 |
34 | Fundamentally, PyVibe returns an HTML string that can be used with:
35 |
36 | - [Static Pages](https://github.com/pycob/pyvibe/blob/main/generator/docs/about.py): Using `.to_html()`
37 | - [Flask](https://www.pyvibe.com/flask.html): Inside a Flask function
38 | - [Pyodide](https://github.com/pycob/pyvibe/blob/main/generator/docs/playground.py#L124-L151): For dynamic client-side rendered pages (experimental)
39 |
40 | ## What can you build with PyVibe?
41 |
42 |
43 |
44 |
45 |
46 | [More Examples](https://www.pyvibe.com/gallery.html)
47 |
48 | ## Designed for Autocomplete
49 | PyVibe is designed to be used with autocomplete. This means that you can type `page.add_` and autocomplete will show you all the components that you can add to your page along with documentation about the component.
50 |
51 | 
52 |
53 | ## Themes
54 | PyVibe is meant to be a generic framework. While the default theme uses [Flowbite](https://flowbite.com/), which are components that use [TailwindCSS](https://tailwindcss.com/), we envision including many themes and CSS frameworks in the future.
55 |
56 | ## How does PyVibe compare to Streamlit, Plotly Dash, Pynecone, Anvil, NiceGUI, etc?
57 | PyVibe is not a web server -- it produces styled HTML that can be used with any web server.
58 |
59 | ## Getting Started
60 | - To get started with PyVibe, simply install the library using pip:
61 |
62 | ```bash
63 | pip install pyvibe
64 | ```
65 | Once installed, you can begin creating UI components by creating a new Page object and adding components to it using the `.add_*` methods. You can then return the page as HTML by calling `page.to_html()`.
66 |
67 | For example, to create a new page with a header and a paragraph of text, you could use the following code:
68 |
69 | ```python
70 | import pyvibe as pv
71 |
72 | page = pv.Page()
73 | page.add_header("Welcome to PyVibe!")
74 | page.add_text("PyVibe is a Python library for creating UI components for web apps without the need to write HTML code.")
75 | print(page.to_html())
76 | ```
77 |
78 | This will output the following HTML (simplified for readability but it also includes the rest of the HTML document including a navbar, footer, head tag, etc):
79 |
80 | ```html
81 | Welcome to PyVibe!
83 |PyVibe is a Python library for creating UI components for web apps without the need to write HTML code.
84 |.add_{element['elementType']})", 4)
77 | else:
78 | page.add_header(element['name'] + f" ({element['elementType']} = {element['name']}(...))", 4)
79 | else:
80 | page.add_header(element['name'], 4)
81 | page.add_text(element['description'])
82 |
83 | if len(element['attachableTo']) > 0:
84 | page.add_header("Use With", 3)
85 |
86 | for attachableTo in element['attachableTo']:
87 | page.add_link(attachableTo + f".add_{element['elementType']}", "#"+attachableTo)
88 |
89 | page.add_text("")
90 | page.add_header('Input', 2)
91 |
92 | table = pv.RawtableComponent()
93 |
94 | tablehead = pv.TableheadComponent()
95 | tablehead.add_tablecellheader("Name")
96 | tablehead.add_tablecellheader("Type")
97 | tablehead.add_tablecellheader("Default Value")
98 | tablehead.add_tablecellheader("Description")
99 |
100 | table.add_component(tablehead)
101 |
102 | tablebody = pv.TablebodyComponent()
103 |
104 | for argument in element['arguments']:
105 | row = pv.TablerowComponent()
106 |
107 | row.add_tablecellheader(argument['name'])
108 | row.add_tablecell(argument['type'])
109 | if 'defaultValue' in argument:
110 | if argument['type'] == "String":
111 | row.add_tablecell("'" + argument['defaultValue'] + "'")
112 | else:
113 | row.add_tablecell(argument['defaultValue'])
114 | else:
115 | row.add_tablecell("")
116 | row.add_tablecell(argument['description'])
117 |
118 | tablebody.add_component(row)
119 |
120 | table.add_component(tablebody)
121 |
122 | if len(element['arguments']) > 0:
123 | page.add_component(table)
124 | else:
125 | page.add_text("No Inputs")
126 | # for argument in element['arguments']:
127 | # if 'defaultValue' in argument:
128 | # page.add_html('' + argument['name'] +': ' + argument['type'] + '. Default: ' + argument['defaultValue'] + '. ' + argument['description'] + '
' + argument['name'] +': ' + argument['type'] + '. ' + argument['description'] + '
PyVibe
108 |Easily create styled web pages with Python
110 |111 | 112 | Learn more 113 | 114 | 115 |
116 |Example
124 |
127 | import pyvibe as pv
128 |
129 | page = pv.Page()
130 |
131 | page.add_header("Welcome to PyVibe!")
132 |
133 | page.add_text("PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.")
134 |
135 | Welcome to PyVibe!
140 |PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.
141 |Login
142 |You can create your own login forms or link to third-party auth providers
143 || ''' + df.index.name + " | " 34 | 35 | if show_actions: 36 | html += '''Actions | ''' 37 | 38 | for column in df.columns: 39 | if column in cols_to_show: 40 | html += '''''' + column + " | " 41 | 42 | html += "|
|---|---|---|---|
| ''' 59 | 60 | action_buttons_to_add = __get_action_buttons_to_add(action_buttons) 61 | 62 | for button_to_add in action_buttons_to_add: 63 | hydrated_label = button_to_add.label.format(**record) 64 | hydrated_url = button_to_add.url.format(**record) 65 | 66 | if button_to_add.open_in_new_window: 67 | html += """""" + hydrated_label + """""" 68 | else: 69 | html += """""" + hydrated_label + """""" 70 | 71 | 72 | html += ''' | ''' 73 | 74 | for column in df.columns: 75 | key = column 76 | if column in cols_to_show: 77 | action_button = __find_key_in_action_buttons(key, action_buttons) 78 | 79 | value = __format_python_object_for_json(record[key]) 80 | 81 | if action_button is not None: 82 | hydrated_label = action_button.label.format(**record) 83 | hydrated_url = action_button.url.format(**record) 84 | if action_button.open_in_new_window: 85 | html += """""" + hydrated_label + """ | """ 86 | else: 87 | html += """""" + hydrated_label + """ | """ 88 | else: 89 | html += '''''' + format_input(row[column]) + " | " 90 | 91 | html += "
Card Example
142 |Card Header
145 |This is a card. You can put most types of content in a card.
146 |147 | 148 | Learn more 149 | 150 | 151 |
152 |Python Source
155 |This is the source code for the current page.
156 | 157 | 158 | 159 |160 | 161 | View Source 162 | 163 | 164 |
165 |Form Example
144 | 163 |Python Source
166 |This is the source code for the current page.
167 | 168 | 169 | 170 |171 | 172 | View Source 173 | 174 | 175 |
176 |Gallery
142 |Here are some examples of what you can create with PyVibe.
143 |PyVibe was spun out of Pycob. We are in the process of transitioning Pycob apps to PyVibe.
206 |207 | 208 | See additional examples on Pycob 209 | 210 | 211 |
212 |Note: Some of the examples on Pycob are not yet compatible with PyVibe but should give you some examples of the layout possibilities.
213 |PyVibe
108 |Easily create styled web pages with Python
110 |111 | 112 | Learn more 113 | 114 | 115 |
116 |Example
124 |
127 | import pyvibe as pv
128 |
129 | page = pv.Page()
130 |
131 | page.add_header("Welcome to PyVibe!")
132 |
133 | page.add_text("PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.")
134 |
135 | Welcome to PyVibe!
140 |PyVibe is an open source Python library for creating UI components for web apps without the need to write HTML code.
141 |Examples
146 |Chart Example
142 | 143 | 147 |Python Source
148 |This is the source code for the current page.
149 | 150 | 151 | 152 |153 | 154 | View Source 155 | 156 | 157 |
158 |Flask
142 |Simple Example
143 |PyVibe can be used with Flask. This is a simple example of how to use PyVibe with Flask.
144 |First, create a new file called app.py and add the following code:
151 |
154 | from flask import Flask
155 | import pyvibe as pv
156 |
157 | app = Flask(__name__)
158 |
159 | @app.route('/')
160 | def index():
161 | page = pv.Page('Home')
162 | page.add_header('Hello World')
163 | return page.to_html()
164 |
165 | if __name__ == '__main__':
166 | app.run(debug=True)
167 |
168 | Then, run the following command in your terminal:
171 |
177 | %
180 | python app.py
181 | Extended Example
185 |
186 | 187 | 188 | Live App 189 | 190 | 191 |
192 |193 | 194 | Source Code on GitHub 195 | 196 | 197 |
198 | 199 | 200 | 201 | 202 | 207 | 235 | 236 |