-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Request: Integration with Package "Rich" #26
Comments
Hi @whisller, I have an easy reply: I have no idea :-) I was not aware of that package, which seems really cool actually. I am not even sure if it is a good idea and how to do it. If there are other request or someone is interesting in integrating it, why not? :-) Is there any reason in particular you think it would be a good idea? Thanks for your message, |
So generally I am super interested in integrating those two libraries. For two reasons, Problem that I noticed when tried to make those two to work sadly is...it doesn't work ;) I believe there would have to be some sort of wrapper that would handle block sizes, resize, same as format that is accepted by In my opinion that integration could boost both libraries, as that's something which is missing in python environment. |
I would be interested in helping, if it won't be a massive work. So ye I am available. but keep in mind i know nothing about the internal architecture of For now one thing that could help if you haven't seen already is the Let me know, please, or write me privately at |
Author of Rich here. I think this is a fantastic idea, and I would be happy to help! |
@piccolomo @willmcgugan awesome! I think this integration would benefit greatly both sides :) I will try to find some time to sit and investigate, but realistically speaking it will not be earlier than after few weeks from now (most likely I will be with my partner on labour ward in next few hours/days ;)). |
That was one of the few excuses I would accept. ;-) Congratulations in advance, @whisller ! |
let's do it then! [obviously after @whisller fatherhood priorities :-) ] |
@willmcgugan @piccolomo I have first "working" draft, at least plot is being semi displayed ;) from time import sleep
from rich.console import Console
from rich.console import ConsoleOptions, RenderResult
from rich.jupyter import JupyterMixin
from rich.layout import Layout
import plotext as plt
from rich.live import Live
from rich.panel import Panel
from rich.segment import Segment
class PlotextIntegration(JupyterMixin):
def __init__(self, figure):
self._figure = figure
def __rich_console__(
self, console: Console, options: ConsoleOptions
) -> RenderResult:
for row in self._figure.subplot.matrix:
line = ""
for character in row:
line += character[0]
yield Segment(line)
yield Segment.line()
def make_layout():
layout = Layout(name="root")
layout.split(
Layout(name="header", size=3),
Layout(name="main", ratio=1),
)
layout["main"].split_row(
Layout(name="main_left"),
Layout(name="main_right"),
)
return layout
def make_plot():
plt.scatter(plt.sin(100, 3))
plt.plotsize(50, 50)
plt.title("Plot Example")
plt.show(True)
from plotext.plot import _fig
return _fig
layout = make_layout()
layout["main_left"].update(Panel(PlotextIntegration(make_plot())))
with Live(layout, refresh_per_second=0.1) as live:
while True:
sleep(0.1) Few questions/obstacles that come to my mind:
|
@piccolomo it seems that design of plotext was not intended to display multiple charts on single window. E.g. shared access to |
Hi @whisller, thanks a lot for the message. I have been looking at your example and I have these considerations:
from time import sleep
from rich.console import Console
from rich.console import ConsoleOptions, RenderResult
from rich.jupyter import JupyterMixin
from rich.layout import Layout
import plotext as plt
from rich.live import Live
from rich.panel import Panel
from rich.segment import Segment
class PlotextIntegration(JupyterMixin):
def __init__(self, canvas):
self.canvas = canvas
def __rich_console__(
self, console: Console, options: ConsoleOptions
) -> RenderResult:
lines = self.canvas.split('\n')
for row in lines:
yield Segment(row)
yield Segment.line()
def make_layout():
layout = Layout(name="root")
layout.split(
Layout(name="header", size=3),
Layout(name="main", ratio=1),
)
layout["main"].split_row(
Layout(name="main_left"),
Layout(name="main_right"),
)
return layout
def make_plot():
plt.scatter(plt.sin(100, 3))
plt.plotsize(50, 50)
plt.title("Plotext - Rich Integration Test")
plt.cls()
plt.show(hide = True)
return plt.get_canvas()
layout = make_layout()
layout["main_left"].update(Panel(PlotextIntegration(make_plot())))
with Live(layout, refresh_per_second=0.1) as live:
while True:
sleep(0.1)
All the best |
With regards to coloring, you could modify your code to generate Segment instances rather than string with ansi codes, but that would be a lot of work and you wouldn't want to break the non-Rich functionality. There is a non-documented AnsiDecoder class that may help. If you construct an AnsiDecoder you can feed it strings with ansi codes and it will yield Text instances which you can print with Rich. If you can't calculate the size in advance you could measure the Text objects that come back from the ansi decoder. Putting that together, something along these lines might just be enough (untested): from rich.ansi import AnsiDecoder
from rich.console import RenderGroup
from rich.measure import Measurement
class PlotextIntegration(JupyterMixin):
def __init__(self, canvas) -> None:
decoder = AnsiDecoder()
self.rich_canvas = RenderGroup(*decoder.decode(canvas))
def __rich_(self):
return self.rich_canvas
def __rich_measure__(self, console, options) -> Measurement:
return Measurement.get(console, options, self.rich_canvas) |
Hi @willmcgugan , thanks for reply. Any idea on how to get any layout size (width and height)? |
You may be better off with a Table there. Tables will adapt to their contents, so all you would need is that |
Hi @whisller and @willmcgugan , I have successfully plotted, in colors and with size adaptation, using from rich.layout import Layout
from rich.jupyter import JupyterMixin
import plotext as plt
from rich.ansi import AnsiDecoder
from rich.console import RenderGroup
from rich.live import Live
from time import sleep
def make_layout():
layout = Layout(name="root")
layout.split(
Layout(name="header", size=3),
Layout(name="main", ratio=1),
)
layout["main"].split_row(
Layout(name="plotext"),
Layout(name="main_right"),
)
return layout
class PlotextIntegration(JupyterMixin):
def __init__(self, canvas):
decoder = AnsiDecoder()
self.rich_canvas = RenderGroup(*decoder.decode(canvas))
def __rich_console__(self, console, options):
yield self.rich_canvas
def make_plot():
plt.clf()
plt.scatter(plt.sin(1000, 3))
plt.plotsize(100, 54)
plt.title("Plotext Integration in Rich - Test")
plt.show(hide = True)
from plotext.plot import _fig
size = _fig.width, _fig.height
return plt.get_canvas(), size
layout = make_layout()
plotext_layout = layout["plotext"]
canvas, size = make_plot()
plti = PlotextIntegration(canvas)
plotext_layout.update(plti)
plotext_layout.size, plotext_layout.height = size
with Live(layout, refresh_per_second=0.1) as live:
while True:
sleep(0.1) which produces the following output: The only minor issue I may need to resolve in the next version is to get the figure size without the convoluted method of importing |
@piccolomo nice one!
Yeah, that was my worry as well. @willmcgugan are there any methods that integration can implement to be called during render/post render that could be used to set width/height of plotext instance? Something that could allow dynamically change the size of it when window is resized. It seems that if we would tackle things from below list we should be fine:
Thank you guys for the effort! |
Would this also integrate then with textual, which would be amazing? |
@henryiii It would! Which is why I'm excited about this project. @piccolomo @whisller You would need to add a So the measure method would be something like this: def __rich_measure__(self, console, options):
return Measurement(20, console.width) Now in How you want to calculate the dimensions is up to you. You might want to add it to the constructor so the developer can set it explicitly. For instance if you set plot_width in the constructor your measure method could be this: def __rich_measure__(self, console, options):
return Measurement(self.plot_width, self.plot_height) There are a lot of examples in the Rich source. panel.py may be a good place to start. Hope that helps! |
@willmcgugan I tried without success to follow your last recommendations. I actually think it may not be possible to let the plot dimension adapt to the layout (while the opposite is possible as I have shown) because the layout dimension seem to be calculated only when already printed or at least after the layout receives an update with some content (with a given dimension like a I also tried to modify the Could the example I have shown before be enough? Optionally one could add a |
What I was think was is that you render the plot inside def __rich_console__(self, console, options):
...
plt.plotsize(options.max_width, options.height or console.height) That way it would be the Layout class that specifies the size of the plot, and it would adapt with the size of the terminal.
That shouldn't be required. Do you have an exception or error message ? |
Hi @willmcgugan, I will try to implement your suggestion. In the meantime I am trying to change the from rich.layout import Layout
from rich.live import Live
from time import sleep
class myLayout(Layout):
def __init__(self):
super().__init__()
def __rich_console__(self, console, options):
super().__rich_console__()
layout = myLayout()
with Live(layout, refresh_per_second=0.1) as live:
while True:
sleep(0.1) and the error:
which seems to be due to the |
I think the exception may be swallowed by the stdout / stderr capturing there. Your renderable is broken. You forgot to pass the console, and options args through, and you need to return the result. Easy fix: def __rich_console__(self, console, options):
return super().__rich_console__(console, options) |
And Ta Daaa: from rich.layout import Layout
from rich.live import Live
from rich.ansi import AnsiDecoder
from rich.console import RenderGroup
from rich.jupyter import JupyterMixin
from rich.panel import Panel
from time import sleep
import plotext as plt
def make_plot(*size):
plt.clf()
plt.scatter(plt.sin(1000, 3))
plt.plotsize(*size)
plt.title("Plotext Integration in Rich - Test")
return plt.build()
class plotextMixin(JupyterMixin):
def __init__(self):
self.decoder = AnsiDecoder()
def __rich_console__(self, console, options):
self.width = options.max_width or console.width
self.height = options.height or console.height
canvas = make_plot(self.width, self.height)
self.rich_canvas = RenderGroup(*self.decoder.decode(canvas))
yield self.rich_canvas
def make_layout():
layout = Layout(name="root")
layout.split(
Layout(name="header", size=3),
Layout(name="main", ratio=1),
)
layout["main"].split_row(
Layout(name="plotext", size=120),
Layout(name="main_right"),
)
return layout
layout = make_layout()
plotext_layout = layout["plotext"]
mix = plotextMixin()
mix = Panel(mix)
plotext_layout.update(mix)
with Live(layout, refresh_per_second=0.1) as live:
while True:
sleep(0.1) In this example it is the Indeed modifying the Thanks @willmcgugan and @whisller for the kind help, and if you want to add any final magical touch (if not more serious corrections) feel free to update. |
I close the issue, but feel free to reopen it, in case there were errors in the code or other things to add for integration. |
Hi @whisller, do you think there are other related issues to solve before closing? |
Hello, I tried to use your example with the latest version of plotext (4.1.2), but the get_canvas function appears to have removed (or renamed?):
Edit: I saw in the release notes that The example mostly works again, but the |
Hi @randerzander , thanks a lot for the message: , I updated the previous code using the function For next version, I was thinking of publishing a guide |
Was wondering how easy/hard would be to integrate your project with rich : >
The text was updated successfully, but these errors were encountered: