I am writing this at 3 a.m. It's difficult to get sleep when so many good 
things are happening.

This post will summarize what has already been done and what will be done 
in the next few days.  Let's start with the basics...

===== About wrappers and widgets

Wrapper and widget classes are at the heart of Leo's design.  Clarifying 
the relationships between wrappers and widgets started the present 
avalanche.

Here is what you *must know* to understand Leo's core:

1. A **widget** is an actual Qt widget.

Leo's core seldom accesses widgets directly.  Instead...

2. A **wrapper class** defines a standard api that hides the details of the 
underlying gui **text** widgets.

Leo's core uses this api almost exclusively.  That is, Leo's core code 
treats wrappers *as if* they were only text widgets there are!

There is, however, a back door for (hopefully rare!) special cases.  All 
wrapper classes define an official "widget" ivar, so core or plugin code 
can gain access to the real Qt widget using wrapper.widget.  Searching for 
wrapper.widget will (or soon will) find all gui-dependent snippets of code 
in Leo's core.

3. Wrappers are crucial, even if Leo *always* uses the Qt gui.

This has just become crystal clear to me.  Indeed:

- Wrappers allow users to choose different Qt widgets for the body pane, 
without changing *any* of Leo's core code (except the startup code).

- Wrappers allow Leo's text-handling code to remain unchanged regardless of 
whether the body or log panes are a QTextBrowser or a QsciScintilla widget.

- Without wrappers, all of Leo's text-editing commands would have to know 
the details of the api of the actual Qt text widget!

==== Summary of the avalanche

Here is what is happening now in Leo's code base, and in my thinking!

1. A collapse in the complexity of relationships between Leo's most 
important classes and between the files containing those classes.

qt_text.py was supposedly a minor reorg.  Wrong!  It has become the road 
map to the reorganization of all of Leo's fundamental classes:

- qt_text.py is *self contained*: it doesn't depend on any other classes!  

- qt_text.py defines a QTextMixin class.  This is a new kind of class for 
me. As its name suggest, it is not so much a base class as a helper class. 
It provides helper code for all of Leo's Qt wrapper widgets.

- All the classes that use QTextMixin implement the HighLevelInterface 
class in leoFrame.py.

The HighLevelInterface class will soon morph into the WrapperInterface 
class.  Th WrapperInterface class will contain only the definition of 
methods and their docstrings.  Unlike the HighLevelInterface class, the 
WrapperInterface will contain *no* redirection code!

- If we were (heaven forbid) going to change Leo's gui, we would simply use 
a copy of qt_text.py, say new_gui.text.py, as a starting point.

2. All "redirection" code will soon be gone.

Ville, Terry and I will be much happier.  Eliminating this cruft makes 
Leo's code both lighter and thicker, to use the go terminology.  It's 
lighter because there is far less blah, blah, blah.  It's thicker because 
there is less to check and to fail.

It's easy to eliminate redirection. For example, the LeoBody methods that 
just call the corresponding wrapper methods will soon be gone.  We'll 
change::

    c.frame.body.wrapperMethod

to::

    c.frame.body.wrapper.wrapperMethod

Doh!  This should have been done years ago.  Yes, it's more verbose, but 
it's also more explicit.  Furthermore, few such calls actually exist!

3. widget.widget and other type-checking horrors will soon be a thing of 
the past.

New coding conventions will remove all confusion between wrappers and 
widgets.  Wrappers will *always* be called wrappers, never widgets.

There is/was something both fundamentally wrong and confusing about 
widget.widget.  The first widget must have a different type than the 
second, but there is *no* indication of this fundamental fact.  The reorg 
changes widget.widget to wrapper.widget everywhere.

4. I have a new approach toward type checking.

It is obvious now, in retrospect, that clarity of design is something much 
more important than type checking! Leo's code will soon make the types of 
objects *explicit* and this far more important in practice than even 
"perfect" static type checking. This is a revolution in my thinking.

pylint, while supremely important for day-to-day programming, is *totally 
inadequate* to have produced the design changes I have been discussing.

Now that widgets are always clearly distinct from wrappers, we could 
imaging type-checking assertions to verify their apparent types. Imo, such 
assertions are seldom pythonic, but in this case assertions might be good 
documentation, if nothing else.  The assertions would be based on the 
following functions:

- g.isWrapper(obj): calls g.app.gui.isWrapper(obj)
- g.isWidget(obj): calls g.app.gui.isWidget(obj)

5. A dead-easy script shows how to enforce the relationships between types 
that really matter.

This is a big Aha. The script merely compares dir(o1) with dir(o2), where 
o1 is the "template" HighLevelInterface class in leoFrame.py and o2 is one 
of QTextEditWrapper or QScintillaWrapper.  You could call this the "back 
door" approach to type checking.  It's sooo easy!  No more messing with 
ast's!

As a result of running this script, (given in the P.P.S.) I was able to 
removed cruft from both the HighLevelInterface class, and other parts of 
Leo's code.

===== Summary

None of these massive changes adds anything directly to Leo :-)

However, they make understanding and changing Leo's code far easier and 
more pleasant.  Leo's code is now more beautiful.

Imo, these changes are long overdue.  They fundamentally improve Leo's code 
base.

That's all for now.  I'll keep you posted as I change code, but this post 
covers all the essentials.

Edward

P.S. git and easier access to pylint contributed to the present avalanche 
of energy and ideas.

Git has allowed me to revise Leo's source code radically, with complete 
safety.  I would never have done what I have done lately with bzr.

Leo's new pylint command allows me to run pylint *easily* on one or more 
"recent" files, *without* having to change any list.  As a result, I run 
pylint much more often.

P.P.S. Here is the script that checks that all text wrapper classes 
implement all required methods.  This is, to the first approximation, the 
only type checking that Leo really needs.  It will probably become a unit 
test.

----- start -----
import leo.core.leoFrame as leoFrame
import leo.plugins.qt_text as qt_text
from leo.core.leoQt import Qsci,QtWidgets

h = leoFrame.HighLevelInterface(c)
w = Qsci.QsciScintilla()
tw = QtWidgets.QTextBrowser()
q = qt_text.QScintillaWrapper(widget=w,c=c,name='test')
t = qt_text.QTextEditWrapper(widget=tw,name='test2')

h_list = [z for z in sorted(dir(h)) if not z.startswith('__')]
t_list = [z for z in sorted(dir(t)) if not z.startswith('__')]
q_list = [z for z in sorted(dir(q)) if not z.startswith('__')]
ignore = [
    'set_focus', # synonym for setFocus
    'mutable_methods', # To be removed.
]
for z in h_list:
    if z not in t_list and z not in ignore:
        print('QTextEditWrapper: not in HighLevelInterface: %s' % z)
for z in h_list:
    if z not in q_list and z not in ignore:
        print('QScintillaWrapper: not in HighLevelInterface: %s' % z)
print('done')
----- end -----

EKR

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/leo-editor.
For more options, visit https://groups.google.com/d/optout.

Reply via email to