Help: Mojo Canvas, Glyph Navigator Idea



  • Hi all

    I am trying to make a zoomed in preview that has similar functionality to Illustrators Navigator panel, it's purpose is so that I can have a permanent crop of an enlarged area of a glyph whilst I am editing it.

    I should be able to control the level of zoom, and it should be draggable (you might call this panning) so I can inspect other areas of the glyph at the same magnification.

    Are there native ways of the canvas to achieve this? Perhaps there's something like canvas.scale()?

    When it comes to moving around the canvas I assumed it would scroll if the canvas is bigger than the window, but this does not seem to be the case even with the relevant keyword args set to true, at least in my test.

    I also tried acceptsMouseMoved=True (as it sounds useful) but this is an unexpected keyword.

    I also tried to do something with the mouseDragged() method but I am not sure how to use it correctly. I would appreciate some guidance on this. I am receiving the NSEvent containing dragging data, but I don't know how to do something useful with it.

    I have attached my first test of implementing this idea.

    If I have done something wrong, please let me know.

    I haven't changed much from the canvas API example it is based on. (http://doc.robofont.com/api/mojo/mojo-canvas/)



  • Thank you so much for the example, not only for the fact you got the basic functionality there, but also for the comments and tips. I will study it hard :)

    I can certainly take it from here,

    It's mostly a learning exercise, but if it turns out to be useful I'll see about making it robust enough for sharing.

    Thanks again

    Side note about docs: I think that if you print help it should not print a joke (or broken) link otherwise it leads you to believe you're missing important information that is no longer accessible, perhaps it can be updated?

    It's a similar story for the "starting with mojo" link on the homepage, I can't count the amount of times I clicked on that thinking that's where I will find answers and there's nothing there. Both examples undermine the authority of the docs that do exist, leaving you wondering if they are incomplete.


  • admin

    no problem

    here are some hints and tricks to get it working :)

    I hope this sets you in the right direction

    good luck

    from mojo.canvas import Canvas
    from mojo.drawingTools import *
    import mojo.drawingTools as mojoDrawingTools
    from vanilla import *
    from mojo.events import addObserver,removeObserver
    from defconAppKit.windows.baseWindow import BaseWindowController
    import AppKit
    
    
    class ExampleWindow(BaseWindowController):
    
        def __init__(self):
            self.size = 50
            self.offset = 0, 0
            
            self.w = Window((400, 400), minSize=(200, 200))
            self.w.slider = Slider((10, 5, -10, 22), value=self.size, callback=self.sliderCallback)
            self.w.canvas = Canvas((0, 30, -0, 400), canvasSize=(1000, 1000),delegate=self, hasHorizontalScroller=True, hasVerticalScroller=True)#,acceptsMouseMoved=True)
            # acceptsMouseMoved=True sounds useful, but it is an unexpected keyword?
            # ----->>>> 'acceptsMouseMoved' is a method, return True to receive mouseMoved callback in the delegate (see below)
            self.setUpBaseWindowBehavior()
            self.w.open()
            
            print help(mojoDrawingTools)
            # module docs link broken http://docs.python.org/library/mojo.drawingTools
            # ----->>>>> euh funny :)
            print help(Canvas)
            
            ## HELP
            ## if I use these observers, I can only get information about the mouse when clicking in a glyph window, 
            ## I Want the information when I click in the canvas I created
            ## If I don't use these observers and use the methods directly, I do get the event when clicking on the canvas, but it is NSEvent, the information for instance location is relative to this window, which is what I want.
            ## how can I get this inforation as python dict or tuples or lists? 
            addObserver(self, "_mouseMoved", "mouseMoved")
            addObserver(self, "_mouseDown", "mouseDown")
            # ----->>>>>
            # this object acts a delegate for the canvas object
            # every event can be redirected to this object from the canvas object
            # this will provide you info about events within the canvas object
            
            # obserers just observer a specific action and send a notification to a given method
            # <<<<<<-----
    
        def windowCloseCallback(self, sender):
            #when this window w is closed, remove the observer 
            removeObserver(self, "mouseMoved")
            removeObserver(self, "mouseDown")
            # super I do not know what it's for, but it is in the example
            # ------>>>>> you need this as the BaseWindowController also does stuff while closing the window
            super(ExampleWindow, self).windowCloseCallback(sender)
                    
        def sliderCallback(self, sender):
            self.size = sender.get()
            self.w.canvas.update()
                    
        def draw(self):
            # offset the canvas
            x, y = self.offset
            translate(x, y)
            #set scale of canvas
            scale(self.size*0.1)
            #print CurrentGlyph()
            if CurrentGlyph():
                #draw current glyph to canvas (will need to observe when current glyph changes, but this is just to test)
                drawGlyph(CurrentGlyph())
            
        def acceptsMouseMoved(self):
            return False
        
        # mouse down that is not from observer, gives NSEvent for click relative to window
        def mouseDown(self, event):
            # ----->>>> see https://developer.apple.com/documentation/appkit/nsevent?language=objc
            print type(event)
            view = self.w.canvas.getNSView()        
            print view.convertPoint_fromView_(event.locationInWindow(), None)
        
        # mouse down from observer give info as dict
        def _mouseDown(self, notification):
            print notification["event"]
            print notification["glyph"]    
            print notification["point"]
    
        # mouse moved that is not from observer, gives NSEvent for mouse relative to window        
        def mouseMoved(self, event):
            print event.locationInWindow()
        
        # mouse moved from observer give info as dict
        def _mouseMoved(self, notification):
            print notification
            
        def mouseDragged(self, event):
            # how to grab and drag the canvas? from sender I get information in an NSEvent, how can I access that information and do something useful with it?
            print 'drag?', event
            x, y = self.offset        
            self.offset = x + event.deltaX(), y - event.deltaY()
            self.w.canvas.update()
    
    ExampleWindow()
    


  • Hi Frederik, thank you for your response.

    I have been trying your suggestions and have had success with the use of scale, but am still struggling to make sense of mouseDown mouseDrag mouseUp relative to the canvas.

    From what I have tried, using those observers in the usual way works for the mouse position relative to the current glyph window, but not relative to my canvas window.

    I can get the 'raw' NSEvent of mouseDown relative to my canvas, but I have no idea how to turn that into useful tuples etc.

    I updated the Gist with comments and questions that explain what I mean, please take a look
    https://gist.github.com/MBurvill/d7921fdc724827cd7c79707969edcbf9#file-GlyphNavTest1-py

    I didn't understand what _getMousePosition(nsEventObj) is or how to use it. Could you explain further?

    I hope to learn a lot from this, thanks for your guidance.

    PS: sorry I can't find the formatting instructions to make this post look as pretty as yours!


  • admin

    and for acceptsMouseMoved that seems to be a typo in the docs

    your controller class needs:

    def acceptsMouseMoved(self):
        return True
    

  • admin

    scale(value) will do it

    • acceptsMouseMoved is very intensive as the canvas would send notifications whenever the mouse has moved, instead of the sequence mouseDown mouseDrag mouseUp but you will need mouseMoved too

    • you will need to check for the notifications: glyphWindowWillOpen and glyphWindowWillClose and currentGlyphChanged to be able to make such a panning panel

    • each glyph window has a glyph view, and a handy method is _getMousePosition(nsEventObj) now private, but I can open that in next versions... this is converting the NSEvent mouse positions to glyph space coordinates.

    I guess from here you should be able to make the puzzle :)

    otherwise ask

    enjoy!



  • My apologies, I cannot attach the .py file.

    Here is a gist:
    http://gist.github.com/MBurvill/d7921fdc724827cd7c79707969edcbf9#file-GlyphNavTest1-py