pythonjustpy

Get Position of clicked pixel on an image in justpy


In justpy, I would like to load an image and record which pixel a clicked on. So I need the position of the pixel of an image with an offset, not position of the mouse on screen.

JustPy click and click_out event do not give any coordinates at all, even in debug mode.

{'event_type': 'click__out', 'id': 2, 'class_name': 'Img', 'html_tag': 'img', 'vue_type': 'html_component', 'page_id': 0, 'websocket_id': 0, 'session_id': 'e0311ccc163e47df884e3cb109d046b9', 'msg_type': 'event', 'page': WebPage(page_id: 0, number of components: 2, reload interval: None), 'websocket': <starlette.websockets.WebSocket object at 0x75ae283282d0>, 'target': Img(id: 2, html_tag: img, vue_type: html_component, name: No name, number of components: 0)}

Any idea how to get the position of the pixel? Or the offset of the image on the users screen?

I read the examples on the webpage and tried different events, but failed.

import justpy as jp

def my_click1(self, msg):
self.text = 'I was clicked'
#self.log.text=msg
print(msg.event_type)
print(msg['event_type'])
print(msg)

def img_click(self, msg):
print("Image Click")
print(msg.event_type)
print(msg['event_type'])
print(msg)
print(self)

def img_clickout(self, msg):
print("Clickout")
print(msg.event_type)
print(msg['event_type'])
print(msg)
print(self)

def event_demo1():
wp = jp.WebPage()
wp.debug = True
d = jp.Div(text='Not clicked yet', a=wp, classes='w-48 text-xl m-2 p-1 bg-blue-500 text-white')
img = jp.Img(src='https://www.python.org/static/community_logos/python-powered-h-140x182.png', a=wp)
d.on('click', my_click1)
img.on('click', img_click)
img.on('click__out', img_clickout)
return wp

jp.justpy(event_demo1)

Solution

  • Using img.additional_properties = ['pageX', 'pageY', 'screenX', 'screenY'] you can get mouse position on page (pageX, pageY)

    To get image position I use

    jp.run_task(wp.run_javascript(js_code, request_id='image_data'))
    

    to execute JavaScript code

        js_code = f'''
        var image = document.querySelector('[name="{self.name}"]');  // put self.name
        var rect = image.getBoundingClientRect();
        //console.log(rect.top, rect.right, rect.bottom, rect.left);
        ({{image: rect, mouse:{{ pageX:{msg.pageX}, pageY:{msg.pageY}, screenX: {msg.screenX}, screenY: {msg.screenY} }} }});  // it has to be in ( )
        '''
    

    I use f-string to put some extra values in code - so it can run with different images.

    I use name from Img(.. , name=...) to find image in browser and I use getBoundingClientRect() to get its size and position.

    Next I send back all information - even mouse position because it can't get it in current function but in function assigned to wp.on('result_ready', result_ready).

    Because function result_ready can be used to get result from different events so I use result_id in run_javascript(..., request_id=...) to recognize this event.


    Doc:


    Full working code.

    import justpy as jp
    
    def result_ready(self, msg):
        #print('result_ready: self:', self)
        #print('result_ready: msg:', msg)
        #print('result_ready: msg.request_id', msg.request_id)
    
        if msg.request_id == 'image_data':
            #print(msg.result)
            print('image:', msg.result.image)
            print('mouse:', msg.result.mouse)
    
            left = msg.result.image.left
            top = msg.result.image.top
            
            pageX = msg.result.mouse.pageX
            pageY = msg.result.mouse.pageY
            
            print('x:', pageX-left)
            print('y:', pageY-top)
    
            #screenX = msg.result.mouse.screenX
            #screenY = msg.result.mouse.screenY
    
            #print('x:', screenX-left)
            #print('y:', screenY-top)
        
    def click_image(self, msg):
        #print('click_image: self:', self)
        #print('click_image: msg:', msg)
        #print('click_image: self.name:', self.name)
    
        print('click_image: msg.pageX:', msg.pageX)
        print('click_image: msg.pageY:', msg.pageY)
        print('click_image: msg.screenX:', msg.screenX)
        print('click_image: msg.screenY:', msg.screenY)
        
        # f-string needs `{{ }}` to put string `{ }` in `({{image: rect}});`
        js_code = f'''
        var image = document.querySelector('[name="{self.name}"]');  // put self.name
        var rect = image.getBoundingClientRect();
        //console.log(rect.top, rect.right, rect.bottom, rect.left);
        ({{image: rect, mouse:{{ pageX:{msg.pageX}, pageY:{msg.pageY}, screenX: {msg.screenX}, screenY: {msg.screenY} }} }});  // it has to be in ( )
        '''
    
        #print('click_image: before run_javascript')
        #print(js_code)
        
        jp.run_task(wp.run_javascript(js_code, request_id='image_data'))
        
        #print('click_image: after run_javascript')
    
    
    def main():
        global wp  # need in `wp.run_javascript`
        
        wp = jp.WebPage()
        wp.debug = True
        wp.on('result_ready', result_ready)
    
        txt = jp.Div(a=wp, text="Some text before Image to move it from border")
        
        img1 = jp.Img(a=wp, name='image_1', src='https://www.python.org/static/community_logos/python-powered-h-140x182.png', style='display: inline')
        img1.on('click', click_image)
        img1.additional_properties = ['pageX', 'pageY', 'screenX', 'screenY']
    
        img2 = jp.Img(a=wp, name='image_2', src='https://www.python.org/static/community_logos/python-powered-h-140x182.png', style='display: inline')
        img2.on('click', click_image)
        img2.additional_properties = ['pageX', 'pageY', 'screenX', 'screenY']
    
        return wp
    
    jp.justpy(main)