U
    PeI                     @   sN  d Z dZddlmZ ddlmZ ddlmZ ddlm	Z	 ddl
mZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZmZmZmZ ddlZddlZe	jdddd G dd deZG dd de Z!dekrdZ"n`ddl#m$Z$ ddlm%Z% G dd de%Z&G dd de Z'G dd de!Z(e( Z"e)d *e"j+ dS )!a  
Asynchronous data loader
========================

This is the Asynchronous Loader. You can use it to load an image
and use it, even if data are not yet available. You must specify a default
loading image when using the loader::

    from kivy.loader import Loader
    image = Loader.image('mysprite.png')

You can also load an image from a url::

    image = Loader.image('http://mysite.com/test.png')

If you want to change the default loading image, you can do::

    Loader.loading_image = Image('another_loading.png')

Tweaking the asynchronous loader
--------------------------------

.. versionadded:: 1.6.0

You can tweak the loader to provide a better user experience or more
performance, depending of the images you are going to load. Take a look at the
parameters:

- :attr:`Loader.num_workers` - define the number of threads to start for
  loading images.
- :attr:`Loader.max_upload_per_frame` - define the maximum image uploads in
  GPU to do per frame.

)Loader
LoaderBase
ProxyImage    )kivy_data_dir)Logger)Clock)Cache)ImageLoaderImage)Config)platform)deque)sleep)join)writecloseunlinkenvironN	kv.loaderi  <   )limittimeoutc                       s4   e Zd ZdZdZ fddZdd Zdd Z  ZS )	r   a  Image returned by the Loader.image() function.

    :Properties:
        `loaded`: bool, defaults to False
            This value may be True if the image is already cached.

    :Events:
        `on_load`
            Fired when the image is loaded or changed.
        `on_error`
            Fired when the image cannot be loaded.
            `error`: Exception data that occurred
    )on_loadon_errorc                    s*   | dd}tt| j|f| || _d S )NloadedF)popsuperr   __init__r   )selfargkwargsr   	__class__ //tmp/pip-unpacked-wheel-xzebddm3/kivy/loader.pyr   K   s    zProxyImage.__init__c                 C   s   d S Nr#   r   r#   r#   r$   r   P   s    zProxyImage.on_loadc                 C   s   d S r%   r#   )r   errorr#   r#   r$   r   S   s    zProxyImage.on_error)	__name__
__module____qualname____doc__Z
__events__r   r   r   __classcell__r#   r#   r!   r$   r   :   s
   r   c                   @   s   e Zd ZdZdZddiZdd Zdd Zd	d
 Zdd Z	e
e	eZdd Zdd Ze
eeZdd Zdd Ze
eeZdd Zdd Ze
eeZdd Zdd Zdd Zdd  Zd!d" Zd#d$ Zd%d& Zd'd( Zd)d* Zd+d, Zd1d-d.Zd/d0 Z dS )2r   zCommon base for the Loader and specific implementations.
    By default, the Loader will be the best available loader implementation.

    The _update() function is called every 1 / 25.s or each frame if we have
    less than 25 FPS.
    Nz.pyzz.zipc                 C   s\   d | _ d | _d| _d| _d| _t | _t | _	t | _
g | _d| _d| _t| j| _d S )N   F)_loading_image_error_image_num_workers_max_upload_per_frame_paused	threading	Condition_resume_condr   _q_load_q_done_client_running_start_wantedr   Zcreate_trigger_update_trigger_updater&   r#   r#   r$   r   o   s    
zLoaderBase.__init__c                 C   s   | j d k	r| j   d S r%   )r<   cancelr&   r#   r#   r$   __del__~   s    
zLoaderBase.__del__c                 C   s   |dk rt d|| _d S )Nr-   zMust have at least 2 workers)	Exceptionr0   r   numr#   r#   r$   _set_num_workers   s    zLoaderBase._set_num_workersc                 C   s   | j S r%   )r0   r&   r#   r#   r$   _get_num_workers   s    zLoaderBase._get_num_workersc                 C   s"   |d k	r|dk rt d|| _d S )N   z/Must have at least 1 image processing per image)r?   r1   r@   r#   r#   r$   _set_max_upload_per_frame   s    z$LoaderBase._set_max_upload_per_framec                 C   s   | j S r%   )r1   r&   r#   r#   r$   _get_max_upload_per_frame   s    z$LoaderBase._get_max_upload_per_framec                 C   s&   | j s ttdd}tj|d| _ | j S )NZimageszimage-loading.zipfilename)r.   r   r   r	   load)r   Zloading_png_fnr#   r#   r$   _get_loading_image   s    zLoaderBase._get_loading_imagec                 C   s$   t |trtj|d| _n|| _d S NrG   )
isinstancestrr	   rI   r.   r   imager#   r#   r$   _set_loading_image   s    
zLoaderBase._set_loading_imagec                 C   s"   | j std}tj|d| _ | j S )Nz.atlas://data/images/defaulttheme/image-missingrG   )r/   r   r	   rI   )r   Zerror_png_fnr#   r#   r$   _get_error_image   s    zLoaderBase._get_error_imagec                 C   s$   t |trtj|d| _n|| _d S rK   )rL   rM   r	   rI   r/   rN   r#   r#   r$   _set_error_image   s    
zLoaderBase._set_error_imagec                 C   s
   d| _ dS )z Start the loader thread/process.TNr9   r&   r#   r#   r$   start   s    zLoaderBase.startc                 G   s   dS )zMain loop for the loader.Nr#   )r   largsr#   r#   r$   run   s    zLoaderBase.runc                 C   s
   d| _ dS )zStop the loader thread/process.FNrS   r&   r#   r#   r$   stop   s    zLoaderBase.stopc                 C   s
   d| _ dS )z^Pause the loader, can be useful during interactions.

        .. versionadded:: 1.6.0
        TN)r2   r&   r#   r#   r$   pause   s    zLoaderBase.pausec                 C   s(   d| _ | j  | j  | j  dS )zSResume the loader, after a :meth:`pause`.

        .. versionadded:: 1.6.0
        FN)r2   r5   acquire
