boris-behav-obs 8.27.9__py2.py3-none-any.whl → 9.0.1__py2.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.
Files changed (106) hide show
  1. boris/about.py +7 -5
  2. boris/add_modifier.py +35 -35
  3. boris/add_modifier_ui.py +229 -129
  4. boris/advanced_event_filtering.py +3 -3
  5. boris/analysis_plugins/__init__.py +0 -0
  6. boris/analysis_plugins/number_of_occurences.py +60 -0
  7. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +72 -0
  8. boris/analysis_plugins/time_budget.py +95 -0
  9. boris/behav_coding_map_creator.py +103 -108
  10. boris/behavior_binary_table.py +1 -1
  11. boris/behaviors_coding_map.py +8 -8
  12. boris/coding_pad.py +6 -6
  13. boris/config.py +6 -0
  14. boris/config_file.py +1 -1
  15. boris/connections.py +4 -2
  16. boris/converters.py +2 -3
  17. boris/converters_ui.py +187 -110
  18. boris/cooccurence.py +2 -2
  19. boris/core.py +340 -94
  20. boris/core_qrc.py +16088 -13246
  21. boris/core_ui.py +922 -812
  22. boris/db_functions.py +3 -1
  23. boris/dialog.py +14 -13
  24. boris/duration_widget.py +5 -5
  25. boris/edit_event.py +1 -1
  26. boris/edit_event_ui.py +162 -88
  27. boris/event_operations.py +4 -25
  28. boris/events_cursor.py +17 -9
  29. boris/events_snapshots.py +5 -5
  30. boris/exclusion_matrix.py +1 -1
  31. boris/export_events.py +38 -28
  32. boris/export_observation.py +1 -1
  33. boris/external_processes.py +3 -5
  34. boris/geometric_measurement.py +49 -26
  35. boris/gui_utilities.py +31 -30
  36. boris/import_observations.py +2 -4
  37. boris/irr.py +1 -1
  38. boris/latency.py +1 -1
  39. boris/map_creator.py +77 -89
  40. boris/measurement_widget.py +4 -4
  41. boris/media_file.py +2 -4
  42. boris/menu_options.py +1 -3
  43. boris/modifiers_coding_map.py +4 -4
  44. boris/mpv2.py +0 -2
  45. boris/observation.py +124 -29
  46. boris/observation_operations.py +18 -40
  47. boris/observation_ui.py +566 -374
  48. boris/observations_list.py +6 -6
  49. boris/param_panel.py +2 -2
  50. boris/param_panel_ui.py +246 -141
  51. boris/player_dock_widget.py +16 -21
  52. boris/plot_data_module.py +6 -6
  53. boris/plot_events_rt.py +7 -8
  54. boris/plot_spectrogram_rt.py +7 -8
  55. boris/plot_waveform_rt.py +6 -7
  56. boris/plugins.py +79 -0
  57. boris/preferences.py +127 -17
  58. boris/preferences_ui.py +464 -240
  59. boris/project.py +69 -72
  60. boris/project_functions.py +233 -31
  61. boris/project_import_export.py +59 -67
  62. boris/project_ui.py +672 -440
  63. boris/qrc_boris.py +6 -3
  64. boris/qrc_boris5.py +6 -3
  65. boris/select_modifiers.py +2 -2
  66. boris/select_observations.py +2 -2
  67. boris/select_subj_behav.py +3 -3
  68. boris/state_events.py +1 -1
  69. boris/subjects_pad.py +5 -5
  70. boris/synthetic_time_budget.py +2 -2
  71. boris/time_budget_functions.py +15 -0
  72. boris/time_budget_widget.py +4 -4
  73. boris/transitions.py +34 -25
  74. boris/utilities.py +95 -2
  75. boris/version.py +2 -2
  76. boris/video_equalizer.py +4 -4
  77. boris/video_equalizer_ui.py +199 -130
  78. boris/video_operations.py +1 -1
  79. boris/view_df.py +106 -0
  80. boris/view_df_ui.py +75 -0
  81. boris/write_event.py +9 -1
  82. {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/METADATA +5 -5
  83. boris_behav_obs-9.0.1.dist-info/RECORD +103 -0
  84. {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/WHEEL +1 -1
  85. boris/qdarkstyle/__init__.py +0 -479
  86. boris/qdarkstyle/__main__.py +0 -66
  87. boris/qdarkstyle/colorsystem.py +0 -38
  88. boris/qdarkstyle/dark/__init__.py +0 -1
  89. boris/qdarkstyle/dark/darkstyle_rc.py +0 -11379
  90. boris/qdarkstyle/dark/palette.py +0 -38
  91. boris/qdarkstyle/example/__init__.py +0 -4
  92. boris/qdarkstyle/example/__main__.py +0 -386
  93. boris/qdarkstyle/example/ui/__init__.py +0 -4
  94. boris/qdarkstyle/light/__init__.py +0 -1
  95. boris/qdarkstyle/light/lightstyle_rc.py +0 -11305
  96. boris/qdarkstyle/light/palette.py +0 -37
  97. boris/qdarkstyle/palette.py +0 -102
  98. boris/qdarkstyle/utils/__init__.py +0 -73
  99. boris/qdarkstyle/utils/__main__.py +0 -96
  100. boris/qdarkstyle/utils/images.py +0 -449
  101. boris/qdarkstyle/utils/scss.py +0 -318
  102. boris/vlc_local.py +0 -83
  103. boris_behav_obs-8.27.9.dist-info/RECORD +0 -114
  104. {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/LICENSE.TXT +0 -0
  105. {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/entry_points.txt +0 -0
  106. {boris_behav_obs-8.27.9.dist-info → boris_behav_obs-9.0.1.dist-info}/top_level.txt +0 -0
@@ -1,449 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Utilities to process and convert svg images to png using palette colors.
4
- """
5
-
6
- # Standard library imports
7
- import logging
8
- import os
9
- import re
10
- import sys
11
- import tempfile
12
- import subprocess
13
-
14
- # Third party imports
15
- from qtpy.QtCore import QSize
16
- from qtpy.QtGui import QIcon
17
- from qtpy.QtWidgets import QApplication
18
-
19
- # Local imports
20
- from qdarkstyle import (IMAGES_PATH, PACKAGE_PATH, QRC_FILE_SUFFIX, QSS_FILE_SUFFIX, QSS_PATH,
21
- STYLES_SCSS_FILE, SVG_PATH)
22
-
23
-
24
- IMAGE_BLACKLIST = ['base_palette']
25
-
26
- TEMPLATE_QRC_HEADER = '''
27
- <RCC warning="WARNING! File created programmatically. All changes made in this file will be lost!">
28
- <qresource prefix="{resource_prefix}">
29
- '''
30
-
31
- TEMPLATE_QRC_FILE = ' <file>rc/{fname}</file>'
32
-
33
- TEMPLATE_QRC_FOOTER = '''
34
- </qresource>
35
- <qresource prefix="{style_prefix}">
36
- <file>{qss_file}</file>
37
- </qresource>
38
- </RCC>
39
- '''
40
-
41
- _logger = logging.getLogger(__name__)
42
-
43
-
44
- def _get_file_color_map(fname, palette):
45
- """
46
- Return map of files (i.e states) to color from given palette.
47
- """
48
- color_disabled = palette.COLOR_BACKGROUND_4
49
- color_focus = palette.COLOR_ACCENT_5
50
- color_pressed = palette.COLOR_ACCENT_2
51
- color_normal = palette.COLOR_TEXT_1
52
-
53
- name, ext = fname.split('.')
54
- file_colors = {
55
- fname: color_normal,
56
- name + '_disabled.' + ext: color_disabled,
57
- name + '_focus.' + ext: color_focus,
58
- name + '_pressed.' + ext: color_pressed,
59
- }
60
-
61
- return file_colors
62
-
63
-
64
- def _create_colored_svg(svg_path, temp_svg_path, color):
65
- """
66
- Replace base svg with fill color.
67
- """
68
- with open(svg_path, 'r') as fh:
69
- data = fh.read()
70
-
71
- base_color = '#ff0000' # Hardcoded in base svg files
72
- new_data = data.replace(base_color, color)
73
-
74
- with open(temp_svg_path, 'w') as fh:
75
- fh.write(new_data)
76
-
77
-
78
- def convert_svg_to_png(svg_path, png_path, height, width):
79
- """
80
- Convert svg files to png files using Qt.
81
- """
82
- size = QSize(height, width)
83
- icon = QIcon(svg_path)
84
- pixmap = icon.pixmap(size)
85
- img = pixmap.toImage()
86
- dirname = os.path.dirname(png_path)
87
-
88
- if not os.path.exists(dirname):
89
- os.makedirs(dirname)
90
-
91
- img.save(os.path.abspath(png_path))
92
-
93
-
94
- def create_palette_image(base_svg_path=SVG_PATH, path=IMAGES_PATH,
95
- palette=None):
96
- """
97
- Create palette image svg and png image on specified path.
98
- """
99
- # Needed to use QPixmap
100
- _ = QApplication([])
101
-
102
- if palette is None:
103
- _logger.error("Please pass a palette class in order to create its "
104
- "associated images")
105
- sys.exit(1)
106
-
107
- if palette.ID is None:
108
- _logger.error("A QDarkStyle palette requires an ID!")
109
- sys.exit(1)
110
-
111
- base_palette_svg_path = os.path.join(base_svg_path, 'base_palette.svg')
112
- palette_svg_path = os.path.join(path, palette.ID, 'palette.svg')
113
- palette_png_path = os.path.join(path, palette.ID, 'palette.png')
114
-
115
- _logger.info("Creating palette image ...")
116
- _logger.info(f"Base SVG: {base_palette_svg_path}")
117
- _logger.info(f"To SVG: {palette_svg_path}")
118
- _logger.info(f"To PNG: {palette_png_path}")
119
-
120
- with open(base_palette_svg_path, 'r') as fh:
121
- data = fh.read()
122
-
123
- color_palette = palette.color_palette()
124
-
125
- for color_name, color_value in color_palette.items():
126
- data = data.replace('{{ ' + color_name + ' }}', color_value.lower())
127
-
128
- with open(palette_svg_path, 'w+') as fh:
129
- fh.write(data)
130
-
131
- convert_svg_to_png(palette_svg_path, palette_png_path, 4000, 4000)
132
-
133
- return palette_svg_path, palette_png_path
134
-
135
-
136
- def create_images(base_svg_path=SVG_PATH, rc_path=None, palette=None):
137
- """Create resources `rc` png image files from base svg files and palette.
138
-
139
- Search all SVG files in `base_svg_path` excluding IMAGE_BLACKLIST,
140
- change its colors using `palette` creating temporary SVG files, for each
141
- state generating PNG images for each size `heights`.
142
-
143
- Args:
144
- base_svg_path (str, optional): [description]. Defaults to SVG_PATH.
145
- rc_path (str, optional): [description].
146
- palette (Palette, optional): Palette.
147
- """
148
-
149
- # Needed to use QPixmap
150
- _ = QApplication([])
151
-
152
- if palette is None:
153
- _logger.error("Please pass a palette class in order to create its "
154
- "associated file")
155
- sys.exit(1)
156
-
157
- if palette.ID is None:
158
- _logger.error("A QDarkStyle palette requires an ID!")
159
- sys.exit(1)
160
-
161
- if not rc_path:
162
- rc_path = os.path.join(PACKAGE_PATH, palette.ID, 'rc')
163
-
164
- temp_dir = tempfile.mkdtemp()
165
- svg_fnames = [f for f in os.listdir(base_svg_path) if f.endswith('.svg')]
166
- base_height = 32
167
-
168
- # See: https://doc.qt.io/qt-5/scalability.html
169
- heights = {
170
- 32: '.png',
171
- 64: '@2x.png',
172
- }
173
-
174
- _logger.info("Creating images ...")
175
- _logger.info(f"SVG folder: {base_svg_path}")
176
- _logger.info(f"TMP folder: {temp_dir}")
177
- _logger.info(f"PNG folder: {rc_path}")
178
-
179
- num_svg = len(svg_fnames)
180
- num_png = 0
181
- num_ignored = 0
182
- num_ignored_list = []
183
-
184
- # Get rc links from scss to check matches
185
- rc_list = get_rc_links_from_scss()
186
- num_rc_list = len(rc_list)
187
-
188
- for height, ext in heights.items():
189
- width = height
190
-
191
- _logger.debug(f" Size HxW (px): {height} X {width}")
192
-
193
- for svg_fname in svg_fnames:
194
- svg_name = svg_fname.split('.')[0]
195
-
196
- # Skip blacklist
197
- if svg_name not in IMAGE_BLACKLIST:
198
- svg_path = os.path.join(base_svg_path, svg_fname)
199
- color_files = _get_file_color_map(svg_fname, palette=palette)
200
-
201
- _logger.log(logging.NOTSET, f" Working on: {os.path.basename(svg_fname)}")
202
-
203
- # Replace colors and create all file for different states
204
- for color_svg_name, color in color_files.items():
205
- temp_svg_path = os.path.join(temp_dir, color_svg_name)
206
- _create_colored_svg(svg_path, temp_svg_path, color)
207
-
208
- png_fname = color_svg_name.replace('.svg', ext)
209
- png_path = os.path.join(rc_path, png_fname)
210
- convert_svg_to_png(temp_svg_path, png_path, height, width)
211
- num_png += 1
212
- _logger.log(logging.NOTSET, f" Creating: {os.path.basename(png_fname)}")
213
-
214
- # Check if the rc_name is in the rc_list from scss
215
- # only for the base size
216
- if height == base_height:
217
- rc_base = os.path.basename(rc_path)
218
- png_base = os.path.basename(png_fname)
219
- rc_name = '/' + rc_base + '/' + png_base
220
- try:
221
- rc_list.remove(rc_name)
222
- except ValueError:
223
- pass
224
- else:
225
- num_ignored += 1
226
- _logger.debug(f" Ignored blacklist: {os.path.basename(svg_fname)}")
227
- num_ignored_list.append(svg_fname)
228
-
229
- _logger.info(f"# SVG files: {num_svg}")
230
- _logger.info(f"# SVG ignored: {num_ignored}")
231
- _logger.info(f"SVG ignored: {num_ignored_list}")
232
- _logger.info(f"# PNG files: {num_png}")
233
- _logger.info(f"# RC links: {num_rc_list}")
234
- _logger.info(f"# RC links in _style.scss not in RC: {len(rc_list)}")
235
- _logger.info(f"RC links in _style.scss not in RC: {rc_list}")
236
-
237
-
238
- def generate_qrc_file(resource_prefix='qss_icons', style_prefix='qdarkstyle',
239
- palette=None):
240
- """
241
- Generate the QRC file programmatically.
242
-
243
- Search all RC folder for PNG images and create a QRC file.
244
-
245
- Args:
246
- resource_prefix (str, optional): Prefix used in resources.
247
- Defaults to 'qss_icons'.
248
- style_prefix (str, optional): Prefix used to this style.
249
- Defaults to 'qdarkstyle'.
250
- palette (Palette, optional): Palette.
251
- """
252
-
253
- files = []
254
-
255
- if palette is None:
256
- _logger.error("Please pass a palette class in order to create its "
257
- "qrc file")
258
- sys.exit(1)
259
-
260
- if palette.ID is None:
261
- _logger.error("A QDarkStyle palette requires an ID!")
262
- sys.exit(1)
263
-
264
- palette_path = os.path.join(PACKAGE_PATH, palette.ID)
265
- rc_path = os.path.join(palette_path, 'rc')
266
- qss_file = palette.ID + QSS_FILE_SUFFIX
267
- qrc_file = palette.ID + QRC_FILE_SUFFIX
268
- qrc_filepath = os.path.join(palette_path, qrc_file)
269
- resource_prefix = resource_prefix + '/' + palette.ID
270
- style_prefix = style_prefix + '/' + palette.ID
271
-
272
- _logger.info("Generating QRC file ...")
273
- _logger.info(f"Resource prefix: {resource_prefix}")
274
- _logger.info(f"Style prefix: {style_prefix}")
275
-
276
- _logger.info(f"Searching in: {rc_path}")
277
-
278
- # Search by png images
279
- for fname in sorted(os.listdir(rc_path)):
280
- if os.path.splitext(fname)[1] == '.png':
281
- files.append(TEMPLATE_QRC_FILE.format(fname=fname))
282
-
283
- # Join parts
284
- qrc_content = (TEMPLATE_QRC_HEADER.format(resource_prefix=resource_prefix)
285
- + '\n'.join(files)
286
- + TEMPLATE_QRC_FOOTER.format(style_prefix=style_prefix, qss_file=qss_file))
287
-
288
- _logger.info(f"Writing in: {qrc_filepath}")
289
-
290
- # Write qrc file
291
- with open(qrc_filepath, 'w') as fh:
292
- fh.write(qrc_content)
293
-
294
-
295
- def get_rc_links_from_scss(pattern=r"\/rc.*\.png"):
296
- """
297
- Get all rc links from scss file returning the list of unique links.
298
-
299
- Args:
300
- pattern (str): regex pattern to find the links.
301
-
302
- Returns:
303
- list(str): list of unique links found.
304
- """
305
-
306
- style_scss_filepath = os.path.join(QSS_PATH, STYLES_SCSS_FILE)
307
-
308
- with open(style_scss_filepath, 'r') as fh:
309
- data = fh.read()
310
-
311
- lines = data.split("\n")
312
- compiled_exp = re.compile('(' + pattern + ')')
313
-
314
- rc_list = []
315
-
316
- for line in lines:
317
- match = re.search(compiled_exp, line)
318
- if match:
319
- path = match.group(1)
320
- rc_list.append(match.group(1))
321
-
322
- rc_list = list(set(rc_list))
323
-
324
- return rc_list
325
-
326
-
327
- def compile_qrc_file(compile_for='qtpy', qrc_path=None, palette=None):
328
- """
329
- Compile the QRC file converting it to _rc.py nad/or .rcc.
330
-
331
- When using an abstraction layer (QtPy/pyqtgraph) over a binging
332
- (PySide/PyQt), in the end, it changes the importing name.
333
-
334
- For all other `compile_for` that not 'qtpy', it prefixes the file name
335
- with `compile_for` value.
336
-
337
- Args:
338
- compile_for (list, optional): Prefix used in resources.
339
- Defaults to 'qtpy'. Possible values are 'qtpy', 'pyqtgraph',
340
- 'pyqt', 'pyqt5', 'pyside', 'pyside2', 'qt', 'qt5', 'all'.
341
- qrc_path (str, optional): .qrc folder path.
342
- Defaults to None.
343
- palette (Palette, optional): Palette.
344
- """
345
-
346
- if palette is None:
347
- _logger.error("Please pass a palette class in order to create its "
348
- "associated file")
349
- sys.exit(1)
350
-
351
- if palette.ID is None:
352
- _logger.error("A QDarkStyle palette requires an ID!")
353
- sys.exit(1)
354
-
355
- if not qrc_path:
356
- qrc_path = os.path.join(PACKAGE_PATH, palette.ID)
357
-
358
- qrc_file = palette.ID + QRC_FILE_SUFFIX
359
-
360
- # get name without extension
361
- filename = os.path.splitext(qrc_file)[0]
362
-
363
- ext = '_rc.py'
364
- ext_c = '.rcc'
365
-
366
- # creating names
367
- py_file_pyqt6 = 'pyqt6_' + filename + ext
368
- py_file_pyqt5 = 'pyqt5_' + filename + ext
369
- py_file_pyqt = 'pyqt_' + filename + ext
370
- py_file_pyside6 = 'pyside6_' + filename + ext
371
- py_file_pyside2 = 'pyside2_' + filename + ext
372
- py_file_pyside = 'pyside_' + filename + ext
373
- py_file_qtpy = '' + filename + ext
374
- py_file_pyqtgraph = 'pyqtgraph_' + filename + ext
375
-
376
- # it is simple to change the directory, otherwise we need to add
377
- # more arguments for each compiler
378
- old_cwd = os.getcwd()
379
- os.chdir(qrc_path)
380
-
381
- # Shell kwarg to pass to subprocess
382
- shell = True if os.name == 'nt' else False
383
-
384
- # calling external commands
385
- if compile_for in ['pyqt', 'pyqtgraph', 'all']:
386
- _logger.info("Compiling using PyQt4 ...")
387
- try:
388
- subprocess.call(['pyrcc4', '-py3', qrc_file, '-o', py_file_pyqt], shell=shell)
389
- except FileNotFoundError:
390
- _logger.error("You must install pyrcc4")
391
-
392
- if compile_for in ['pyqt5', 'qtpy','all']:
393
- _logger.info("Compiling using PyQt5 ...")
394
- try:
395
- subprocess.call(['pyrcc5', qrc_file, '-o', py_file_pyqt5], shell=shell)
396
- except FileNotFoundError:
397
- _logger.error("You must install pyrcc5")
398
-
399
- if compile_for in ['pyside', 'all']:
400
- _logger.info("Compiling using PySide ...")
401
- try:
402
- subprocess.call(['pyside-rcc', '-py3', qrc_file, '-o', py_file_pyside], shell=shell)
403
- except FileNotFoundError:
404
- _logger.error("You must install pyside-rcc")
405
-
406
- if compile_for in ['pyside2', 'all']:
407
- _logger.info("Compiling using PySide 2...")
408
- try:
409
- subprocess.call(['pyside2-rcc', qrc_file, '-o', py_file_pyside2], shell=shell)
410
- except FileNotFoundError:
411
- _logger.error("You must install pyside2-rcc")
412
-
413
- if compile_for in ['pyside6', 'all']:
414
- _logger.info("Compiling using PySide 6...")
415
- try:
416
- subprocess.call(['pyside6-rcc', '-g', 'python', qrc_file, '-o', py_file_pyside6], shell=shell)
417
- except FileNotFoundError:
418
- _logger.error("You must install pyside6-rcc")
419
-
420
- if compile_for in ['qtpy', 'all']:
421
- _logger.info("Converting for QtPy ...")
422
- # special case - qtpy - syntax is PyQt5
423
- with open(py_file_pyqt5, 'r') as file:
424
- filedata = file.read()
425
-
426
- # replace the target string
427
- filedata = filedata.replace('from PyQt5', 'from qtpy')
428
-
429
- with open(py_file_qtpy, 'w+') as file:
430
- # write the file out again
431
- file.write(filedata)
432
-
433
- if compile_for not in ['pyqt5']:
434
- os.remove(py_file_pyqt5)
435
-
436
- if compile_for in ['pyqtgraph', 'all']:
437
- _logger.info("Converting for PyQtGraph ...")
438
- # special case - pyqtgraph - syntax is PyQt4
439
- with open(py_file_pyqt, 'r') as file:
440
- filedata = file.read()
441
-
442
- # replace the target string
443
- filedata = filedata.replace('from PyQt4', 'from pyqtgraph.Qt')
444
-
445
- with open(py_file_pyqtgraph, 'w+') as file:
446
- # write the file out again
447
- file.write(filedata)
448
-
449
- os.chdir(old_cwd)