├── PyClock.py ├── README.md └── web ├── eel.js ├── main.html └── style.css /PyClock.py: -------------------------------------------------------------------------------- 1 | import eel 2 | import datetime 3 | import time 4 | 5 | eel.init('web') #directory 6 | 7 | @eel.expose 8 | def ang(unit): #angle secod 9 | time.sleep(0.007) 10 | now = datetime.datetime.now() 11 | 12 | if unit == "now.hour": 13 | return 360/12 * eval(unit) - 90 14 | 15 | elif unit== "now.microsecond": 16 | return (360/60 * (now.second + now.microsecond/1000000)) - 90 17 | 18 | else: 19 | return 360/60 * eval(unit) - 90 20 | 21 | 22 | eel.start('main.html', block=False, size=(500, 500)) 23 | 24 | while True: 25 | eel.sleep(1.0) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyClock 2 | I know, the layout is not responsive ... the front end is boring 3 | 4 | this is a very simple example of how to use Eel to create a python GUI using web scripts (html, css, js) ... In this case it would have been easier to do everything in javascript, but I wanted to have fun with this library 5 | -------------------------------------------------------------------------------- /web/eel.js: -------------------------------------------------------------------------------- 1 | eel = { 2 | _host: window.location.origin, 3 | 4 | set_host: function (hostname) { 5 | eel._host = hostname 6 | }, 7 | 8 | expose: function(f, name) { 9 | if(name === undefined){ 10 | name = f.toString(); 11 | let i = 'function '.length, j = name.indexOf('('); 12 | name = name.substring(i, j).trim(); 13 | } 14 | 15 | eel._exposed_functions[name] = f; 16 | }, 17 | 18 | guid: function() { 19 | return eel._guid; 20 | }, 21 | 22 | // These get dynamically added by library when file is served 23 | /** _py_functions **/ 24 | /** _start_geometry **/ 25 | 26 | _guid: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => 27 | (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) 28 | ), 29 | 30 | _exposed_functions: {}, 31 | 32 | _mock_queue: [], 33 | 34 | _mock_py_functions: function() { 35 | for(let i = 0; i < eel._py_functions.length; i++) { 36 | let name = eel._py_functions[i]; 37 | eel[name] = function() { 38 | let call_object = eel._call_object(name, arguments); 39 | eel._mock_queue.push(call_object); 40 | return eel._call_return(call_object); 41 | } 42 | } 43 | }, 44 | 45 | _import_py_function: function(name) { 46 | let func_name = name; 47 | eel[name] = function() { 48 | let call_object = eel._call_object(func_name, arguments); 49 | eel._websocket.send(eel._toJSON(call_object)); 50 | return eel._call_return(call_object); 51 | } 52 | }, 53 | 54 | _call_number: 0, 55 | 56 | _call_return_callbacks: {}, 57 | 58 | _call_object: function(name, args) { 59 | let arg_array = []; 60 | for(let i = 0; i < args.length; i++){ 61 | arg_array.push(args[i]); 62 | } 63 | 64 | let call_id = (eel._call_number += 1) + Math.random(); 65 | return {'call': call_id, 'name': name, 'args': arg_array}; 66 | }, 67 | 68 | _sleep: function(ms) { 69 | return new Promise(resolve => setTimeout(resolve, ms)); 70 | }, 71 | 72 | _toJSON: function(obj) { 73 | return JSON.stringify(obj, (k, v) => v === undefined ? null : v); 74 | }, 75 | 76 | _call_return: function(call) { 77 | return function(callback = null) { 78 | if(callback != null) { 79 | eel._call_return_callbacks[call.call] = callback; 80 | } else { 81 | return new Promise(function(resolve) { 82 | eel._call_return_callbacks[call.call] = resolve; 83 | }); 84 | } 85 | } 86 | }, 87 | 88 | _position_window: function(page) { 89 | let size = eel._start_geometry['default'].size; 90 | let position = eel._start_geometry['default'].position; 91 | 92 | if(page in eel._start_geometry.pages) { 93 | size = eel._start_geometry.pages[page].size; 94 | position = eel._start_geometry.pages[page].position; 95 | } 96 | 97 | if(size != null){ 98 | window.resizeTo(size[0], size[1]); 99 | } 100 | 101 | if(position != null){ 102 | window.moveTo(position[0], position[1]); 103 | } 104 | }, 105 | 106 | _init: function() { 107 | eel._mock_py_functions(); 108 | 109 | document.addEventListener("DOMContentLoaded", function(event) { 110 | let page = window.location.pathname.substring(1); 111 | eel._position_window(page); 112 | 113 | let websocket_addr = (eel._host + '/eel').replace('http', 'ws'); 114 | websocket_addr += ('?page=' + page); 115 | eel._websocket = new WebSocket(websocket_addr); 116 | 117 | eel._websocket.onopen = function() { 118 | for(let i = 0; i < eel._py_functions.length; i++){ 119 | let py_function = eel._py_functions[i]; 120 | eel._import_py_function(py_function); 121 | } 122 | 123 | while(eel._mock_queue.length > 0) { 124 | let call = eel._mock_queue.shift(); 125 | eel._websocket.send(eel._toJSON(call)); 126 | } 127 | }; 128 | 129 | eel._websocket.onmessage = function (e) { 130 | let message = JSON.parse(e.data); 131 | if(message.hasOwnProperty('call') ) { 132 | // Python making a function call into us 133 | if(message.name in eel._exposed_functions) { 134 | let return_val = eel._exposed_functions[message.name](...message.args); 135 | eel._websocket.send(eel._toJSON({'return': message.call, 'value': return_val})); 136 | } 137 | } else if(message.hasOwnProperty('return')) { 138 | // Python returning a value to us 139 | if(message['return'] in eel._call_return_callbacks) { 140 | eel._call_return_callbacks[message['return']](message.value); 141 | } 142 | } else { 143 | throw 'Invalid message ' + message; 144 | } 145 | 146 | }; 147 | }); 148 | } 149 | } 150 | 151 | eel._init(); 152 | 153 | if(typeof require !== 'undefined'){ 154 | // Avoid name collisions when using Electron, so jQuery etc work normally 155 | window.nodeRequire = require; 156 | delete window.require; 157 | delete window.exports; 158 | delete window.module; 159 | } -------------------------------------------------------------------------------- /web/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 36 | 37 | 38 | 39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 | -------------------------------------------------------------------------------- /web/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 800px; 4 | width: 800px; 5 | background: black; 6 | } 7 | 8 | .corona{ 9 | border-radius: 50%; 10 | border-color: aquamarine; 11 | border-top-color: black; 12 | border-bottom-color: black; 13 | border-style: solid; 14 | height: 95%; 15 | width: 90%; 16 | position: absolute; 17 | transform-origin: center center; 18 | 19 | 20 | } 21 | 22 | 23 | #clock{ 24 | width: 6%; 25 | height: 6%; 26 | border-radius: 50%; 27 | background-color: white; 28 | position: absolute; 29 | left: 50%; 30 | top: 50%; 31 | transform: translate(-50%, -50%); 32 | z-index: 3; 33 | } 34 | 35 | 36 | #seconds{ 37 | background-color: lime; 38 | width: 40%; 39 | height: 2%; 40 | border-top-right-radius: 50px; 41 | border-bottom-right-radius: 50px; 42 | transform-origin: left center; 43 | position: absolute; 44 | top : 50%; 45 | left: 50%; 46 | z-index: 2; 47 | } 48 | 49 | 50 | #minutes{ 51 | background-color: darkturquoise; 52 | width: 30%; 53 | height: 3%; 54 | border-top-right-radius: 50px; 55 | border-bottom-right-radius: 50px; 56 | transform-origin: left center; 57 | position: absolute; 58 | top : 50%; 59 | left: 50%; 60 | z-index: 1; 61 | 62 | } 63 | 64 | #hours{ 65 | background-color: orangered; 66 | width: 20%; 67 | height: 4%; 68 | border-top-right-radius: 50px; 69 | border-bottom-right-radius: 50px; 70 | transform-origin: left center; 71 | position: absolute; 72 | top : 50%; 73 | left: 50%; 74 | z-index: 0; 75 | } --------------------------------------------------------------------------------