
    	h@                     j   S r SSKrSSKrSSKrSSKrSSKJr  SSKJr  SSKJ	r	  Sr
SrSrSr\R                  " \
\\\5        \R                  " \
\\\S	S
/5         " S S5      r " S S5      r " S S\5      r " S S\5      r\S:X  a*  SSKr\R,                  R.                  R1                  \5        gg)aD  Excel IRTDServer implementation.

This module is a functional example of how to implement the IRTDServer interface
in python, using the pywin32 extensions. Further details, about this interface
and it can be found at:
     https://learn.microsoft.com/en-us/previous-versions/office/developer/office-xp/aa140060(v=office.10)
    N)	universal)gencache)COMExceptionz&{00020813-0000-0000-C000-000000000046}      
IRtdServerIRTDUpdateEventc                      ^  \ rS rSrSrS/r/ SQr\R                  r	Sr
SrU 4S jrS rS	 rS
 rS rS rS rS rSS jrS rS rS rS rS rSrU =r$ )ExcelRTDServerH   a	  Base RTDServer class.

Provides most of the features needed to implement the IRtdServer interface.
Manages topic adding, removal, and packing up the values for excel.

Shouldn't be instanciated directly.

Instead, descendant classes should override the CreateTopic() method.
Topic objects only need to provide a GetValue() function to play nice here.
The values given need to be atomic (eg. string, int, float... etc).

Also note: nothing has been done within this class to ensure that we get
time to check our topics for updates. I've left that up to the subclass
since the ways, and needs, of refreshing your topics will vary greatly. For
example, the sample implementation uses a timer thread to wake itself up.
Whichever way you choose to do it, your class needs to be able to wake up
occaisionally, since excel will never call your class without being asked to
first.

Excel will communicate with our object in this order:
  1. Excel instanciates our object and calls ServerStart, providing us with
     an IRTDUpdateEvent callback object.
  2. Excel calls ConnectData when it wants to subscribe to a new "topic".
  3. When we have new data to provide, we call the UpdateNotify method of the
     callback object we were given.
  4. Excel calls our RefreshData method, and receives a 2d SafeArray (row-major)
     containing the Topic ids in the 1st dim, and the topic values in the
     2nd dim.
  5. When not needed anymore, Excel will call our DisconnectData to
     unsubscribe from a topic.
  6. When there are no more topics left, Excel will call our ServerTerminate
     method to kill us.

Throughout, at undetermined periods, Excel will call our Heartbeat
method to see if we're still alive. It must return a non-zero value, or
we'll be killed.

NOTE: By default, excel will at most call RefreshData once every 2 seconds.
      This is a setting that needs to be changed excel-side. To change this,
      you can set the throttle interval like this in the excel VBA object model:
        Application.RTD.ThrottleInterval = 1000 ' milliseconds
