Coverage for src / ezqt_widgets / cli / commands / _demo.py: 0.00%
151 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-01 22:46 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-01 22:46 +0000
1# ///////////////////////////////////////////////////////////////
2# CLI Demo Commands
3# Project: ezqt_widgets
4# ///////////////////////////////////////////////////////////////
6"""
7CLI commands for running and listing demo examples.
8"""
10from __future__ import annotations
12# ///////////////////////////////////////////////////////////////
13# IMPORTS
14# ///////////////////////////////////////////////////////////////
15# Standard library imports
16import os
17import runpy
18import sys
19from pathlib import Path
21# Third-party imports
22import click
24# ///////////////////////////////////////////////////////////////
25# CLASSES
26# ///////////////////////////////////////////////////////////////
29class ExampleRunner:
30 """Handles running EzQt Widgets examples.
32 Provides functionality to discover, list, and execute example files
33 from the EzQt Widgets package.
35 Args:
36 verbose: Whether to enable verbose output (default: False).
37 """
39 # ///////////////////////////////////////////////////////////////
40 # INIT
41 # ///////////////////////////////////////////////////////////////
43 def __init__(self, verbose: bool = False) -> None:
44 """Initialize the example runner."""
45 self.verbose: bool = verbose
46 self.examples_dir: Path = self._find_examples_dir()
48 # ------------------------------------------------
49 # PRIVATE METHODS
50 # ------------------------------------------------
52 def _find_examples_dir(self) -> Path:
53 """Find the examples directory relative to the package.
55 Returns:
56 Path to the examples directory.
58 Raises:
59 FileNotFoundError: If the examples directory cannot be found.
60 """
61 # First priority: examples in the project root
62 package_dir = Path(__file__).parent.parent.parent
63 examples_dir = package_dir / "examples"
65 if examples_dir.exists():
66 return examples_dir
68 # Second priority: examples inside the package (ezqt_widgets/examples/)
69 package_examples = Path(__file__).parent.parent / "examples"
70 if package_examples.exists():
71 return package_examples
73 # Fallback: try to find examples in the current directory
74 current_examples = Path.cwd() / "examples"
75 if current_examples.exists():
76 return current_examples
78 raise FileNotFoundError("Examples directory not found")
80 def _execute_example(self, example_path: Path) -> bool:
81 """Execute a specific example file.
83 Args:
84 example_path: Path to the example file to execute.
86 Returns:
87 True if execution was successful, False otherwise.
88 """
89 if self.verbose:
90 click.echo(f"🚀 Running: {example_path.name}")
92 try:
93 # Change to the examples directory to ensure relative imports work
94 original_cwd = Path.cwd()
95 original_argv = sys.argv[:]
96 original_sys_path = sys.path.copy()
97 os.chdir(example_path.parent)
98 sys.argv = [str(example_path)]
99 example_dir = str(example_path.parent)
100 if example_dir not in sys.path:
101 sys.path.insert(0, example_dir)
103 runpy.run_path(str(example_path), run_name="__main__")
104 return True
106 except SystemExit as exc:
107 code = exc.code if isinstance(exc.code, int) else 1
108 if code != 0:
109 click.echo(f"❌ Error running {example_path.name}: exit {code}")
110 return False
111 return True
112 except Exception as e:
113 click.echo(f"❌ Exception running {example_path.name}: {e}")
114 return False
115 finally:
116 os.chdir(original_cwd)
117 sys.argv = original_argv
118 sys.path = original_sys_path
120 # ///////////////////////////////////////////////////////////////
121 # PUBLIC METHODS
122 # ///////////////////////////////////////////////////////////////
124 def get_available_examples(self) -> list[Path]:
125 """Get list of available example files.
127 Returns:
128 List of paths to available example files.
129 """
130 examples: list[Path] = []
131 for pattern in ["_*.py", "run_all_examples.py"]:
132 examples.extend(self.examples_dir.glob(pattern))
133 return sorted(examples)
135 def run_example(self, example_name: str) -> bool:
136 """Run a specific example by name.
138 Args:
139 example_name: Name of the example to run (without .py extension).
141 Returns:
142 True if execution was successful, False otherwise.
143 """
144 normalized_name = example_name.removesuffix(".py")
145 candidates = (
146 [normalized_name, normalized_name.lstrip("_")]
147 if normalized_name.startswith("_")
148 else [normalized_name, f"_{normalized_name}"]
149 )
151 for candidate in candidates:
152 example_path = self.examples_dir / f"{candidate}.py"
153 if example_path.exists():
154 return self._execute_example(example_path)
156 click.echo(f"❌ Example not found: {example_name}")
157 return False
159 def run_all_examples(self, use_gui_launcher: bool = True) -> bool:
160 """Run all examples or use the GUI launcher.
162 Args:
163 use_gui_launcher: Whether to use the GUI launcher if available
164 (default: True).
166 Returns:
167 True if all examples ran successfully, False otherwise.
168 """
169 if use_gui_launcher:
170 launcher_path = self.examples_dir / "run_all_examples.py"
171 if launcher_path.exists():
172 return self._execute_example(launcher_path)
173 click.echo("⚠️ GUI launcher not found, running examples sequentially")
174 use_gui_launcher = False
176 # Run each example sequentially
177 examples = [
178 "_button",
179 "_input",
180 "_label",
181 "_misc",
182 ]
183 success_count = 0
185 for example in examples:
186 click.echo(f"\n{'=' * 50}")
187 click.echo(f"🚀 Running: {example}")
188 click.echo(f"{'=' * 50}")
190 if self.run_example(example):
191 success_count += 1
192 else:
193 click.echo(f"❌ Failed to run: {example}")
195 click.echo(f"\n✅ Successfully ran {success_count}/{len(examples)} examples")
196 return success_count == len(examples)
198 def list_examples(self) -> None:
199 """List all available examples."""
200 examples = self.get_available_examples()
202 if not examples:
203 click.echo("❌ No examples found")
204 return
206 click.echo("📋 Available examples:")
207 click.echo("=" * 40)
209 for example in examples:
210 status = "✅" if example.exists() else "❌"
211 click.echo(f"{status} {example.stem}")
213 click.echo(f"\nTotal: {len(examples)} examples found")
216# ///////////////////////////////////////////////////////////////
217# PUBLIC FUNCTIONS
218# ///////////////////////////////////////////////////////////////
221def run_example_by_category(category: str, verbose: bool = False) -> bool:
222 """Run examples by category.
224 Args:
225 category: Category name (buttons, inputs, labels, misc).
226 verbose: Whether to enable verbose output (default: False).
228 Returns:
229 True if execution was successful, False otherwise.
230 """
231 runner = ExampleRunner(verbose)
233 category_mapping = {
234 "buttons": "_button",
235 "inputs": "_input",
236 "labels": "_label",
237 "misc": "_misc",
238 }
240 if category not in category_mapping:
241 click.echo(f"❌ Unknown category: {category}")
242 click.echo(f"Available categories: {', '.join(category_mapping.keys())}")
243 return False
245 return runner.run_example(category_mapping[category])
248def run_all_examples(use_gui: bool = True, verbose: bool = False) -> bool:
249 """Run all examples.
251 Args:
252 use_gui: Whether to use the GUI launcher if available (default: True).
253 verbose: Whether to enable verbose output (default: False).
255 Returns:
256 True if all examples ran successfully, False otherwise.
257 """
258 runner = ExampleRunner(verbose)
259 return runner.run_all_examples(use_gui)
262def list_available_examples() -> None:
263 """List all available examples."""
264 runner = ExampleRunner()
265 runner.list_examples()
268# ///////////////////////////////////////////////////////////////
269# COMMANDS
270# ///////////////////////////////////////////////////////////////
273@click.group(name="demo", help="Run and list demo examples")
274def demo_group() -> None:
275 """Demo command group."""
278@demo_group.command(name="run", help="Run widget examples")
279@click.option(
280 "--all", "-a", "run_all", is_flag=True, help="Run all examples with GUI launcher"
281)
282@click.option(
283 "--buttons",
284 "-b",
285 is_flag=True,
286 help="Run button examples (DateButton, IconButton, LoaderButton)",
287)
288@click.option(
289 "--inputs",
290 "-i",
291 is_flag=True,
292 help="Run input examples (AutoComplete, Password, Search, TabReplace)",
293)
294@click.option(
295 "--labels",
296 "-l",
297 is_flag=True,
298 help="Run label examples (ClickableTag, Framed, Hover, Indicator)",
299)
300@click.option(
301 "--misc",
302 "-m",
303 is_flag=True,
304 help="Run misc examples (CircularTimer, DraggableList, OptionSelector, ToggleIcon, ToggleSwitch)",
305)
306@click.option(
307 "--verbose", "-v", is_flag=True, help="Verbose output with detailed information"
308)
309def run_command(
310 run_all: bool,
311 buttons: bool,
312 inputs: bool,
313 labels: bool,
314 misc: bool,
315 verbose: bool,
316) -> None:
317 """Run EzQt Widgets examples."""
318 options_selected = any([run_all, buttons, inputs, labels, misc])
320 if not options_selected:
321 click.echo("❌ Please specify which examples to run.")
322 click.echo("\n📋 Available options:")
323 click.echo(" --all, -a Run all examples with GUI launcher")
324 click.echo(" --buttons, -b Run button examples")
325 click.echo(" --inputs, -i Run input examples")
326 click.echo(" --labels, -l Run label examples")
327 click.echo(" --misc, -m Run misc examples")
328 click.echo("\n💡 Example: ezqt run --buttons")
329 return
331 if verbose:
332 click.echo("🔍 Verbose mode enabled")
334 success = True
336 if run_all:
337 click.echo("🎯 Running all examples...")
338 success = run_all_examples(use_gui=True, verbose=verbose)
340 elif buttons:
341 click.echo("🎛️ Running button examples...")
342 success = run_example_by_category("buttons", verbose)
344 elif inputs:
345 click.echo("⌨️ Running input examples...")
346 success = run_example_by_category("inputs", verbose)
348 elif labels:
349 click.echo("🏷️ Running label examples...")
350 success = run_example_by_category("labels", verbose)
352 elif misc:
353 click.echo("🔧 Running misc examples...")
354 success = run_example_by_category("misc", verbose)
356 if success:
357 click.echo("✅ Examples completed successfully!")
358 else:
359 click.echo("❌ Some examples failed to run.")
360 raise SystemExit(1)
363@demo_group.command(name="list", help="List available examples")
364def list_command() -> None:
365 """List available examples."""
366 list_available_examples()