unisi 0.1.9__py3-none-any.whl → 0.1.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unisi/autotest.py +2 -3
- unisi/proxy.py +2 -2
- unisi/server.py +56 -46
- unisi/users.py +42 -26
- {unisi-0.1.9.dist-info → unisi-0.1.10.dist-info}/METADATA +102 -68
- {unisi-0.1.9.dist-info → unisi-0.1.10.dist-info}/RECORD +8 -8
- {unisi-0.1.9.dist-info → unisi-0.1.10.dist-info}/WHEEL +0 -0
- {unisi-0.1.9.dist-info → unisi-0.1.10.dist-info}/licenses/LICENSE +0 -0
unisi/autotest.py
CHANGED
@@ -202,9 +202,8 @@ def check_module(module):
|
|
202
202
|
def run_tests():
|
203
203
|
if not os.path.exists(testdir):
|
204
204
|
os.makedirs(testdir)
|
205
|
-
user = User.UserType()
|
206
|
-
user.load()
|
207
|
-
user.session = 'autotest'
|
205
|
+
user = User.UserType('autotest')
|
206
|
+
user.load()
|
208
207
|
errors = []
|
209
208
|
for module in user.screens:
|
210
209
|
errors += check_module(module)
|
unisi/proxy.py
CHANGED
@@ -26,11 +26,11 @@ message_types = ['error','warning','info']
|
|
26
26
|
|
27
27
|
class Proxy:
|
28
28
|
"""UNISI proxy"""
|
29
|
-
def __init__(self, host_port, timeout = 7, ssl = False):
|
29
|
+
def __init__(self, host_port, timeout = 7, ssl = False, session = ''):
|
30
30
|
addr_port = f'{wss_header if ssl else ws_header}{host_port}'
|
31
31
|
addr_port = f'{addr_port}{"" if addr_port.endswith("/") else "/"}{ws_path}'
|
32
32
|
self.host_port = f'{"https" if ssl else "http"}://{host_port}'
|
33
|
-
self.conn = create_connection(addr_port, timeout = timeout)
|
33
|
+
self.conn = create_connection(addr_port, timeout = timeout, header = {'session' : session})
|
34
34
|
self.screen = None
|
35
35
|
self.screens = {}
|
36
36
|
self.dialog = None
|
unisi/server.py
CHANGED
@@ -44,60 +44,70 @@ async def static_serve(request):
|
|
44
44
|
|
45
45
|
def broadcast(message, message_user):
|
46
46
|
screen = message_user.screen_module
|
47
|
-
for user in
|
47
|
+
for user in message_user.reflections:
|
48
48
|
if user is not message_user and screen is user.screen_module:
|
49
49
|
user.sync_send(message)
|
50
|
-
|
50
|
+
import gc
|
51
51
|
async def websocket_handler(request):
|
52
52
|
ws = web.WebSocketResponse()
|
53
53
|
await ws.prepare(request)
|
54
|
-
user,
|
55
|
-
|
54
|
+
user, status = make_user(request)
|
55
|
+
if not user:
|
56
|
+
await ws.send_str(toJson(status))
|
57
|
+
else:
|
58
|
+
user.transport = ws._writer.transport if divpath != '/' else None
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
async def send(res):
|
61
|
+
if type(res) != str:
|
62
|
+
res = toJson(user.prepare_result(res))
|
63
|
+
await ws.send_str(res)
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
user.log(traceback.format_exc())
|
65
|
+
user.send = send
|
66
|
+
await send(user.screen if status else empty_app)
|
67
|
+
try:
|
68
|
+
async for msg in ws:
|
69
|
+
if msg.type == WSMsgType.TEXT:
|
70
|
+
if msg.data == 'close':
|
71
|
+
await ws.close()
|
72
|
+
else:
|
73
|
+
raw_message = json.loads(msg.data)
|
74
|
+
message = None
|
75
|
+
if isinstance(raw_message, list):
|
76
|
+
if raw_message:
|
77
|
+
for raw_submessage in raw_message:
|
78
|
+
message = ReceivedMessage(raw_submessage)
|
79
|
+
result = user.result4message(message)
|
80
|
+
else:
|
81
|
+
result = Warning('Empty command batch!')
|
82
|
+
else:
|
83
|
+
message = ReceivedMessage(raw_message)
|
84
|
+
result = user.result4message(message)
|
85
|
+
await send(result)
|
86
|
+
if message:
|
87
|
+
if recorder.record_file:
|
88
|
+
recorder.accept(message, user.prepare_result (result))
|
89
|
+
if user.reflections and not is_screen_switch(message):
|
90
|
+
if result:
|
91
|
+
broadcast(result, user)
|
92
|
+
msg_object = user.find_element(message)
|
93
|
+
if not isinstance(result, Message) or not result.contains(msg_object):
|
94
|
+
broadcast(toJson(user.prepare_result(msg_object)), user)
|
95
|
+
elif msg.type == WSMsgType.ERROR:
|
96
|
+
user.log('ws connection closed with exception %s' % ws.exception())
|
97
|
+
except:
|
98
|
+
user.log(traceback.format_exc())
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
|
100
|
+
uss = User.sessions
|
101
|
+
if uss and uss.get(user.session):
|
102
|
+
del uss[user.session]
|
103
|
+
|
104
|
+
if user.reflections: #reflections is common array
|
105
|
+
if len(user.reflections) == 2:
|
106
|
+
user.reflections.clear() #1 element in user.reflections has no sense
|
107
|
+
else:
|
108
|
+
user.reflections.remove(user)
|
109
|
+
gc.collect()
|
110
|
+
return ws #?<->
|
101
111
|
|
102
112
|
def start(appname = None, user_type = User, http_handlers = []):
|
103
113
|
if appname is not None:
|
unisi/users.py
CHANGED
@@ -9,14 +9,27 @@ from threading import Thread
|
|
9
9
|
import logging
|
10
10
|
|
11
11
|
class User:
|
12
|
-
def __init__(self):
|
13
|
-
self.
|
14
|
-
self.active_dialog = None
|
15
|
-
self.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
def __init__(self, session: str, share = None):
|
13
|
+
self.session = session
|
14
|
+
self.active_dialog = None
|
15
|
+
self.last_message = None
|
16
|
+
User.last_user = self
|
17
|
+
|
18
|
+
if share:
|
19
|
+
self.screens = share.screens
|
20
|
+
self.screen_module = share.screen_module if share.screens else []
|
21
|
+
self.__handlers__ = share.__handlers__
|
22
|
+
|
23
|
+
if share.reflections:
|
24
|
+
share.reflections.append(self)
|
25
|
+
else:
|
26
|
+
share.reflections = [share, self]
|
27
|
+
self.reflections = share.reflections
|
28
|
+
else:
|
29
|
+
self.screens = []
|
30
|
+
self.reflections = []
|
31
|
+
self.screen_module = None
|
32
|
+
self.__handlers__ = {}
|
20
33
|
|
21
34
|
async def send_windows(self, obj):
|
22
35
|
await self.send(obj)
|
@@ -218,32 +231,34 @@ class User:
|
|
218
231
|
else:
|
219
232
|
self.log(f'{elem} does not contain method for {event} event type!')
|
220
233
|
return Error(f'Invalid {event} event type for {message.block}>>{message.element} is received!')
|
221
|
-
|
222
|
-
def reflect(self):
|
223
|
-
user = User.UserType()
|
224
|
-
user.screens = self.screens
|
225
|
-
if self.screens:
|
226
|
-
user.screen_module = self.screens[0]
|
227
|
-
user.__handlers__ = self.__handlers__
|
228
|
-
return user
|
229
|
-
|
234
|
+
|
230
235
|
def log(self, str, type = 'error'):
|
231
|
-
scr = self.screen.name if self.screens else '
|
236
|
+
scr = self.screen.name if self.screens else 'void'
|
232
237
|
str = f"session: {self.session}, screen: {scr}, message: {self.last_message} \n {str}"
|
233
238
|
if type == 'error':
|
234
239
|
logging.error(str)
|
235
240
|
else:
|
236
241
|
logging.warning(str)
|
237
242
|
|
238
|
-
def make_user():
|
239
|
-
|
240
|
-
|
243
|
+
def make_user(request):
|
244
|
+
session = f'{request.remote}-{User.count}'
|
245
|
+
User.count += 1
|
246
|
+
requested_connect = request.headers.get('session', '')
|
247
|
+
if requested_connect:
|
248
|
+
user = User.sessions.get(requested_connect, None)
|
249
|
+
if not user:
|
250
|
+
error = f'Session id "{requested_connect}" is unknown. Connection refused!'
|
251
|
+
logging.error(error)
|
252
|
+
return None, Error(error)
|
253
|
+
user = User.UserType(session, user)
|
254
|
+
ok = user.screens
|
255
|
+
elif config.mirror and User.last_user:
|
256
|
+
user = User.UserType(session, User.last_user)
|
241
257
|
ok = user.screens
|
242
258
|
else:
|
243
|
-
user = User.UserType()
|
244
|
-
ok = user.load()
|
245
|
-
|
246
|
-
User.reflections.append(user)
|
259
|
+
user = User.UserType(session)
|
260
|
+
ok = user.load()
|
261
|
+
User.sessions[session] = user
|
247
262
|
return user, ok
|
248
263
|
|
249
264
|
#loop and thread is for progress window and sync interactions
|
@@ -265,4 +280,5 @@ User.extra_loop = loop
|
|
265
280
|
User.UserType = User
|
266
281
|
User.last_user = None
|
267
282
|
User.toolbar = []
|
268
|
-
User.
|
283
|
+
User.sessions = {}
|
284
|
+
User.count = 0
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: unisi
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.10
|
4
4
|
Summary: Unified System Interface, GUI and Remote API
|
5
5
|
Author-Email: UNISI Tech <g.dernovoy@gmail.com>
|
6
6
|
License: Apache-2.0
|
@@ -84,7 +84,10 @@ unisi.start('Test app')
|
|
84
84
|
Unisi builds the interactive app for the code above.
|
85
85
|
Connect a browser to localhast:8000 which are by default and will see:
|
86
86
|
|
87
|
-