r   )ConnectDataDisconnectData	HeartbeatRefreshDataServerStartServerTerminater   r   c                 `   > [         TU ]  5         U R                  U l        SU l        0 U l        g)ConstructorN)super__init__ALIVEIsAlive_ExcelRTDServer__callbacktopicsself	__class__s    pC:\Users\julio\OneDrive\Documentos\Trabajo\Ideas Frescas\venv\Lib\site-packages\win32com/demos/excelRTDServer.pyr   ExcelRTDServer.__init__   s'    zz    c                 d    U R                   c	  [        SS9eU R                   R                  5         g)zCUse the callback we were given to tell excel new data is available.NzCallback excel provided is Nulldesc)r   r   UpdateNotifyr   s    r   SignalExcelExcelRTDServer.SignalExcel   s(    ??"$EFF$$&r    c                 ,    U R                  U5      U R                  U'   SnU R                  U   nUc  SU R
                  R                  -  nOUR                  5       nU R                  U5        XS4$ ! [         a  n[        [	        U5      S9eSnAff = f)z6Creates a new topic out of the Strings excel gives us.r"   NTz# %s: Waiting for update)	CreateTopicr   	Exceptionr   strr   __name__GetValueOnConnectData)r   TopicIDStringsGetNewValueswhyresults         r   r   ExcelRTDServer.ConnectData   s    	.#'#3#3G#<DKK  W%>/$..2I2IIF__&F 	7# ##  	.CH--	.s   A2 2
B<BBc                 ~    U R                  U5        XR                  ;   a  SU R                  U'   U R                  U	 gg)zDeletes the given topic.N)OnDisconnectDatar   r   r/   s     r   r   ExcelRTDServer.DisconnectData   s9    g&kk!#'DKK G$ "r    c                     U R                   $ )z+Called by excel to see if we're still here.)r   r%   s    r   r   ExcelRTDServer.Heartbeat   s    ||r    c                    [        U R                  5      nU R                  5         S/U-  S/U-  /n[        U R                  R	                  5       5       H&  u  p4Uu  pVXRS   U'   UR                  5       US   U'   M(     [        U5      U4$ )af  Packs up the topic values. Called by excel when it's ready for an update.

Needs to:
  * Return the current number of topics, via the "ByRef" TopicCount
  * Return a 2d SafeArray of the topic data.
    - 1st dim: topic numbers
    - 2nd dim: topic values

We could do some caching, instead of repacking everytime...
But this works for demonstration purposes.Nr   r   )lenr   OnRefreshData	enumerateitemsr-   tuple)r   
TopicCountresultsidx	topicdatatopicNumtopics          r   r   ExcelRTDServer.RefreshData   s     %
 6J&(;< ((9(9(;<NC'OH&AJsO#nn.GAJsO = W~z))r    c                     U R                   U l        Uc	  [        SS9e[        R                  R
                  R                  S5      nU" U5      U l        U R                  5         U R                  $ )zMExcel has just created us... We take its callback for later, and set up shop.z Excel did not provide a callbackr"   z&{A43788C1-D91B-11D3-8F39-00C04F3651B8})	r   r   r   win32comclientCLSIDToClassGetClassr   OnServerStart)r   CallbackObjectIRTDUpdateEventKlasss      r   r   ExcelRTDServer.ServerStart   sc    zz!$FGG  (;;DD4 
 /~>||r    c                 F    U R                   U l        U R                  5         g)z%Called when excel no longer wants us.N)	NOT_ALIVEr   OnServerTerminater%   s    r   r   ExcelRTDServer.ServerTerminate   s    ~~ r    c                     [        S5      e)zTopic factory method. Subclass must override.

Topic objects need to provide:
  * GetValue() method which returns an atomic value.

Will raise NotImplemented if not overridden.
zSubclass must implementNotImplementedr   TopicStringss     r   r)   ExcelRTDServer.CreateTopic   s     677r    c                     g)z=Called when a new topic has been created, at excel's request.N r7   s     r   r.   ExcelRTDServer.OnConnectData       r    c                     g)z?Called when a topic is about to be deleted, at excel's request.Nr\   r7   s     r   r6   ExcelRTDServer.OnDisconnectData   r^   r    c                     g)z7Called when excel has requested all current topic data.Nr\   r%   s    r   r=   ExcelRTDServer.OnRefreshData   r^   r    c                     g)z&Called when excel has instanciated us.Nr\   r%   s    r   rM   ExcelRTDServer.OnServerStart   r^   r    c                     g)z)Called when excel is about to destroy us.Nr\   r%   s    r   rS    ExcelRTDServer.OnServerTerminate  r^   r    )r   
__callbackr   N)r,   
__module____qualname____firstlineno____doc___com_interfaces__public_methods_	pythoncomCLSCTX_INPROC_SERVER_reg_clsctx_r   rR   r   r&   r   r   r   r   r   r   r)   r.   r6   r=   rM   rS   __static_attributes____classcell__r   s   @r   r   r   H   s    )V %~ 11L
 EI'$&%*@"!
8 r    r   c                   J   ^  \ rS rSrSrU 4S jrS rS rS rS r	S r
S	rU =r$ )
RTDTopici  zxBase RTD Topic.
Only method required by our RTDServer implementation is GetValue().
The others are more for convenience.c                 J   > [         TU ]  5         Xl        S U l        SU l        g )NF)r   r   rY   _RTDTopic__currentValue_RTDTopic__dirty)r   rY   r   s     r   r   RTDTopic.__init__  s#    ("r    c                     [        S5      e)zCalled by the RTD Server.
Gives us a chance to check if our topic data needs to be
changed (eg. check a file, quiz a database, etc).zsubclass must implementrV   )r   senders     r   UpdateRTDTopic.Update  s     677r    c                     SU l         g)z6Call when this topic isn't considered "dirty" anymore.FNry   r%   s    r   ResetRTDTopic.Reset  s	    r    c                     U R                   $ rh   )rx   r%   s    r   r-   RTDTopic.GetValue  s    """r    c                     SU l         Xl        g )NT)ry   rx   )r   values     r   SetValueRTDTopic.SetValue  s    #r    c                     U R                   $ rh   r   r%   s    r   
HasChangedRTDTopic.HasChanged#  s    ||r    )rY   __currentValue__dirty)r,   ri   rj   rk   rl   r   r}   r   r-   r   r   rr   rs   rt   s   @r   rv   rv     s+    ,8#$ r    rv   c                   X   ^  \ rS rSrSrSrSrSrSrU 4S jr	S r
S	 rS
 rSS jrSrU =r$ )
TimeServeri.  a  Example Time RTD server.

Sends time updates back to excel.

example of use, in an excel sheet:
  =RTD("Python.RTD.TimeServer","","seconds","5")

This will cause a timestamp string to fill the cell, and update its value
every 5 seconds (or as close as possible depending on how busy excel is).

The empty string parameter denotes the com server is running on the local
machine. Otherwise, put in the hostname to look on. For more info
on this, lookup the Excel help for its "RTD" worksheet function.

Obviously, you'd want to wrap this kind of thing in a friendlier VBA
function.

Also, remember that the RTD function accepts a maximum of 28 arguments!
If you want to pass more, you may need to concatenate arguments into one
string, and have your topic parse them appropriately.
z&{EA7F2CF1-11A2-45E4-B2D5-68E240DB8CB1}zPython.RTD.TimeServerz8Python class implementing Excel IRTDServer -- feeds timeg      ?c                    > [         TU ]  5         [        R                  " U R                  U R
                  5      U l        g rh   )r   r   	threadingTimerINTERVALr}   tickerr   s    r   r   TimeServer.__init__N  s*      oodmmT[[Ar    c                 8    U R                   R                  5         g rh   )r   startr%   s    r   rM   TimeServer.OnServerStartY  s    r    c                     U R                   R                  R                  5       (       d  U R                   R                  5         g g rh   )r   finishedis_setcancelr%   s    r   rS   TimeServer.OnServerTerminate\  s0    {{##**,,KK  -r    c                    [         R                  " U R                  U R                  5      U l         [        U R                  5      (       ar  SnU R                  R                  5        H;  nUR                  U 5        UR                  5       (       a  SnUR                  5         M=     U(       a  U R                  5         U R                  R                  5         g ! U R                  R                  5         f = f)NFT)r   r   r   r}   r   r<   r   valuesr   r   r&   r   )r   refreshrF   s      r   r}   TimeServer.Update`  s    oodmmT[[A	 4;;![[//1ELL&''))"&KKM	 2 $$&KKDKKs   BC C5c                     [        U5      $ )zGTopic factory. Builds a TimeTopic object out of the given TopicStrings.)	TimeTopicrX   s     r   r)   TimeServer.CreateTopicr  s    &&r    )r   rh   )r,   ri   rj   rk   rl   _reg_clsid__reg_progid_
_reg_desc_r   r   rM   rS   r}   r)   rr   rs   rt   s   @r   r   r   .  s?    0 ;K*LKJ H	B! $' 'r    r   c                   8   ^  \ rS rSrSrU 4S jrS rS rSrU =r	$ )r   iw  zExample topic for example RTD server.

Will accept some simple commands to alter how long to delay value updates.

Commands:
  * seconds, delay_in_seconds
  * minutes, delay_in_minutes
  * hours, delay_in_hours
c                 P  > [         TU ]  U5         U R                  u  U l        U l        [        U R                  5      U l        U R                  5       U l
        U R                  [        U R                  5      5        g ! [
         a  n[        S[        U5      -  5      eS nAff = f)NzInvalid topic strings: %s)r   r   rY   cmddelayr*   
ValueErrorr+   float	timestamp
checkpointr   )r   rY   Er   s      r   r   TimeTopic.__init__  s    &	N#'#4#4 DHdj 4::&
 ..*c$//*+  	N 83|;LLMM		Ns   A? ?
B%	B  B%c                 >    [         R                   R                  5       $ rh   )datetimenowr%   s    r   r   TimeTopic.timestamp  s      $$&&r    c                    U R                  5       nX R                  -
  nSnU R                  S:X  a  UR                  U R                  :  a  SnOxU R                  S:X  a  UR
                  U R                  :  a  SnOKU R                  S:X  a  UR                  U R                  :  a  SnOU R                  SU R                  -   5        U(       a!  U R                  [        U5      5        X l        g g )NFsecondsTminuteshoursz#Unknown command: )	r   r   r   r   r   r   r   r   r+   )r   r|   r   deltar   s        r   r}   TimeTopic.Update  s    nnoo%88y }}

*XX"}}

*XX {{djj(MM.9:MM#c(#!O r    )r   r   r   )
r,   ri   rj   rk   rl   r   r   r}   rr   rs   rt   s   @r   r   r   w  s    ,"'" "r    r   __main__)rl   r   r   ro   win32com.clientrI   r   r   win32com.server.exceptionr   EXCEL_TLB_GUIDEXCEL_TLB_LCIDEXCEL_TLB_MAJOREXCEL_TLB_MINOREnsureModuleRegisterInterfacesr   rv   r   r   r,   win32com.server.registerserverregisterUseCommandLiner\   r    r   <module>r      s   @      $ 2
 : 	  nno W 
  $%| |~ NF' F'R1" 1"h z# OO++J7 r    