after looking over VR3's
ViewRenderedProvider3 class, and the
TestPaneProvider class in Terry Brown's example of putting up an additional pane (see
https://leoeditor.com/FAQ.html#how-can-i-create-a-pane-for-matplotlib-charts), it seems to me that Leo should be providing a basic or generic class that provides enough basic splitter methods that many people will be able to use it as is. Of course, it could be subclassed to add functionality.
This would let Leo script writers avoid indulging in cargo cult programming, since most of us don't understand how the nested splitter machinery works. I have taken a shot at this, and even though I know that it needs at least a few more methods. Maybe there already is such a class, but at least this one is simpler.
Here is the code for this experimental class, followed by a little example code that shows how it can be used to install a QWebView widget -
# Adapted from Terry Brown's example in the FAQ at
# https://leoeditor.com/FAQ.html#how-can-i-create-a-pane-for-matplotlib-charts
'''
A script that adds a QT Widget pane to Leo.
After running this script, right click on one of the pane dividers and
select Insert. A new pane with a button 'Action' appears. Click it, and
select "Add test panel" from the context menu.
Alternatively, right click on a pane divider and click on
"Open Window/Add Test Pane". This will open a free-floating window
with this test panel.
'''
class Generic_Ns_Provider:
def __init__(self, c, widget_type, ns_name, ns_id, menu_label):
"""Create a Generic Namespace Provider object.
PARAMETERS
c -- the commander for a Leo outline.
widget_type -- the type of widget to be installed (e.g.,
QtWebKitWidgets.QWebView).
ns_name -- used by the nested splitter mechanism to identify
this panel.
ns_id -- used by register_provider() to unregister previously registered
providers of the same service. Also used to compute the
object name of the widget instance.
menu_label -- a string used by the splitter context menu to display
the menu entry for this panel.
RETURNS
nothing
"""
self.c = c
self.widget_type = widget_type
self.id = ns_id
self.ns_name = ns_name
self.menu_label = menu_label
if hasattr(c, 'free_layout'):
splitter = c.free_layout.get_top_splitter()
if splitter:
splitter.register_provider(self)
def ns_provides(self):
return[(self.menu_label, self.ns_name)]
def ns_provide(self, id_):
if id_ == self.ns_name:
c = self.c
widget = self.widget_type()
setattr(c, Generic_Ns_Provider.get_widget_name(self.id), widget)
return widget
def ns_provider_id(self):
"""used by register_provider() to unregister previously registered
providers of the same service
(provider ID is not the same as the service id_ above)
"""
return self.id
@staticmethod
def get_widget_name(id):
return f'{id}_widget'
# Example: connect to splitter apparatus
from leo.core.leoQt import QtWebKitWidgets
QWebView = QtWebKitWidgets.QWebView
NS_ID = '_generic_pane'
NS_NAME = '_add_generic_pane'
MENU_LABEL = 'Add generic panel'
HTML = '''<body><h2>This is a test</h2></body>'''
wn = Generic_Ns_Provider.get_widget_name(NS_ID)
if not hasattr(c, wn):
Generic_Ns_Provider(c, QWebView, NS_NAME, NS_ID, MENU_LABEL)
if hasattr(c, wn):
w = getattr(c, wn)
w.setHtml(HTML)
Paste this into a node, then <CNTRL-b>. Nothing will seem to happen, but the widget will be registered with the nested splitter machinery. Until you activate this machinery, the widget won't actually be instantiated. Now right-click on a divider bar and select Insert. A new pane will open with a button Action. Click Action and click on
Add Generic Panel. The panel will turn blank - The QWebView object has been instantiated but has nothing to display yet. Now click back in the node with the code and do <CTRL-b> again. This will run the code at the bottom, which will discover that the widget exists and write a bit of HTML to it.
If you do a similar procedure but select Open Window/add generic panel, A blank free-floating window will open. Again do <CNTRL-b> in the code, and the HTML will show up in this new free-floating window.
(It is left as an exercise to get rid of these panes!).
The main things demonstrated here are how easy it can be to get a new pane installed and opened, and how this approach avoids the cargo-cult programming of the past. You can focus on the things that count, such as the type of widget, the menu label by which it will be activated, and a simple way to get the name by which it can be addressed.
SInce my own knowledge is not far removed from the cargo-cult stage, other people's input would be very helpful!