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.