[share]Interactive MatplotLib backend
I had the idea to make a custom backend for matplotlib, to allow pythonista to be used with pylab as an interactive matlab of sorts. I found the show() functionality which dumped things to a console image to be hard to work with.
Currently this only works in the python 2.7 interpreter, due to a problem with the included matplotlib on py3 (subject of a future bug report ), but it is otherwise reasonably functional.
There is also a problem due to global clearing, which I have not yet found a workaround, other than the slow force reloading in the example code.
This has live figure windows, which can be moved , resized or minimized. These live inside the pythonista root window, so can be seen in the editor or console, and do not steal focus from the keyboard. Plots are updated interactively in real time for most operations.
Lol, better work on battery re-charge also 😁😱
But on a serious note, regarding getting your repo onto my ipad is really nice now since @ccc appex code to copy from working copy to Pythonista.
I know when you are good, you can clone, write scripts etc...
Anyway, it's just somewhat related to this. If it's easy to do and remember more people will download code to look at.
But I like the Family Resemblance window....
@JonB , the overlay class is really nice. I would call it a palette, but each to there own. You have seen to specifically talk about MatplotLib here.
But was your intention that it could be just used as any other view in making a ui.
The below, I chopped up somethings quickly to see how it would work. Maybe I should have just used a ui.TableView instead of a custom ui.View class. But I think often it's a convenient programming container.
But the list appeared to work ok(I changed another setting in the code somewhere to receive events and hard coded the name)
But was not meant to be an exhaustive test, just wanted to see how I could use it.
In this case the bottom resizing arrow didn't work. But it's understandable, I didn't even try. Could be a simple thing
class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.make_view() def make_view(self): tbl = ui.TableView(frame=self.bounds) tbl.data_source = ui.ListDataSource(items = range(0, 100)) tbl.flex = 'wh' self.add_subview(tbl) if __name__=='__main__': f=(0,0,490,490) mc = MyClass(frame = f) #i=ui.ImageView(frame=(0,0,490,490)) #i.image=ui.Image.named('test:Mandrill') #i.name='Family resemblance' #i.alpha=1 o=Overlay(content=mc) o.content_view.border_width=2 #i.border_width=1 #i.content_mode=ui.CONTENT_SCALE_ASPECT_FIT
thanks. yes my intention was to try to have something that could be useful for ther things. (eventually I am thinking about dockable toolbars, that can have submenus that fly in and out from screen edge -- which could be used as a replacement for the defunct sidebar present mode)
I was somewhat lazy in the touch handling code. All dragging, including the resizing, is handled by the top view touch handling code. If the content view is enabled, you will need to drag from the title bar. I need to give the resize arrow its own touch handling.
As @Webmaster4o has pointed out, the menu bar does not jive well with ios ui philosophy. I think a title bar may still be needed, since it needs to contain the figure title, but perhaps something akin to safari where the title shrinks down when not in use, and a tap opens up a larger menu ( which could eventually include other matplot specific toolbar items, such as data select, delete, data cursor, as well as docking/resize presets)
On the matplotlib side, I am also not particularly happy with the update rate performance, and am rethinking the entire renderer approach. Right now, each time you do anything to the figure, it has to rerender the entire thing, that means drawing out each little line segment, marker, etc get drawn into a bitmap. I am thinking it would be possible to use UIBezierPath's that get created and cached, and make use of ios transforms rather than matplotlib's transforms, and only get regenerated when the data changes. The matplotlib backend api is pretty poorly documented, (and no other backends take advantage of such path caching), making this an annoying exercise in trial and error.
@JonB , 0k. I was just coming here to make another comment.
You have this code,with a comment
@classmethod def detail(cls): '''detail, a.k.a. editor panel. dragging does not work yet until gesture recognizer is added''' return cls
But in my example above i am running in py3, the TableView scrolls, I can swipe to the console or help panel, the palette is still there and appears to behaving itself.
I did change the below attr to True, to get the list to scroll
self.content_view.touch_enabled=True, it was False
So I am not sure what is not working?
Edit: all the underlying screens working as expected.
For content views which do not have PanGestureRecognizers, which would be anything not in a scrollview or tableview, ui.Views do not take ownership of touches that occur in their bounds, and any type of touch motion gets stolen by the PA2SlidingPanel container. This has long been a complaint of mine ( presenting a custom view in a
panelbreaks any custom touch handling that is achieved by simple touch_moved), but using Gestures.py, it is possible to install a gesture recognizer that prevents the PA2SlidingPanel container from recognizing swipes if they are recognized by the custom view.
You can see the problem if you set the parent to Windows.detail in the original example, and try dragging the window left or right -- you get one move, then the pythonista panel starts sliding.
For content that use pans, the the only way to move would be through the title bar( another reason to not completely ditch the title bar), though perhaps using something like long press to switch between the content getting touch events, and the Overlay container getting the events.... Not sure. Floating interactive windows are not a concept used by iOS, (the ios9 PiP content is not interactive for example) so I don't know that anyone has thought through how they should work.
@JonB , sorry I dint really figure out how to make the change you pointed out to see the problem with panning.
If not too much trouble could you just write the change.
I have the original ready to try on.
I think I get what you are saying though. Still like to see it
@JonB , but anyway I really like how it fits in with minimal code. I realize you are not finished. But I really like when you can do something like the below. Meaning it seems natural to how things already work.
Getting the name from the content needs some help though. If content.name is None, error expects a string. Small thing I know, just let you know.
But one line and your overlay works in a normal senerio. That's great
class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.overlay = Overlay(content=self) self.make_view() def make_view(self): tbl = ui.TableView(frame=self.bounds) tbl.height -= 44 tbl.data_source = ui.ListDataSource(items = range(0, 100)) tbl.flex = 'wh' self.add_subview(tbl) if __name__=='__main__': f=(0,0,600, 800) mc = MyClass( name = 'My Table', frame = f)
@JonB , sorry, don't mean to bombard you post with senseless code. But this just seemed to small to put into a gist.
Anyway, the below is more inline with your original example about and fitting it into a so called normal use as I think of it anyway.
I thought It would be nice to see what happened when drawing into the content view. You would hope it all just works as it does. But I guess a lot is going on. Also nice to see that the alpha is working all the way back to underlying Windows.
class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.content_mode=ui.CONTENT_SCALE_ASPECT_FIT self.overlay = Overlay(content=self) self.overlay.content_view.touch_enabled = False def draw(self): w = self.width if self.width < self.height else self.height r = ui.Rect(0, 0, w, w).inset(5, 5) r.center(self.center) s = ui.Path.oval(*r) ui.set_color((255, 0, 0, .4)) s.fill() if __name__=='__main__': f=(0, 0, 300, 400) mc = MyClass( name = 'My Red Cirlce', frame = f)
@JonB, I don't know if this special or not. But I had both your original version running and my modified version running at the same time. So 2 different apps running at the same time. The overlays still working normally. Maybe you expected that. I launched the other version by mistake. That's pretty cool
Actually there is a third app running also. I was replying to the post in an app that is in a panel.
(in the latest github version)
@JonB , are you still refining the overlay class? Not sure about the real world use cases for it. But I think one real world use case is when you are making tools for ui. Look, I call it a floating palette, but toolbar or whatever. But can be be very handy if you want a bunch of cmds to act on a view without having to incorporate the interface into the view.
Just saying you might be more inclined to refine it in a certain direction if you can see a real world use for it.
Same idea as below...
yes that is a good idea. i probably ought to revive editmenu as an overlay...
@JonB , I really think it's a great idea. Granted it's not a normal iOS interface element. But many other uses also I suspect other than a floating toolbar/editmenu. Realtime property sheet of the current view for example. But it does something that is very unique. It can float and it's not modal.