> There is one enhancement, that I don't know how to do myself: I don't
> what to upload the files, that weren't changed since the last upload,
> so I need some kind of DB table, that will have this structure:
>
> | local file name | server file name | last modify date time |
>
> If last modify has changed, then upload. Now I have this simple DB as
> a matrix in code - files array. What is the best way to implement this
> feature in python inside leo? I don't think, that Mysql or Oracle
> would be good choice for this. :)))
I'd probably use sqlite, which comes with python since 2.5. Otherwise if there's not too many files a pickled dictionary would work, but sqlite is probably easier.
Cheers -Terry
> I decided to store the DB in an external file, called files.txt in
> JSON format.
>
[code snipped]
Couldn't see where in the code you read the info. in, so it seemed you were just overwriting the info. each time - was that the intent?
Given a Leo node `nd` you can read and write JSON to it with:
files = json.loads(nd.b)
and
nd.b = json.dumps(files)
(loads and dumps are the *s*tring versions of their file counterparts)
Minor picky thing, built in open() returns a python file object, not a operating system file-handle, so calling it 'FH' potentially confusing. os.open() returns file handles, I think, but you probably need python file objects or at least something with a .write() method, so your code's correct but the name's odd.
Cheers -Terry
So the goal is to make Leo log in to an ftp site and upload the derived files in the outline which have changed to that site?
Personally I use the linux command line tool `sitecopy` for this sort of thing.
But if you want to make Leo do it, there's a few considerations.
Does it need to be a plugin, or would a @button node be sufficient? It would probably be simpler to get it working in a @button node first, anyway.
I don't know I'd store the config data in JSON, although I guess it wouldn't look so bad if you used the pretty print mode to save it back into the body text.
You probably want a c.save() in there somewhere to make sure the derived files on disk are current.
And of course you need to scan the outline to fine a list of all the derived files, that's probably not too hard, although off the top of my head I'm not sure what the best way to ID them without listing the directive names (file, auto, shadow, edit) yourself is.
So really the fiddly stuff is in the plugin / button creation part, which can be avoided with a @button node. But there are plenty of plugins that do that sort of thing, which you could use as examples. The nav_qt plugin (on by default) is one example, although maybe not the best one, as it adds icon buttons, not regular text buttons.
Also, onCreate's only called one on a commander, so you shouldn't need to worry about the button already being created. I think following examples in other plugins will cover most of what you need.
Cheers -Terry
> * @data ftp
> here we have AJAX text, that must be parsed
> * node 3
>
> Can you tell me, what functions to use to find the node "@data ftp"
> and read it's contents?
>
> "It would probably be simpler to get it working in a @button node
> first, anyway."
>
> I made it work with @button node and wanna make it more generic.
p = g.findTopLevelNode(c, '@data ftp')
body content of p is p.b
there's also g.findNodeAnywhere(c, '@data ftp')
Cheers -Terry
> Ville, can you tell me those 2 functions: for searching of the node
> and getting it's value. I am stuck with the development because of
c.find_h
c.find_b
You can find examples in leo source code.
--
Ville M. Vainio @@ Forum Nokia
> Can you tell me, how to create a button on the top panel inside a
> plugin and attach a code to it?
Take a look at makeButtons function in nav_qt.py
If you don't want icons but text, instead of:
QtGui.QAction(icon_l, 'prev', ib_w)
do:
QtGui.QAction('prev', ib_w)
I'm pasting the whole function here for reference
def makeButtons(self):
ib_w = self.c.frame.iconBar.w
if not ib_w: return # EKR: can be None when unit testing.
icon_l = ib_w.style().standardIcon(QtGui.QStyle.SP_ArrowLeft)
icon_r = ib_w.style().standardIcon(QtGui.QStyle.SP_ArrowRight)
act_l = QtGui.QAction(icon_l, 'prev', ib_w)
act_r = QtGui.QAction(icon_r, 'next', ib_w)
act_l.connect(act_l, QtCore.SIGNAL("triggered()"), self.clickPrev)
act_r.connect(act_r, QtCore.SIGNAL("triggered()"), self.clickNext)
self.c.frame.iconBar.add(qaction = act_l, command = self.clickPrev)
self.c.frame.iconBar.add(qaction = act_r, command = self.clickNext)
self.clickPrev and clickNext are the function that are run, e.g:
def clickPrev(self):
c = self.c
p = c.goPrevVisitedNode()
Let's see if we can simplify the big picture.
It should be easy to simulate sitecopy in Leo. Just put an upload
date in a uA associated with each @<file> node. The script will
compare upload time to modification time, and upload as needed.
There should be no need to create an @button (or @command) node
programmatically. Just make the node and put the script in it. The
next time you reload the .leo file Leo will create the button for you.
HTH.
Edward
I can't do proper merge for this (I would have to copy paste the
files, which loses your personal version history).
You need to "bzr branch lp:leo-editor", add your changes to that tree,
and push these changes to your own branch.
Try: http://webpages.charter.net/edreamleo/scripting.html#iterators
you probably want p.children()
Cheers -Terry
>> Ville, can you tell me, how to get the subnodes of a leo node in
>> python? I thought, that you know.
>
> Try: http://webpages.charter.net/edreamleo/scripting.html#iterators
>
> you probably want p.children()
Or p.subtree() or p.self_and_subtree()
EKR
> Can somebody tell me, how to effectively program leo plugins, so I
> won't need to restart leo after I modify the code, so it would load
> the new plugin again?
You can usually get all the functionality sorted out in @button nodes,
the
exec(g.findTestScript(c,'@common my common code'))
pattern can be used to let different buttons use the same core code.
Then, when everything works, you can do the conversion to a plugin.
In some cases using pythons reload command might be helpful, but I
suspect the @button route is simpler.
> And how to debug?
I just stick print statements into the code :-) Of course that assumes
quickly executing the code is easy, but that's the case using the
@button route.
Cheers -Terry
I test Leo in a separate copy so that I don't have to reload Leo. See the FAQ:
http://webpages.charter.net/edreamleo/FAQ.html#how-can-i-use-leo-to-develop-leo-itself
> And how to debug?
I use two main methods:
1. Insert calls to g.trace to see what is happening. This is
extremely easy to do with Python.
For complex code, I use variations on the following pattern::
trace = False and not g.unitTesting # change False to True to enable
verbose = False # Optional, for complex traces.
if trace: g.trace(<<list of variables to trace, just like print>>)
if trace and verbose: g.trace(<< more complex traces>>)
Leo's source code has many examples of this pattern.
2. Use g.pdb() a wrapper for::
import pdb ; pdb.set_trace()
pdb is your friend for mysterious bugs. They don't happen often with Python.
Edward
> Maybe, that's because QWidget is not thread safe and I use threads?
Yeah, using threads risks crashing the whole program randomly. In
general threads are not recommended for GUI programs.
> How can I output messages to the user?
Try to avoid threads in the first place.
If that is not possible, Leo could grow an output function that is
thread safe (g.es_thread?). It would only use the ui when running on
main thread.
Making the default g.es wouldn't make sense because it would be somewhat slower.
> > Maybe, that's because QWidget is not thread safe and I use threads?
>
> Yeah, using threads risks crashing the whole program randomly. In
> general threads are not recommended for GUI programs.
The geotag plugin which lets you interactively geotag / "geoview" nodes
in a google map browser page uses threads and doesn't crash, although
this may only be because it hasn't been tested that much. But I seem
to remember needing to use some quite specialized thread aware queues to
avoid blocking etc., you might look at that.
Come to think of it it probably doesn't do any drawing in leo outside
the main thread, but maybe its architecture will give you a hint about
how you could also avoid that.
Whoops, now I see it does block when you request the user to locate
something, which is ok for it's workflow, but nonetheless it has some
esoteric (to me) thread queue thing in it which might be interesting.
Cheers -Terry
> Can we create a thread safe function for all the graphic libs?
Don't even think about it.
Edward
It could work. The implementation would just be different for Tk and Qt.
In Qt, you would queue the messages in a list when running in a
thread, and use QTimer.singleShot to dump the message to the UI. This
would make all ui manipulation happen in main loop.
ValueError: No JSON object could be decodedWould you teach me how to use this plugin?
> I'll see what I can do today. It looks like this plugin needs some work.
As far as I remember it needs ftp credentials written in JSON format.
Maybe, user did something wrong with JSON format? I'll look as well.