import posixpath\r
import time\r
import pythoncom\r
+import pystray\r
+import tkinter as tk\r
+from tkinter import ttk\r
+from PIL import Image, ImageDraw\r
\r
logging.basicConfig()\r
\r
+global STATE\r
+global STATE_DEFAULT\r
global current_slideshow\r
current_slideshow = None\r
CACHEDIR = r'''C:\Windows\Temp\ppt-cache'''\r
if len(words) > 0 and words[0] == "cache":\r
if current_slideshow:\r
path = CACHEDIR + "\\" + current_slideshow.name()\r
+ else:\r
+ path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "black.jpg") + '/'\r
+ return path\r
words.pop(0)\r
else:\r
path = self.directory\r
http_server = server.HTTPServer(("", 80), Handler)\r
http_server.serve_forever()\r
\r
-STATE = {"connected": 0, "current": 0, "total": 0, "visible": 0, "name": ""}\r
+STATE_DEFAULT = {"connected": 0, "current": 0, "total": 0, "visible": 0, "name": ""}\r
+STATE = STATE_DEFAULT\r
USERS = set()\r
\r
\r
def state_event():\r
+ print("Running state event")\r
return json.dumps({"type": "state", **STATE})\r
\r
\r
\r
\r
async def notify_state():\r
- global current_slideshow\r
- if current_slideshow:\r
+ print("Notifying state to " + str(len(USERS)) + " users")\r
+ global STATE\r
+ if current_slideshow and STATE["connected"] == 1:\r
STATE["current"] = current_slideshow.current_slide()\r
STATE["total"] = current_slideshow.total_slides()\r
STATE["visible"] = current_slideshow.visible()\r
STATE["name"] = current_slideshow.name()\r
+ else:\r
+ STATE = STATE_DEFAULT\r
if USERS: # asyncio.wait doesn't accept an empty list\r
message = state_event()\r
await asyncio.wait([user.send(message) for user in USERS])\r
\r
\r
async def ws_handle(websocket, path):\r
+ print("Received command")\r
global current_slideshow\r
# register(websocket) sends user_event() to websocket\r
await register(websocket)\r
current_slideshow.goto(int(data["value"]))\r
await notify_state()\r
elif data["action"] == "refresh":\r
+ print("Received refresh command")\r
+ await notify_state()\r
if current_slideshow:\r
current_slideshow.export_current_next()\r
current_slideshow.refresh()\r
- await notify_state()\r
else:\r
logging.error("unsupported event: {}", data)\r
finally:\r
await unregister(websocket)\r
\r
def run_ws():\r
+ # https://stackoverflow.com/questions/21141217/how-to-launch-win32-applications-in-separate-threads-in-python/22619084#22619084\r
+ # https://www.reddit.com/r/learnpython/comments/mwt4qi/pywintypescom_error_2147417842_the_application/\r
pythoncom.CoInitializeEx(pythoncom.COINIT_MULTITHREADED)\r
asyncio.set_event_loop(asyncio.new_event_loop())\r
- start_server = websockets.serve(ws_handle, "0.0.0.0", 5678)\r
+ start_server = websockets.serve(ws_handle, "0.0.0.0", 5678, ping_interval=None)\r
asyncio.get_event_loop().run_until_complete(start_server)\r
asyncio.get_event_loop().run_forever()\r
\r
if self.instance.ActivePresentation is None:\r
raise ValueError("PPT instance has no active presentation")\r
self.presentation = self.instance.ActivePresentation\r
- \r
- self.export_all()\r
+\r
+ def unload(self):\r
+ connect_ppt()\r
\r
def refresh(self):\r
- if self.instance is None:\r
- raise ValueError("PPT instance cannot be None")\r
+ try:\r
+ if self.instance is None:\r
+ raise ValueError("PPT instance cannot be None")\r
\r
- #if self.instance.SlideShowWindows.Count == 0:\r
- # raise ValueError("PPT instance has no slideshow windows")\r
- self.view = self.instance.SlideShowWindows[0].View\r
+ if self.instance.SlideShowWindows.Count == 0:\r
+ raise ValueError("PPT instance has no slideshow windows")\r
+ self.view = self.instance.SlideShowWindows[0].View\r
\r
- if self.instance.ActivePresentation is None:\r
- raise ValueError("PPT instance has no active presentation")\r
+ if self.instance.ActivePresentation is None:\r
+ raise ValueError("PPT instance has no active presentation")\r
+ except:\r
+ self.unload()\r
\r
def total_slides(self):\r
- return len(self.presentation.Slides)\r
+ try:\r
+ self.refresh()\r
+ return len(self.presentation.Slides)\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def current_slide(self):\r
- return self.view.CurrentShowPosition\r
+ try:\r
+ self.refresh()\r
+ return self.view.CurrentShowPosition\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def visible(self):\r
- return self.view.State\r
+ try:\r
+ self.refresh()\r
+ return self.view.State\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def prev(self):\r
- self.refresh()\r
- self.view.Previous()\r
+ try:\r
+ self.refresh()\r
+ self.view.Previous()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def next(self):\r
- self.refresh()\r
- self.view.Next()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ self.view.Next()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def first(self):\r
- self.refresh()\r
- self.view.First()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ self.view.First()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def last(self):\r
- self.refresh()\r
- self.view.Last()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ self.view.Last()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def goto(self, slide):\r
- self.refresh()\r
- if slide <= self.total_slides():\r
- self.view.GotoSlide(slide)\r
- else:\r
- self.last()\r
- self.next()\r
- self.export_current_next()\r
+ try:\r
+ self.refresh()\r
+ if slide <= self.total_slides():\r
+ self.view.GotoSlide(slide)\r
+ else:\r
+ self.last()\r
+ self.next()\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def black(self):\r
- self.refresh()\r
- self.view.State = 3\r
+ try:\r
+ self.refresh()\r
+ self.view.State = 3\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def white(self):\r
- self.refresh()\r
- self.view.State = 4\r
+ try:\r
+ self.refresh()\r
+ self.view.State = 4\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def normal(self):\r
- self.refresh()\r
- self.view.State = 1\r
+ try:\r
+ self.refresh()\r
+ self.view.State = 1\r
+ self.export_current_next()\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
\r
def name(self):\r
- return self.presentation.Name\r
+ try:\r
+ self.refresh()\r
+ return self.presentation.Name\r
+ except ValueError or pywintypes.com_error:\r
+ self.unload()\r
+\r
\r
def export_current_next(self):\r
self.export(self.current_slide())\r
while attempts < 3:\r
try:\r
self.presentation.Slides(slide).Export(destination, "JPG")\r
- time.sleep(0.5)\r
break\r
except:\r
pass\r
def get_current_slideshow():\r
return current_slideshow\r
\r
-\r
-if __name__ == "__main__":\r
-\r
- start_server()\r
- \r
+def connect_ppt():\r
+ global STATE\r
+ if STATE["connected"] == 1:\r
+ print("Disconnected from PowerPoint instance")\r
+ STATE = STATE_DEFAULT\r
while True:\r
- # Check if PowerPoint is running\r
- instance = get_ppt_instance()\r
try:\r
+ instance = get_ppt_instance()\r
+ global current_slideshow\r
current_slideshow = Slideshow(instance)\r
STATE["connected"] = 1\r
STATE["current"] = current_slideshow.current_slide()\r
STATE["total"] = current_slideshow.total_slides()\r
print("Connected to PowerPoint instance")\r
+ current_slideshow.export_all()\r
break\r
except ValueError as e:\r
current_slideshow = None\r
pass\r
time.sleep(1)\r
+\r
+def start(_=None):\r
+ #root = tk.Tk()\r
+ #root.iconphoto(False, tk.PhotoImage(file="icons/ppt.png"))\r
+ #root.geometry("250x150+300+300")\r
+ #app = Interface(root)\r
+ #interface_thread = threading.Thread(target=root.mainloop())\r
+ #interface_thread.setDaemon(True)\r
+ #interface_thread.start()\r
+ start_server()\r
+ connect_ppt()\r
+ \r
+\r
+def null_action():\r
+ pass\r
+\r
+class Interface(ttk.Frame):\r
+\r
+ def __init__(self, parent):\r
+ ttk.Frame.__init__(self, parent)\r
+\r
+ self.parent = parent\r
+\r
+ self.initUI()\r
+\r
+ def initUI(self):\r
+\r
+ self.parent.title("ppt-control")\r
+ self.style = ttk.Style()\r
+ #self.style.theme_use("default")\r
+\r
+ self.pack(fill=tk.BOTH, expand=1)\r
+\r
+ quitButton = ttk.Button(self, text="Close",\r
+ command=self.quit)\r
+ quitButton.place(x=50, y=50)\r
+ status_label = ttk.Label(self, text="PowerPoint status: not detected")\r
+ status_label.place(x=10,y=10)\r
+ \r
+ \r
+\r
+def show_icon():\r
+ menu = (pystray.MenuItem("Status", lambda: null_action(), enabled=False),\r
+ pystray.MenuItem("Restart", lambda: start()),\r
+ pystray.MenuItem("Settings", lambda: open_settings()))\r
+ icon = pystray.Icon("ppt-control", Image.open("icons/ppt.ico"), "ppt-control", menu)\r
+ icon.visible = True\r
+ icon.run(setup=start)\r
+\r
+if __name__ == "__main__":\r
+ show_icon()\r