peterbrittain / asciimatics

A cross platform package to do curses-like operations, plus higher level APIs and widgets to create text UIs and ASCII art animations
Apache License 2.0
3.62k stars 237 forks source link

Renderer + Drawing shapes #314

Closed jame-sparker closed 3 years ago

jame-sparker commented 3 years ago

Is your feature request related to a problem? Please describe. I would like to have a FigletText next to my custom shapes. Is this possible? Custom shapes: https://asciimatics.readthedocs.io/en/stable/io.html#drawing-shapes FigletText: https://asciimatics.readthedocs.io/en/stable/asciimatics.html#asciimatics.renderers.FigletText

Describe the solution you'd like Is this possible to perform this outside of the animation routine? (Assuming FigletText is displayed like with Print.)

peterbrittain commented 3 years ago

It's possible with some work...

If you're drawing directly to the Screen (as per the first link) you will also need to print the figlet text direct to the Screen too. Basically, call Figlet directly to get the full multi-line string, break it down into individual lines (by splitting on new lines) and then print each line to the desired location using print_at.

Hope that all makes sense!

jame-sparker commented 3 years ago

Thanks. That makes sense. I guess it doesn't make sense to have a method in Screen to handle this since the print locations are not specified renderers. Does it make sense to have such method for effects?

jame-sparker commented 3 years ago

For future reference. The following code prints the renderer at (10, 20).

for i, row in enumerate(str(renderer).split("\n")):
        screen.print_at(row, 10, 20 + i)
jame-sparker commented 3 years ago

Thanks. That makes sense. I guess it doesn't make sense to have a method in Screen to handle this since the print locations are not specified renderers. Does it make sense to have such method for effects?

Or print a renderer along with x, y coordinates?

peterbrittain commented 3 years ago

I'm not quite sure what you're asking here, so let me try to answer by explaining how you could also do this with new Effects...

The contract for the Effects API is that the Screen will regularly call the update method on each Effect. The Effect then decides what to do to create its effect on the Screen. Typically this will be by calling a load of methods on Screen to update the contents (e.g. print_at, paint, etc).

So to get your shape drawing into this, you'd need a new Effect that does the necessary drawing inside the update method. You can then combine your new Effect with the existing Print Effect to get both drawing and Figlet text in the same Scene.

peterbrittain commented 3 years ago

For future reference. The following code prints the renderer at (10, 20).

for i, row in enumerate(str(renderer).split("\n")):
        screen.print_at(row, 10, 20 + i)

This only prints the text, it throws away all the colour information. If you want to print a renderer at a specific location, use the Print Effect,

jame-sparker commented 3 years ago

I can achieve what I want with screen.print_at. Below is the suggestion for an API update. I was wondering if you could expose update method in Effects and drawEffect method in the screen for low-level IO. Or another suggestion is to add drawRender to the Screen class. The only issue here is that some renders contain color information and it's not consistent.

peterbrittain commented 3 years ago

Sorry, but I don't understand what you're asking for here... There is already an update method in Effects. It's true that there is no drawEffect mathod, but as I said earlier, if all you need is to be able to print a renderer at a specific location, use the existing Print Effect. Check out the various demos for how to use it - e.g. cogs.py.

jame-sparker commented 3 years ago

I was suggesting rendering methods for Effects class outside of screen.play or asyncio.get_event_loop(). Currently, it seems like it is not possible to render Effects on the same level as screen.draw. It is totally understandable if this does not align with your API design.

peterbrittain commented 3 years ago

Yeah - that breaks the existing object model as the whole point of the Effect API is that it is the heart of the animation design. Taking it out of there will have all sorts of unintended consequences.

That said, the method that you want to be able to draw a renderer is Screen.paint. The code is slightly clunky, though, owing to when the various pieces were added to asciimatics. The logic is here: https://github.com/peterbrittain/asciimatics/blob/1fca1417f46abce910f15b1aa6c5721b268139b5/asciimatics/effects.py#L343-L349

There's no reason that couldn't be added as a new method to the Screen object, though.

jame-sparker commented 3 years ago

Thanks for taking a look into this! Feel free to close it.

peterbrittain commented 3 years ago

Sure. If you find that you need the above code as a new method on the Screen, feel free to submit an MR.