notify_allreleaser&   r#   r#   r$   resume   s    

zLoaderBase.resumec                 C   s2   | j r.| jr.| j  | jd | j  q d S )Ng      ?)r9   r2   r5   rY   waitr[   r&   r#   r#   r$   _wait_for_resume   s    
zLoaderBase._wait_for_resumec                 C   s   t | j| j| j kr td q |   |d }|d }|d }z|ddd }W n   Y dS X |dk	rv||}n*|d	kr| ||d
 }n| ||d
 }|r||}| j	||f | 
  dS )z(internal) Loading function, called by the thread.
        Will call _load_local() if the file is local,
        or _load_urllib() if the file is on Internet.
        g?rH   load_callbackpost_callback:rD   r   N)httphttpsftpsmbr    )lenr7   max_upload_per_framer0   r   r^   split_load_urllib_load_local
appendleftr<   )r   r    rH   r_   r`   protodatar#   r#   r$   _load  s*    


zLoaderBase._loadc                 C   s   t j|fddi|S )z(internal) Loading a local fileZ	keep_dataT)r	   rI   )r   rH   r    r#   r#   r$   rj   '  s    zLoaderBase._load_localc                 C   s  ddl }ddl}|ddd }|dkrZzddlm} W n  tk
rX   td Y dS X d } }}	zvzd}
|dkr|j
||}nx|j
|}td	d
rtd	d
}|r|d| d}tdkrddl}ddl}|j| d}|j|_|j
j||d}d|kr$d|dd  }n|  }t|}tj||}|s|dd ddd }t |dkr|d s|dd }qjt |dkrd|d krd|d dd  }|j!d|d\}	}
|" }|  d}t#|	| t|	 d}	| $|
|}|j%D ]}||_&qW n t'k
r } zt(d|  z|	rTt|	 W n t)k
rl   Y nX | j*dd D ]>\}}||krq|| j+|_,|j-d|d | j*.||f q|| j+ W Y W S d}~X Y nX W 5 |r|  |	rt|	 |
dkrt	|
 X |S )zx(internal) Loading a network file. First download it, save it to a
        temporary file, and pass it to _load_local().r   Nra   rD   re   )
SMBHandlerz5Loader: can not load PySMB: make sure it is installed network	useragentz
User-Agent)ZandroidZios)cafile)contextz#..?/Z
kivyloader)prefixsuffixz!Loader: Failed to load image <%s>r   )r'   )/urllib.requesttempfilerh   Zsmb.SMBHandlerro   ImportErrorr   warningr   r   requestbuild_openeropenRequestr   
has_optionget
add_headerr   certifisslcreate_default_contextwhereCERT_REQUIREDverify_modeurlopeninfoget_content_type	mimetypesguess_extensionr   	EXT_ALIASrf   mkstempreadr   rj   _datasourcer?   	exceptionOSErrorr8   error_imagerO   dispatchremove)r   rH   r    urllibr|   rl   ro   rm   fdZ	_out_osfdZ_out_filenamer   rr   Zssl_ctxr   r   rz   ctypepartsZidataZimdataex
c_filenameclientr#   r#   r$   ri   -  s    

 



$

zLoaderBase._load_urllibc              	   G   s   | j r| js|   d| _ | jr,|   dS t| jD ]}z| j \}}W n t	k
rd   Y  dS X |}|j
s~td|| | jdd D ]8\}}||krq||_d|_|d | j||f qq6|   dS )z=(internal) Check if a data is loaded, and pass to the client.FNr   Tr   )r:   r9   rT   r2   r<   rangerg   r7   r   
IndexErrornocacher   appendr8   rO   r   r   r   )r   rU   xrH   rm   rO   r   r   r#   r#   r$   r;     s.    

zLoaderBase._updatec                 K   s   t d|}|dkr,t|f| jdd|S t| jfd| ji|}| j||f |dkr| j||||d |dd	st d|d	 d| _| 	  n |S )
a  Load a image using the Loader. A ProxyImage is returned with a
        loading image. You can use it as follows::

            from kivy.app import App
            from kivy.uix.image import Image
            from kivy.loader import Loader

            class TestApp(App):
                def _image_loaded(self, proxyImage):
                    if proxyImage.image.texture:
                        self.image.texture = proxyImage.image.texture

                def build(self):
                    proxyImage = Loader.image("myPic.jpg")
                    proxyImage.bind(on_load=self._image_loaded)
                    self.image = Image()
                    return self.image

            TestApp().run()

        In order to cancel all background loading, call *Loader.stop()*.
        r   NFT)loading_imager   r   N)rH   r_   r`   r    r   F)
