Equivalent of robofab.RFont.update()



  • I thought of a way that this could be done in the font and glyph objects. Here is the font object version:

    def update(self):
        document = self.document
        for windowController in document.windowControllers():
            window = windowController.window()
            contentView = window.contentView()
            contentView.displayIfNeeded()
    

    (Not tested, but it should work. I think. Maybe.)

    Is this a good idea? I don't know. It could slow down lots of scripts for the sake of one script.


  • admin

    works!
    but it will never make it into RF, sorry :)

    import time
    
    def update(font):
        document = font.document()
        for windowController in document.windowControllers():
            window = windowController.window()
            contentView = window.contentView()
            contentView.displayIfNeeded()
            
    f = CurrentFont()
    g = f["A"]
    s = time.time()
    
    for angle in range(0, 360, 15):
        g.rotate(angle)
        update(f)
    
    print time.time() - s
    
    s = time.time()
    for angle in range(0, 360, 15):
        g.rotate(angle)
        f.update()
    
    print time.time() - s
    

    (with a font window open, glyph window open and Space Center open)

    For such a simple task it takes one 1 sec more to update.

    >>> 1.04617500305
    >>> 0.0473890304565
    

    So this is not a good plan unless you realllllyy like coffee or having days with 25 hours or when the sun is turning half speed....

    if this is the case you can install a script during startup that add an observer to a fontChanged notification and updates all the possible windows and views.



  • My understanding of why RFont.update() exists & how to use it was gleaned entirely from the Robofab docs & examples. Some Robofabbers may use it more, some less. But it is part of the standard Robofab-in-Fontlab scripting idiom. If you followed the Robofab docs, your scripts rely on it.

    (Tal, since you ask, one example of a script that "needs to update the UI immediately" is EVB's accent builder example on the Robofab site. (http://robofab.com/howto/buildingaccents.html) And I try to learn from the master.)

    As a matter of policy, having update() do nothing in NoneLab makes logical sense because there is no UI to update.

    Likewise, my comment is also one of policy: now that Robofab is in another UI environment, how should update() behave? Does it matter that it behaves differently than it did in the previous UI environment?

    Ben says no, because update() was designed to accommodate FL and I can test for FL in the script. A fair point, but there's another dimension to the problem. The update() command gives Robofab-in-Fontlab a nice modular division between the Robofab layer and the Fontlab UI layer. Everything gets encapsulated into the update() command. I don't have to worry about the specifics. Maybe this was an unintentional result, but to the extent that one of Robofab's goals is to insulate me from the FL object model, I always thought it was deliberate.

    Above, Frederik showed me how to work with view objects and view.display() to do my updates. That's fine as far as it goes, but it lacks modularity. I've got a script that would be happy if it could just be in the business of manipulating Robofab objects. Now it has to get into the business of manipulating Robofont UI objects too.

    As a scripter, it's not that I mind manipulating UI objects. It's just that ideally, I'd like to avoid mixing UI stuff with data stuff, because it won't scale. For instance, what happens when folks start releasing scripts with new UI widgets? Now my data-manipulation script has to get updated to work with those widgets too. (Tal, perhaps your proposed approach is forward-compatible in that way.)

    Nor am I saying that Robofont or defcon does it "wrong".

    Again, it's a policy question of how to expose the Robofont UI to scripts, whether to be consistent with FL, or whether to invent a 3rd idiom (i.e., require Robofab-in-Robofont scripters to take more of an interest in Robofont UI objects).



  • Matthew:

    With all apologies, I don't think that this is a policy question.

    I think that we can agree that .update() in both FontLab and RoboFont and Glyphs (for all I know) updates the UI. Code with .update() works in both FontLab and RoboFont, with the result that at the end of a script, the UI is updated for the font or glyph that it is called on.

    In FontLab, it happens that if you call f.update(), FontLab will refresh the UI for each incremental change if it is called in a loop. This, from a speed of the application viewpoint, is a really dumb thing to do, as it slows everything down to molasses speed (see Frederik's timing and Tal's comment about when RoboFab was too liberal with .update() — and, if you've used UFO export in FontLab through RoboFab before the .update()s were taken out, you know that doing so could mean a nice coffee break). The only use of this 'feature' of FontLab, which you mention, that I can see would be for testing one's script. After the testing stage, one would just want their script to run, and run as quickly as possible. Frederik has demonstrated code that one could insert into a script for testing, much like how one inserts several print statements when testing a script, then takes them out when things are working.

    You pointed to Erik van Blokland's build accent script, which calls .update() so that FontLab will refresh the UI and show all the built accents. This script will work the same way in RoboFont, as .update() will also update the UI. Things work well in both applications. I believe, that what Tal, and for that matter, I, am curious about, is a example where a script depends on showing the UI update for each point in a loop, not just needing to update when all transformations are finished.

    I think that we can agree that FontLab isn't always the most consistent of applications, and that a view of consistency of RoboFab meaning that it needs to behave, bugs and all, 100% exactly like RoboFab in FontLab, even if the end result is the same, is both impractical and akin to asking every single web browser to behave just like IE6.

    The end result of a .update() is the same, which is all I think one can expect and ask for. If .update()'s end result was different in different applications, this would be a problem for RoboFab, but this is not the case here.



  • Fair enough. At the moment, I don't have another script in my back pocket that illustrates the need for incremental UI updates. If I come across one, I will post it. Like I say, mostly I have used the feature for status reporting and debugging.

    As for update(), my only remaining question is when it actually makes a difference in a Robofont script. For instance, this code snippet (adapted from the EVB script) works the same way with or without the calls to update().

    from robofab.world import CurrentFont
    
    f = CurrentFont()
    
    for k in ["A", "B", "C", "D"]:
            f[k].mark = 100
            f.update()
    
    from robofab.world import CurrentFont
    
    f = CurrentFont()
    
    for k in ["A", "B", "C", "D"]:
            f[k].mark = 100
    


  • (How do I include a tag in a comment? Apparently it's something different than typing )



  • GAAAH

    (How do I include a pre tag in a comment? Apparently it’s something different than typing greater-than + pre + less-than)


  • admin

    thanks for proving it yourself! ;)

    as stated in the documentation .update() only tells the UI that your object has changed.

    see http://doc.robofont.com/api/robofab-extras/

    off topic:
    I've just edited your post and added <pre>bla bla bla</pre> for none-editors and none-admins



  • Matthew:

    My guess as to why it's working the same way (Frederik can confirm) is that RoboFont is smarter about updating the UI than FL is. You'd want to use .update() in the case you want to be absolutely sure the UI updates, and/or your script needs to run in both RoboFont and FontLab, and need it in there to force the FontLab UI to update.

    Best,
    Ben



  • Thanks Ben. You impliedly make a good case that my goal of having FL & RF compatible scripts is impractical. There is something to be said for just rebuilding Robofont-optimized versions.

    As for update() in the middle of a script, my idiomatic use for this technique in Fontlab was when I was working with Robofab pens, specifically scripts that attempted to do pen operations on hundreds of glyphs at a pull. Of all Robofab operations, I found that pens were the most likely to awaken the FL crash monster.

    The traditional behavior of FL when it encounters a script error is to hang. So relying on a UI update at the end of the script to show me status didn't work, because it never got there. So I got in the habit of calling update() during the loops. This was slower, but when FL would hang, I could at least see how far it had gotten, giving some clues about why the script was failing, and whether the error was due to my own abuse of Robofab (which I could fix) or Fontlab's arbitrary refusal to continue (which I could not).

    Clearly, Robofont has a better work ethic about finishing scripts.



  • Matthew:

    Two quick things: I don't think that .update() will slow down RoboFont, as you demonstrated it's smart about updating when need be, so having this in for FontLab won't impact RoboFont.

    Second, sometimes a quicker way to do what you wanted with update in FontLab is to just have a print statement for each iteration of a loop. You'll get a running list of what's been done, so you'll know what happens before a crash, even if the glyph your script is working on isn't visible in the font window.

    Best,
    Ben


  • admin

    a quick tip if you a really, really want to debug in FL:

    redirect the stdout and stderr to a file instead if printing it into FLs output window, cause can FL can quit unexpectedly.

    import sys
    
    f = open(u"/Users/frederik/Desktop/FLlog.txt", "w")
    
    savedstdout = sys.stdout
    savedstderr = sys.stderr
    
    sys.stdout = f
    sys.stderr = f
    
    print "hello world"
    
    f.close()
    
    sys.stdout = savedstdout 
    sys.stderr = savedstderr
    

    This will also work in RoboFont off course!! your scripts will still be exchangeable.

    another tip:
    visual debugging is in most cases a bad idea.... or you should make a .png, .pdf that you can review afterwards. Use DrawBot or the tinyDrawBot extension (will released when 1.2 is out)



  • In all my years of working on and with RoboFab in FontLab I’ve never seen a script that needed to update the way that you describe.

    UFOCentral also relies on this technique, no? When I used it the other day, the glyphs and layers flashed before my eyes as I exported them. (Though I understand that UFOCentral is superfluous inside of Robofont.)

    The macro point is that stdout/stderr is useful for debugging only when you can describe the status accurately in text. For instance, plenty of times in FL I've used a pointPen to copy an outline from one font to another, only to find out it's been turned into crumpled space junk along the way. But as far as Robofab or FL was concerned, the pen operation reported no errors.

    Therefore, to rely on stdout/stderr only, you'd have to add in an extra step where you programmatically compare the expected and actual result, so the status can be reported accurately ("oops, you got space junk"). Sometimes that's easy to do (e.g., making a straight duplicate) but sometimes not (where there's a transform happening in between).

    Either way, you're adding new debugging code, which a) will slow your script (no big deal) and b) may itself have bugs in it (very big deal). For complicated pen operations, your debugging code would have to essentially reimplement the pen. Or you'd need to do something ritzy like apply your pen operation into the actual target glyph, and then also a buffer glyph, and compare the results. But it creates a spiraling need to debug the debugger. So what is the advantage of visual debugging? Like stdout and stderr, it works without creating collateral complications.

    Anyways, the vagaries of FL need not be hashed over here. My FL scripts all work fine — I've already absorbed those slings & arrows. When I get to the point where Robofont's update() is causing a more-than-theoretical problem, I will report back. In text.



  • UFOCentral also relies on this technique, no?

    No. That's a symptom of FL's interface problem, not a feature of the script.


Log in to reply