|
87
|
+

|
88
|
+
|
89
|
+
### 'The fastest way to create Web applications in Python.' is a free crash course video how to use UNISI ###
|
90
|
+
https://www.unisi.tech/learn
|
88
91
|
|
89
92
|
### Handling events ###
|
90
93
|
All handlers are functions which have a signature
|
@@ -110,25 +113,37 @@ clean_button = Button('Clean the table’, clean_table)
|
|
110
113
|
| Gui object array or tuple | Objects to update |
|
111
114
|
| None | Nothing to update, Ok |
|
112
115
|
| Error(...), Warning(...), Info(...) | Show to user info about a state. |
|
113
|
-
|
|
116
|
+
| True | Redraw whole screen |
|
117
|
+
|
114
118
|
| Dialog(..) | Open a dialog with parameters |
|
115
119
|
| user.set_screen(screen_name) | switch to another screen |
|
116
120
|
|
117
|
-
|
118
|
-
Unisi synchronizes GUI state on frontend-end automatically after calling a handler.
|
121
|
+
Unisi synchronizes GUI state on frontend-end automatically after calling a handler.
|
119
122
|
|
120
123
|
If a Gui object doesn't have 'changed' handler the object accepts incoming value automatically to the 'value' variable of gui object.
|
121
124
|
|
122
125
|
If 'value' is not acceptable instead of returning an object possible to return Error or Warning or Info. That functions can update a object list passed after the message argument.
|
123
126
|
|
124
127
|
```
|
125
|
-
def
|
126
|
-
if value
|
127
|
-
return Error(f‘The value
|
128
|
+
def changed(elem, value):
|
129
|
+
if value == 4:
|
130
|
+
return Error(f‘The value can not be 4!', elem)
|
128
131
|
#accept value othewise
|
129
|
-
|
132
|
+
elem.value = value
|
130
133
|
|
131
|
-
edit = Edit('
|
134
|
+
edit = Edit('Involving', 0.6, changed)
|
135
|
+
```
|
136
|
+
|
137
|
+
#### Events interception of shared blocks ####
|
138
|
+
Interception handlers have the same in/out format as usual handlers.
|
139
|
+
#### They are called before the inner element handler call. They cancel the call of inner element handler but you can call it as shown below.
|
140
|
+
For example above interception of select_mode changed event will be:
|
141
|
+
```
|
142
|
+
@handle(select_mode, 'changed')
|
143
|
+
def do_not_select_mode_x(selector, value):
|
144
|
+
if value == 'Mode X':
|
145
|
+
return Error('Do not select Mode X in this context', selector) # send old value for update select_mode to the previous state
|
146
|
+
return _.accept(value) #otherwise accept the value
|
132
147
|
```
|
133
148
|
|
134
149
|
### Block details ###
|
@@ -159,18 +174,6 @@ from blocks.tblock import concept_block
|
|
159
174
|
blocks = [.., concept_block]
|
160
175
|
```
|
161
176
|
|
162
|
-
#### Events interception of shared elements ####
|
163
|
-
Interception handlers have the same in/out format as usual handlers.
|
164
|
-
#### They are called before the inner element handler call. They cancel the call of inner element handler but you can call it as shown below.
|
165
|
-
For example above interception of select_mode changed event will be:
|
166
|
-
```
|
167
|
-
@handle(select_mode, 'changed')
|
168
|
-
def do_not_select_mode_x(selector, value):
|
169
|
-
if value == 'Mode X':
|
170
|
-
return Error('Do not select Mode X in this context', selector) # send old value for update select_mode to the previous state
|
171
|
-
return _.accept(value) #otherwise accept the value
|
172
|
-
```
|
173
|
-
|
174
177
|
#### Layout of blocks. ####
|
175
178
|
If the blocks are simply listed Unisi draws them from left to right or from top to bottom depending on the orientation setting. If a different layout is needed, it can be set according to the following rule: if the vertical area must contain more than one block, then the enumeration in the array will arrange the elements vertically one after another. If such an element enumeration is an array of blocks, then they will be drawn horizontally in the corresponding area.
|
176
179
|
|
@@ -180,6 +183,24 @@ blocks = [ [b1,b2], [b3, [b4, b5]]]
|
|
180
183
|
|
181
184
|

|
182
185
|
|
186
|
+
### ParamBlock ###
|
187
|
+
ParamBlock(name, *gui_elements, row = 3, **parameters)
|
188
|
+
|
189
|
+
ParamBlock creates blocks with Gui elements formed from parameters. Parameters can be string, bool, number and optional types. Example:
|
190
|
+
```
|
191
|
+
block = ParamBlock('Learning parameters', Button('Start learning', learn_nn)
|
192
|
+
per_device_eval_batch_size=16, num_train_epochs=10, warmup_ratio=0.1,
|
193
|
+
logging_steps=10, device = (‘cpu’,['cpu', 'gpu']),load_best = True)
|
194
|
+
```
|
195
|
+
|
196
|
+
If a string parameter has several options as a device in the example, its value is expressed as an option list and the first value is the initial value.
|
197
|
+
For optional types Select, Tree, Range the value has to contain the current value and its options. In the example
|
198
|
+
```
|
199
|
+
device = (‘cpu’,['cpu', 'gpu'])
|
200
|
+
```
|
201
|
+
means the current value of 'device' is 'cpu' and options are ['cpu', 'gpu'] .
|
202
|
+
|
203
|
+
|
183
204
|
### Basic gui elements ###
|
184
205
|
Normally they have type property which says unisi what data it contains and optionally how to draw the element.
|
185
206
|
#### If the element name starts from _ , unisi will hide its name on the screen. ####
|
@@ -200,34 +221,34 @@ causes a call changed handler if it defined, otherwise just save value to self.
|
|
200
221
|
### Button ###
|
201
222
|
Normal button.
|
202
223
|
```
|
203
|
-
Button('Push me', changed = push_callback)
|
224
|
+
Button('Push me', changed = push_callback, icon = None)
|
204
225
|
```
|
205
226
|
Short form
|
206
227
|
```
|
207
|
-
Button('Push me', push_callback)
|
228
|
+
Button('Push me', push_callback = None, icon = None)
|
208
229
|
```
|
209
|
-
Icon button
|
230
|
+
Icon button, the name has to be started from _ for hiding
|
210
231
|
```
|
211
|
-
Button('_Check', push_callback, icon =
|
232
|
+
Button('_Check', push_callback = None, icon = None)
|
212
233
|
```
|
213
234
|
|
214
235
|
### Load to server Button ###
|
215
236
|
Special button provides file loading from user device or computer to the Unisi server.
|
216
237
|
```
|
217
|
-
UploadButton('Load', handler_when_loading_finish, icon='photo_library')
|
238
|
+
UploadButton('Load', handler_when_loading_finish, icon = 'photo_library')
|
218
239
|
```
|
219
240
|
handler_when_loading_finish(button_, the_loaded_file_filename) where the_loaded_file_filename is a file name in upload server folder. This folder name is defined in config.py .
|
220
241
|
|
221
242
|
### Edit and Text field. ###
|
222
243
|
```
|
223
|
-
Edit(
|
224
|
-
Edit(
|
244
|
+
Edit(name,value = '', changed_handler = None) #for string value
|
245
|
+
Edit(name, value: number, changed_handler = None) #changed handler gets a number in the value parameter
|
225
246
|
```
|
226
|
-
If set edit = false
|
247
|
+
If set edit = false the element will be readonly.
|
227
248
|
```
|
228
249
|
Edit('Some field', '', edit = false)
|
229
|
-
#text
|
230
|
-
Text('Some
|
250
|
+
#text, it is equal
|
251
|
+
Text('Some field')
|
231
252
|
```
|
232
253
|
complete handler is optional function which accepts the current edit value and returns a string list for autocomplete.
|
233
254
|
|
@@ -241,23 +262,30 @@ Edit('Edit me', 'value', complete = get_complete_list) #value has to be string o
|
|
241
262
|
Optional 'update' handler is called when the user press Enter in the field.
|
242
263
|
It can return None if OK or objects for updating as usual 'changed' handler.
|
243
264
|
|
244
|
-
|
245
|
-
|
265
|
+
### Range ###
|
266
|
+
Number field for limited in range values.
|
267
|
+
|
268
|
+
Range('Name', value = 0, changed_handler = None, options=[min,max, step])
|
269
|
+
|
270
|
+
Example:
|
271
|
+
```
|
272
|
+
Range('Scale content', 1, options=[0.25, 3, 0.25])
|
273
|
+
```
|
246
274
|
|
247
275
|
|
248
276
|
### Radio button ###
|
249
277
|
```
|
250
|
-
Switch(name, value,
|
251
|
-
value is boolean,
|
278
|
+
Switch(name, value = False, changed_handler = None, type = 'radio')
|
279
|
+
value is boolean, changed_handler is an optional handler.
|
252
280
|
Optional type can be 'check' for a status button or 'switch' for a switcher .
|
253
281
|
```
|
254
282
|
|
255
283
|
### Select group. Contains options field. ###
|
256
284
|
```
|
257
|
-
Select(
|
285
|
+
Select(name, value = None, changed_handler = None, options = ["choice1","choice2", "choice3"])
|
258
286
|
```
|
259
|
-
Optional type parameter can be '
|
260
|
-
|
287
|
+
Optional type parameter can be 'radio','list','select'. Unisi automatically chooses between 'radio' and 'select', if type is omitted.
|
288
|
+
If type = 'list' then Unisi build it as vertical select list.
|
261
289
|
|
262
290
|
|
263
291
|
### Image. ###
|
@@ -265,28 +293,30 @@ width,changed,height,header are optional, changed is called if the user select o
|
|
265
293
|
When the user click the image, a check mark is appearing on the image, showning select status of the image.
|
266
294
|
It is usefull for image list, gallery, e.t.c
|
267
295
|
```
|
268
|
-
Image(
|
296
|
+
Image(image_path, value = False, changed_handler, header = 'description', url = ..., width = .., height = ..)
|
269
297
|
```
|
270
298
|
|
271
299
|
### Video. ###
|
272
300
|
width and height are optional.
|
273
301
|
```
|
274
|
-
Video(video_url, width =
|
302
|
+
Video(video_url, width = None, height = None)
|
275
303
|
```
|
276
304
|
|
277
305
|
### Tree. The element for tree-like data. ###
|
278
306
|
```
|
279
|
-
Tree(name,
|
307
|
+
Tree(name, value = None, changed_handler = None, options = {name1: parent1, name2 : None, .})
|
280
308
|
```
|
281
309
|
options is a tree structure, a dictionary {item_name:parent_name}.
|
282
310
|
parent_name is None for root items. changed_handler gets item key (name) as value.
|
283
311
|
|
284
312
|
### Table. ###
|
285
313
|
Tables is common structure for presenting 2D data and charts.
|
286
|
-
Optional append, delete, update handlers are called for adding, deleting and updating rows.
|
287
314
|
|
315
|
+
Table(name, value = None, changed_handler = None, **options)
|
316
|
+
|
317
|
+
Optional append, delete, update handlers are called for adding, deleting and updating handlers for a table.
|
288
318
|
|
289
|
-
|
319
|
+
Auto assigning handlers for such action can be blocked by assigning edit = False to the Table constructor.
|
290
320
|
```
|
291
321
|
table = Table('Videos', [0], row_changed, headers = ['Video', 'Duration', 'Owner', 'Status'],
|
292
322
|
rows = [
|
@@ -315,7 +345,6 @@ value = [0] means 0 row is selected in multiselect mode (in array). multimode is
|
|
315
345
|
### Table handlers. ###
|
316
346
|
complete, modify and update have the same format as the others handlers, but value is consisted from the cell value and its position in the table.
|
317
347
|
|
318
|
-
|
319
348
|
```
|
320
349
|
def table_updated(table_, tabval):
|
321
350
|
value, position = tabval
|
@@ -323,6 +352,7 @@ def table_updated(table_, tabval):
|
|
323
352
|
...
|
324
353
|
if error_found:
|
325
354
|
return Error('Can not accept the value!')
|
355
|
+
#call a standart handler
|
326
356
|
accept_rowvalue(table_, tabval)
|
327
357
|
```
|
328
358
|
|
@@ -333,45 +363,40 @@ Chart is a table with additional Table constructor parameter 'view' which explai
|
|
333
363
|
### Graph ###
|
334
364
|
Graph supports an interactive graph.
|
335
365
|
```
|
336
|
-
graph = Graph('X graph',
|
337
|
-
nodes = [
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
], edges = [
|
342
|
-
{ 'id' : 'edge1', 'source': "node1", 'target': "node2", 'name' : 'extending' },
|
343
|
-
{ 'id' :'edge2' , 'source': "node2", 'target': "node3" , 'name' : 'extending'}
|
344
|
-
])
|
345
|
-
```
|
346
|
-
where graph_value is a dictionary like {'nodes' : ["node1"], 'edges' : ['edge3']}, where enumerations are selected nodes and edges.
|
366
|
+
graph = Graph('X graph', value = None, changed_handler = None,
|
367
|
+
nodes = [ Node("Node 1"),Node("Node 2", size = 20),None, Node("Node 3", color = "#3CA072")],
|
368
|
+
edges = [ Edge(0,1, color = "#3CA072"), Edge(1,3,'extending', size = 6),Edge(3,4, size = 2), Edge(2,4)]])
|
369
|
+
```
|
370
|
+
where value is None or a dictionary like {'nodes' : [id1, ..], 'edges' : [id2, ..]}, where enumerations are selected nodes and edges.
|
347
371
|
Constant graph_default_value == {'nodes' : [], 'edges' : []} i.e. nothing to select.
|
348
372
|
|
349
|
-
'
|
373
|
+
'changed_handler' is called when the user (de)selected nodes or edges:
|
350
374
|
```
|
351
|
-
def
|
352
|
-
|
375
|
+
def changed_handler(graph, val):
|
376
|
+
graph.value = val
|
353
377
|
if 'nodes' in val:
|
354
378
|
return Info(f'Nodes {val["nodes"]}')
|
355
379
|
if 'edges' in val:
|
356
380
|
return Info(f"Edges {val['edges']}")
|
357
381
|
```
|
358
|
-
With pressed 'Shift' multi select works for nodes and edges.
|
382
|
+
With pressed 'Shift' multi (de)select works for nodes and edges.
|
359
383
|
|
360
384
|
id nodes and edges are optinal, if node ids are ommited then edge 'source' and 'target' have to point node index in nodes array.
|
385
|
+
Graph can handle invalid edges and null nodes in the nodes array.
|
361
386
|
|
362
387
|
### Dialog ###
|
363
388
|
```
|
364
|
-
Dialog(
|
389
|
+
Dialog(question, dialog_callback, commands = ['Ok', 'Cancel'], *content)
|
365
390
|
```
|
366
391
|
where buttons is a list of the dialog button names,
|
367
392
|
Dialog callback has the signature as other with a pushed button name value
|
368
393
|
```
|
369
|
-
def dialog_callback(current_dialog,
|
370
|
-
if
|
394
|
+
def dialog_callback(current_dialog, command_button_name):
|
395
|
+
if command_button_name == 'Yes':
|
371
396
|
do_this()
|
372
397
|
elif ..
|
373
398
|
```
|
374
|
-
content can be filled with Gui elements for additional dialog functionality.
|
399
|
+
content can be filled with Gui elements for additional dialog functionality like a Block.
|
375
400
|
|
376
401
|
|
377
402
|
### Popup windows ###
|
@@ -398,9 +423,9 @@ Progree window is automatically closed when the handler is finished.
|
|
398
423
|
Unisi automatically creates and serves an environment for every user.
|
399
424
|
The management class is User contains all required methods for processing and handling the user activity. A programmer can redefine methods in the inherited class, point it as system user class and that is all. Such methods suit for history navigation, undo/redo and initial operations. The screen folder contains screens which are recreated for every user. The same about blocks. The code and modules outside that folders are common for all users as usual. By default Unisi uses the system User class and you do not need to point it.
|
400
425
|
```
|
401
|
-
class Hello_user(unisi.User):
|
426
|
+
class Hello_user(unisi.User, session, share = None):
|
402
427
|
def __init__(self):
|
403
|
-
super().__init__()
|
428
|
+
super().__init__(session, share)
|
404
429
|
print('New Hello user connected and created!')
|
405
430
|
|
406
431
|
unisi.start('Hello app', user_type = Hello_user)
|
@@ -457,10 +482,19 @@ if proxy.set_screen("Image analysis"):
|
|
457
482
|
proxy.close()
|
458
483
|
```
|
459
484
|
|
485
|
+
### REST ##
|
460
486
|
|
487
|
+
The REST service facilitates the REST interface through the following approach: a programmer is required to define aiohttp routes, which consist of routes and handlers, such as web.get('/', hello), and then pass it to the Unisi start function.
|
461
488
|
|
462
|
-
|
463
|
-
|
489
|
+
```
|
490
|
+
from aiohttp import web
|
491
|
+
import unisi
|
492
|
+
async def handle_get(request):
|
493
|
+
print(request.query_string)
|
494
|
+
http_handlers = [web.get('/get', handle_get)]
|
495
|
+
unisi.start(http_handlers = http_handlers)
|
496
|
+
```
|
497
|
+
#### REST is redundant because UNISI automatically provides the Unified Remote API without programming. Can use Proxy for accessing and querying. ####
|
464
498
|
|
465
499
|
Examples are in tests folder.
|
466
500
|
|
@@ -1,8 +1,8 @@
|
|
1
|
-
unisi-0.1.
|
2
|
-
unisi-0.1.
|
3
|
-
unisi-0.1.
|
1
|
+
unisi-0.1.10.dist-info/METADATA,sha256=VJYe06r0OBYZAx_YSCYGL5RiOZECwl0TcUn0Yw2iwTY,21428
|
2
|
+
unisi-0.1.10.dist-info/WHEEL,sha256=N2J68yzZqJh3mI_Wg92rwhw0rtJDFpZj9bwQIMJgaVg,90
|
3
|
+
unisi-0.1.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
4
4
|
unisi/__init__.py,sha256=bQ7yTDcgybkbIGTjxkajTbBJXtgyryrCvt3nXg8QxlQ,175
|
5
|
-
unisi/autotest.py,sha256=
|
5
|
+
unisi/autotest.py,sha256=sB4WCyxp9TB5r0OkxeO8_jlj3END4xKOr12l2gen6Yg,9469
|
6
6
|
unisi/common.py,sha256=jsZpOkZIQdfPZjrTIoO1OLiZfQ_spEpMCX3td269RR0,635
|
7
7
|
unisi/containers.py,sha256=Me28aZkhchWJA5OExtXoFzl2u5bc6p0m2Cww7CfKIbA,4200
|
8
8
|
unisi/guielements.py,sha256=svPMIEMbhW5uH4jz5rsjTeVVFl_P7VrMniTFFQvpmzs,5998
|
@@ -11,11 +11,11 @@ unisi/jsoncomparison/compare.py,sha256=qPDaxd9n0GgiNd2SgrcRWvRDoXGg7NStViP9PHk2t
|
|
11
11
|
unisi/jsoncomparison/config.py,sha256=LbdLJE1KIebFq_tX7zcERhPvopKhnzcTqMCnS3jN124,381
|
12
12
|
unisi/jsoncomparison/errors.py,sha256=wqphE1Xn7K6n16uvUhDC45m2BxbsMUhIF2olPbhqf4o,1192
|
13
13
|
unisi/jsoncomparison/ignore.py,sha256=xfF0a_BBEyGdZBoq-ovpCpawgcX8SRwwp7IrGnu1c2w,2634
|
14
|
-
unisi/proxy.py,sha256=
|
14
|
+
unisi/proxy.py,sha256=meZ4-26KXefuaZ04Gfrexij7rreBPKLPJSwPCIqftx0,7660
|
15
15
|
unisi/reloader.py,sha256=B0f9GU452epFvdih8sH7_NiIJrXnC5i27uw2imGQxMg,6585
|
16
|
-
unisi/server.py,sha256=
|
16
|
+
unisi/server.py,sha256=v17oT4smLi4XZpsf7lVazisYOH3Ak3GPBFmsSrGt8-8,5136
|
17
17
|
unisi/tables.py,sha256=v7Fio5iIN7u1t7cE4Yvl4ewn7jTmmNPyWigoKW1Mj8U,4239
|
18
|
-
unisi/users.py,sha256=
|
18
|
+
unisi/users.py,sha256=g8YMOvDmibgu-kxvl884_WdlMAcrN2UNQseSqHGFBzc,10636
|
19
19
|
unisi/utils.py,sha256=6iDXECQE_44v8m6fvte_PXM0Ku6ZV-8LkAqMcNSMYdk,2975
|
20
20
|
unisi/web/css/353.64dbc68c.css,sha256=ckeNz4l2QkIp60LdV_-cEXf9orp0ZVklEbrQfjkAG5M,2691
|
21
21
|
unisi/web/css/app.31d6cfe0.css,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -39,4 +39,4 @@ unisi/web/js/353.ddd028b2.js,sha256=DQGZ3nBM5feVYkLf3293CmnNfIcKs8WKq8MySyO9HQA,
|
|
39
39
|
unisi/web/js/430.591e9a73.js,sha256=7S1CJTwGdE2EKXzeFRcaYuTrn0QteoVI03Hykz8qh3s,6560
|
40
40
|
unisi/web/js/app.bf75d7b6.js,sha256=2tat-f-LfOgU17bsIx4OrmVZbVdQNRhlh-cYWjPEohk,5923
|
41
41
|
unisi/web/js/vendor.d6797c01.js,sha256=2aKM3Lfxc0pHSirrcUReL1LcvDYWnfeBj2c67XsSELk,1279477
|
42
|
-
unisi-0.1.
|
42
|
+
unisi-0.1.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|