Skip to content

Commit

Permalink
Merge pull request #43 from SkwalExe/textual-wizard
Browse files Browse the repository at this point in the history
Textual wizard
  • Loading branch information
SkwalExe authored Jul 22, 2024
2 parents db6c9fe + 4510b60 commit ffa3607
Show file tree
Hide file tree
Showing 10 changed files with 699 additions and 1,036 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/pr_checks.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Check for typing/linting/formatting/test errors
name: Check for typing/linting/formatting errors

on:
pull_request:
Expand All @@ -25,6 +25,4 @@ jobs:
run: pdm run lint-check
- name: Check for type errors
run: pdm run check-types
- name: Run tests
run: pdm run tests

7 changes: 2 additions & 5 deletions .github/workflows/pypi_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ name: Publish to PyPI

on:
workflow_dispatch:
push:
branches:
- 'main'
paths:
- 'pyproject.toml'
release:
types: [published]

jobs:
publish:
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# v3.1.0 - 2024-07-22

### Changed
- Now using the textual-wizard library instead of the prototype that was precedently used in this project.

# v3.0.2 - 2024-06-14

### Fixed
Expand Down
1,241 changes: 631 additions & 610 deletions pdm.lock

Large diffs are not rendered by default.

25 changes: 13 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,35 @@ dependencies = [
"textual>=0.38.1",
"click-extra>=4.7.2",
"loguru>=0.7.2",
"textual-wizard>=0.4.3",
]
dynamic = ["version"]

[tool.pdm]
distribution = true
[tool.pdm.version]
source = "file"
path = "src/octologo/__init__.py"

[project.urls]
Home = "https://github.com/SkwalExe/octo-logo"

[project.scripts]
octologo = "octologo.__main__:main"

[tool.pdm]
distribution = true

[tool.pdm.dev-dependencies]
dev = [
"ruff>=0.4.7",
"pytest>=8.2.2",
"pytest-asyncio>=0.23.7",
"pyright>=1.1.365",
"textual-dev>=1.5.1",
]

[tool.pdm.scripts]
format = "ruff format"
dev = "textual run --dev -c octologo"
format-check = "ruff format --check"
lint = "ruff check --fix --show-fixes"
lint-check = "ruff check"
check-types = "pyright"
tests = "pytest"
console = "textual console -x EVENT -x SYSTEM"

[project.urls]
Home = "https://github.com/SkwalExe/octo-logo"

[project.scripts]
octologo = "octologo.__main__:main"

140 changes: 29 additions & 111 deletions src/octologo/__main__.py
Original file line number Diff line number Diff line change
@@ -1,129 +1,47 @@
# ---------------------------------------- Standard Imports
import os
from collections.abc import Generator
from click_extra import extra_command, option
from textual_wizard import Wizard
from textual_wizard.inputs import Select, Text

# ---------------------------------------- External Imports
from click_extra import ExtraContext, Parameter, extra_command, option
from PIL.Image import Image
from textual.app import App
from textual.events import Key
from textual.validation import Length
from textual.widgets import Footer, Header, Label, LoadingIndicator

# ---------------------------------------- Local Imports
from octologo import __version__
from octologo.utils import BASE_DIR, get_output_filename, logger, style_names, styles
from octologo.wizard import SelectQuestion, TextQuestion, Wizard, inq_ask
from octologo.utils import get_output_filename, logger, style_names, styles