r   r   r   r   r8   r   r6   rk   r:   r<   )r   rH   r_   r`   r    rm   r   r#   r#   r$   rO     s4    

zLoaderBase.imagec                 C   s   t d| d S )Nr   )r   r   )r   rH   r#   r#   r$   remove_from_cache  s    zLoaderBase.remove_from_cache)NN)!r(   r)   r*   r+   r<   r   r   r>   rB   rC   propertynum_workersrE   rF   rg   rJ   rP   r   rQ   rR   r   rT   rV   rW   rX   r\   r^   rn   rj   ri   r;   rO   r   r#   r#   r#   r$   r   W   sB    





!j$
4r   ZKIVY_DOC)queue)Threadc                   @   s    e Zd ZdZdd Zdd ZdS )_Workerz8Thread executing tasks from a given tasks queue
        c                 C   s(   t |  || _d| _|| _|   d S NT)r   r   tasksdaemonpoolrT   )r   r   r   r#   r#   r$   r     s
    
z_Worker.__init__c              
   C   sb   | j jr^| j \}}}z||| W n* tk
rP } zt| W 5 d }~X Y nX | j  q d S r%   )r   runningr   r   r?   print	task_done)r   funcargskargser#   r#   r$   rV     s    z_Worker.runN)r(   r)   r*   r+   r   rV   r#   r#   r#   r$   r     s   r   c                       s0   e Zd ZdZ fddZdd Zdd Z  ZS )_ThreadPoolz5Pool of threads consuming tasks from a queue
        c                    s<   t t|   d| _t | _t|D ]}t| | j q&d S r   )	r   r   r   r   r   Queuer   r   r   )r   Znum_threads_r!   r#   r$   r     s
    
z_ThreadPool.__init__c                 O   s   | j |||f dS )z$Add a task to the queue
            N)r   put)r   r   r   r   r#   r#   r$   add_task!  s    z_ThreadPool.add_taskc                 C   s   d| _ | j  d S r   )r   r   r   r&   r#   r#   r$   rW   &  s    z_ThreadPool.stop)r(   r)   r*   r+   r   r   rW   r,   r#   r#   r!   r$   r     s   r   c                       s<   e Zd Z fddZ fddZ fddZdd Z  ZS )	LoaderThreadPoolc                    s   t t|   d | _d S r%   )r   r   r   r   r&   r!   r#   r$   r   +  s    zLoaderThreadPool.__init__c                    s,   t t|   t| j| _t| jd d S )Nr   )	r   r   rT   r   r0   r   r   Zschedule_intervalrV   r&   r!   r#   r$   rT   /  s    zLoaderThreadPool.startc                    s(   t t|   t| j | j  d S r%   )r   r   rW   r   Z
unschedulerV   r   r&   r!   r#   r$   rW   4  s    zLoaderThreadPool.stopc                 G   s:   | j r6z| j }W n   Y d S X | j| j| q d S r%   )r9   r6   r   r   r   rn   )r   rU   
parametersr#   r#   r$   rV   9  s    zLoaderThreadPool.run)r(   r)   r*   r   rT   rW   rV   r,   r#   r#   r!   r$   r   *  s   r   z)Loader: using a thread pool of {} workers),r+   __all__Zkivyr   Zkivy.loggerr   Z
kivy.clockr   Z
kivy.cacher   Zkivy.core.imager	   r
   Zkivy.configr   Z
kivy.utilsr   collectionsr   timer   os.pathr   osr   r   r   r   r3   r   registerr   objectr   r   Zkivy.compatr   r   r   r   r   r   formatr   r#   r#   r#   r$   <module>   s>   #   #