BASIC_INFO_QUESTIONS = [
TextQuestion(
Text(
"name",
"Your project's name",
[Length(1, failure_description="Your project's name cannot be blank")],
"super-octo-project",
placeholder="super-octo-project",
),
SelectQuestion("style", "Logo Style", style_names, "first_letter_underlined"),
Select("style", "Logo Style", options=style_names, default_value="first_letter_underlined"),
]


class OctoLogoApp(App):
BINDINGS = [
("ctrl+q", "quit", "Quit"),
("ctrl+t", "toggle_dark", "Toggle Dark Mode"),
]
answers = dict()

CSS_PATH = os.path.join(BASE_DIR, "app.tcss")
TITLE = "Octo Logo Wizard"
finished: bool = False
result: Image | None = None
loading_wid: LoadingIndicator = LoadingIndicator(classes="hidden")

async def on_key(self, event: Key) -> None:
if event.key == "enter" and self.finished:
await self.action_quit()
elif event.key == "v" and self.finished:
if self.result is None:
raise Exception("self.result should not be null")
self.result.show()

def on_wizard_finished(self, message: Wizard.Finished) -> None:
# Get the wizard answers and the wizard's id
self.answers.update(message.answers)
finished_wizard_id = message.wizard_id

# remove the wizard
self.query_one(f"#{finished_wizard_id}").remove()

# When the basic info wizard is finished, mount the style-specific wizard
if finished_wizard_id == "basic_info_wizard":
style_wizard = Wizard(id="style_wizard")
style_wizard.questions = styles[self.answers["style"]].module.questions
style_wizard.title = "Style Settings"
self.mount(style_wizard)
# When the style-specific wizard is finished, create the image and save it
elif finished_wizard_id == "style_wizard":
style = styles[self.answers["style"]].module
self.result = style.get_image(self.answers)
self.loading_wid.remove_class("hidden")
self.set_timer(2, self.final_message)

# Final message
def final_message(self) -> None:
if self.result is None:
raise Exception("self.result should not be none in OctoLogoApp.final_message")

save_to = get_output_filename(self.answers["name"])

self.loading_wid.add_class("hidden")
self.mount(
Label(
f"Logo saved to [bold]{save_to}[/bold].\n"
f"[blue blink]-> Press v to view the result[/blue blink]\n"
f"[red]Press enter to quit[/red]"
)
)
self.result.save(save_to)
self.finished = True

def compose(self) -> Generator:
self.app.title = f"Octo Logo v{__version__}"

yield Header(show_clock=True)
yield Footer()

basic_info_wizard = Wizard(id="basic_info_wizard")
basic_info_wizard.questions = BASIC_INFO_QUESTIONS
basic_info_wizard.title = "Basic Information"
yield basic_info_wizard
yield self.loading_wid


def disable_ansi(ctx: ExtraContext, _: Parameter, val: bool) -> bool:
ctx.color = not val

# We must return the value for the main function no_ansi parameter not to be None
return val
def handle_wizard_cancelled() -> None:
logger.error("Wizard cancelled by user.")
quit(0)


@extra_command(params=[])
@option("-t", "--no-tui", is_flag=True, help="Dont use the Textual Terminal User Interface")
def main(no_tui: bool) -> None:
if no_tui:
# If the tui is disabled, do everything without textual
answers = dict()
app_title = f"Octologo v{__version__}"
wiz1 = Wizard(BASIC_INFO_QUESTIONS, app_title, "Basic Information", disable_tui=no_tui)
answers = wiz1.run()

if answers is None:
handle_wizard_cancelled()
return

style = styles[answers["style"]].module
wiz2 = Wizard(style.questions, app_title, "Specific Information", disable_tui=no_tui)
res2 = wiz2.run()
if res2 is None:
handle_wizard_cancelled()
return

answers.update(inq_ask(BASIC_INFO_QUESTIONS))
answers.update(inq_ask(styles[answers["style"]].module.questions))
answers.update(res2)

style = styles[answers["style"]].module
result = style.get_image(answers)
result_image = style.get_image(answers)

save_to = get_output_filename(answers["name"])
result.save(save_to)
logger.success(f"Image saved to : {save_to}")
else:
# If the tui is enabled, run the textual app
app = OctoLogoApp()
app.run()
quit(0)
save_to = get_output_filename(answers["name"])
result_image.save(save_to)
logger.success(f"Image saved to : {save_to}")
28 changes: 17 additions & 11 deletions src/octologo/styles/underline_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,32 @@
get_text_size,
os,
)
from octologo.wizard import SelectQuestion, TextQuestion
from PIL import Image, ImageColor, ImageDraw, ImageFont
from PIL.Image import Image as ImageClass
from PIL.ImageFont import FreeTypeFont
from textual.validation import Number
from textual_wizard.inputs import Select, Text

questions = [
SelectQuestion(
Select(
"font",
"Select a font",
[(font, font) for font in font_list],
"Iosevka-Nerd-Font-Complete.ttf",
options=[(font, font) for font in font_list],
default_value="Iosevka-Nerd-Font-Complete.ttf",
),
Select("color", "Select a color scheme", options=color_scheme_names, default_value="adi1090x"),
Text("underline_count", "Lettrs to undrline", validators=[Number(minimum=0)], initial_value="1", placeholder="1"),
Text("padding_x", "Padding x (px)", validators=[Number()], initial_value="200", placeholder="200"),
Text("padding_y", "Padding y (px)", validators=[Number()], initial_value="20", placeholder="20"),
Text("gap", "Gap between text and bar (px)", validators=[Number()], initial_value="20", placeholder="20"),
Text("bar_size", "Bar weight (px)", validators=[Number()], initial_value="20", placeholder="20"),
Text(
"additionnal_bar_width",
"Additionnal bar width (px)",
validators=[Number()],
initial_value="20",
placeholder="20",
),
SelectQuestion("color", "Select a color scheme", color_scheme_names, "adi1090x"),
TextQuestion("underline_count", "Lettrs to undrline", [Number(minimum=0)], "1", "1"),
TextQuestion("padding_x", "Padding x (px)", [Number()], "200", "200"),
TextQuestion("padding_y", "Padding y (px)", [Number()], "20", "20"),
TextQuestion("gap", "Gap between text and bar (px)", [Number()], "20", "20"),
TextQuestion("bar_size", "Bar weight (px)", [Number()], "20", "20"),
TextQuestion("additionnal_bar_width", "Additionnal bar width (px)", [Number()], "20", "20"),
]

active = False
Expand Down
2 changes: 1 addition & 1 deletion src/octologo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def get_text_size(text: str, font: FreeTypeFont) -> tuple[int, int]:


def get_font_height(font: FreeTypeFont) -> int:
return font.getbbox("azertyuiopqsdfghjklmwxcvbnAZERTYUIOPQASDFGHJKLMWXCVBN0123456789")[3]
return int(font.getbbox("azertyuiopqsdfghjklmwxcvbnAZERTYUIOPQASDFGHJKLMWXCVBN0123456789")[3])


def remove_ext(filename: str) -> str:
Expand Down
Loading

0 comments on commit ffa3607

Please sign in to comment.