boris-behav-obs 9.7.7__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 (109) hide show
  1. boris/__init__.py +26 -0
  2. boris/__main__.py +25 -0
  3. boris/about.py +143 -0
  4. boris/add_modifier.py +635 -0
  5. boris/add_modifier_ui.py +303 -0
  6. boris/advanced_event_filtering.py +455 -0
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +1110 -0
  18. boris/behavior_binary_table.py +305 -0
  19. boris/behaviors_coding_map.py +239 -0
  20. boris/boris_cli.py +340 -0
  21. boris/cmd_arguments.py +49 -0
  22. boris/coding_pad.py +280 -0
  23. boris/config.py +785 -0
  24. boris/config_file.py +356 -0
  25. boris/connections.py +409 -0
  26. boris/converters.py +333 -0
  27. boris/converters_ui.py +225 -0
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +5901 -0
  30. boris/core_qrc.py +15958 -0
  31. boris/core_ui.py +1107 -0
  32. boris/db_functions.py +324 -0
  33. boris/dev.py +134 -0
  34. boris/dialog.py +1108 -0
  35. boris/duration_widget.py +238 -0
  36. boris/edit_event.py +245 -0
  37. boris/edit_event_ui.py +233 -0
  38. boris/event_operations.py +1040 -0
  39. boris/events_cursor.py +61 -0
  40. boris/events_snapshots.py +596 -0
  41. boris/exclusion_matrix.py +141 -0
  42. boris/export_events.py +1006 -0
  43. boris/export_observation.py +1203 -0
  44. boris/external_processes.py +332 -0
  45. boris/geometric_measurement.py +941 -0
  46. boris/gui_utilities.py +135 -0
  47. boris/image_overlay.py +72 -0
  48. boris/import_observations.py +242 -0
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +634 -0
  51. boris/latency.py +244 -0
  52. boris/measurement_widget.py +161 -0
  53. boris/media_file.py +115 -0
  54. boris/menu_options.py +213 -0
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +157 -0
  57. boris/mpv.py +2016 -0
  58. boris/mpv2.py +2193 -0
  59. boris/observation.py +1453 -0
  60. boris/observation_operations.py +2538 -0
  61. boris/observation_ui.py +679 -0
  62. boris/observations_list.py +337 -0
  63. boris/otx_parser.py +442 -0
  64. boris/param_panel.py +201 -0
  65. boris/param_panel_ui.py +305 -0
  66. boris/player_dock_widget.py +198 -0
  67. boris/plot_data_module.py +536 -0
  68. boris/plot_events.py +634 -0
  69. boris/plot_events_rt.py +237 -0
  70. boris/plot_spectrogram_rt.py +316 -0
  71. boris/plot_waveform_rt.py +230 -0
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +31 -0
  74. boris/portion/const.py +95 -0
  75. boris/portion/dict.py +365 -0
  76. boris/portion/func.py +52 -0
  77. boris/portion/interval.py +581 -0
  78. boris/portion/io.py +181 -0
  79. boris/preferences.py +510 -0
  80. boris/preferences_ui.py +770 -0
  81. boris/project.py +2007 -0
  82. boris/project_functions.py +2041 -0
  83. boris/project_import_export.py +1096 -0
  84. boris/project_ui.py +794 -0
  85. boris/qrc_boris.py +10389 -0
  86. boris/qrc_boris5.py +2579 -0
  87. boris/select_modifiers.py +312 -0
  88. boris/select_observations.py +210 -0
  89. boris/select_subj_behav.py +286 -0
  90. boris/state_events.py +197 -0
  91. boris/subjects_pad.py +106 -0
  92. boris/synthetic_time_budget.py +290 -0
  93. boris/time_budget_functions.py +1136 -0
  94. boris/time_budget_widget.py +1039 -0
  95. boris/transitions.py +365 -0
  96. boris/utilities.py +1810 -0
  97. boris/version.py +24 -0
  98. boris/video_equalizer.py +159 -0
  99. boris/video_equalizer_ui.py +248 -0
  100. boris/video_operations.py +310 -0
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.7.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.7.dist-info/RECORD +109 -0
  106. boris_behav_obs-9.7.7.dist-info/WHEEL +5 -0
  107. boris_behav_obs-9.7.7.dist-info/entry_points.txt +2 -0
  108. boris_behav_obs-9.7.7.dist-info/licenses/LICENSE.TXT +674 -0
  109. boris_behav_obs-9.7.7.dist-info/top_level.txt +1 -0
boris/converters.py ADDED
@@ -0,0 +1,333 @@
1
+ """
2
+ BORIS
3
+ Behavioral Observation Research Interactive Software
4
+ Copyright 2012-2025 Olivier Friard
5
+
6
+ This file is part of BORIS.
7
+
8
+ BORIS is free software; you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation; either version 3 of the License, or
11
+ any later version.
12
+
13
+ BORIS is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with this program; if not see <http://www.gnu.org/licenses/>.
20
+
21
+ """
22
+
23
+ import sys
24
+ import json
25
+ import urllib.request
26
+
27
+
28
+ from PySide6.QtWidgets import QMessageBox, QTableWidgetItem, QFileDialog, QInputDialog, QLineEdit
29
+
30
+ from . import dialog
31
+ from . import config as cfg
32
+
33
+
34
+ def pb_code_help_clicked(self):
35
+ """
36
+ help for writing converters
37
+ """
38
+ msg = QMessageBox()
39
+ msg.setIcon(QMessageBox.Information)
40
+ msg.setWindowTitle("Help for writing converters")
41
+
42
+ msg.setText(
43
+ (
44
+ "A converter is a function that will convert a time value from external data into seconds.<br>"
45
+ "A time value like 00:23:59 must be converted into seconds before to be plotted synchronously with your media.<br>"
46
+ "For this you can use BORIS native converters or write your own converter.<br>"
47
+ 'A converter must be written using the <a href="www.python.org">Python3</a> language.<br>'
48
+ )
49
+ )
50
+
51
+ # msg.setInformativeText("This is additional information")
52
+
53
+ msg.setStandardButtons(QMessageBox.Ok)
54
+ msg.exec_()
55
+
56
+
57
+ def add_converter(self):
58
+ """
59
+ Add a new converter
60
+ """
61
+
62
+ for w in [
63
+ self.le_converter_name,
64
+ self.le_converter_description,
65
+ self.pteCode,
66
+ self.pb_save_converter,
67
+ self.pb_cancel_converter,
68
+ ]:
69
+ w.setEnabled(True)
70
+ # disable buttons
71
+ for w in [
72
+ self.pb_add_converter,
73
+ self.pb_modify_converter,
74
+ self.pb_delete_converter,
75
+ self.pb_load_from_file,
76
+ self.pb_load_from_repo,
77
+ self.tw_converters,
78
+ ]:
79
+ w.setEnabled(False)
80
+
81
+
82
+ def modify_converter(self):
83
+ """
84
+ Modifiy the selected converter
85
+ """
86
+
87
+ if not self.tw_converters.selectedIndexes():
88
+ QMessageBox.warning(self, cfg.programName, "Select a converter in the table")
89
+ return
90
+
91
+ for w in [
92
+ self.le_converter_name,
93
+ self.le_converter_description,
94
+ self.pteCode,
95
+ self.pb_save_converter,
96
+ self.pb_cancel_converter,
97
+ ]:
98
+ w.setEnabled(True)
99
+
100
+ # disable buttons
101
+ for w in [
102
+ self.pb_add_converter,
103
+ self.pb_modify_converter,
104
+ self.pb_delete_converter,
105
+ self.pb_load_from_file,
106
+ self.pb_load_from_repo,
107
+ self.tw_converters,
108
+ ]:
109
+ w.setEnabled(False)
110
+
111
+ self.le_converter_name.setText(self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 0).text())
112
+ self.le_converter_description.setText(self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 1).text())
113
+ self.pteCode.setPlainText(self.tw_converters.item(self.tw_converters.selectedIndexes()[0].row(), 2).text().replace("@", "\n"))
114
+
115
+ self.row_in_modification = self.tw_converters.selectedIndexes()[0].row()
116
+
117
+
118
+ def code_2_func(self, name: str, code: str):
119
+ """
120
+ convert code to function
121
+
122
+ Args:
123
+ name (str): function name
124
+ code (str): Python code
125
+
126
+ Returns:
127
+ str: string containing Python function
128
+ """
129
+
130
+ function = f"def {name}(INPUT):\n"
131
+ function += """ INPUT = INPUT.decode("utf-8") if isinstance(INPUT, bytes) else INPUT\n"""
132
+ function += "\n".join([" " + row for row in code.split("\n")])
133
+ function += "\n return OUTPUT"
134
+
135
+ return function
136
+
137
+
138
+ def save_converter(self):
139
+ """Save converter"""
140
+
141
+ # check if name
142
+ self.le_converter_name.setText(self.le_converter_name.text().strip())
143
+ if not self.le_converter_name.text():
144
+ QMessageBox.critical(self, "BORIS", "The converter must have a name")
145
+ return
146
+
147
+ if not self.le_converter_name.text().replace("_", "a").isalnum():
148
+ QMessageBox.critical(self, "BORIS", "Forbidden characters are used in converter name.<br>Use a..z, A..Z, 0..9 _")
149
+ return
150
+
151
+ # test code with exec
152
+ code: str = self.pteCode.toPlainText()
153
+ if not code:
154
+ QMessageBox.critical(self, "BORIS", "The converter must have Python code")
155
+ return
156
+
157
+ function = code_2_func(self, name=self.le_converter_name.text(), code=code)
158
+
159
+ try:
160
+ exec(function)
161
+ except Exception:
162
+ QMessageBox.critical(self, "BORIS", f"The code produces an error:<br><b>{sys.exc_info()[1]}</b>")
163
+ return
164
+
165
+ if self.row_in_modification == -1:
166
+ self.tw_converters.setRowCount(self.tw_converters.rowCount() + 1)
167
+ row = self.tw_converters.rowCount() - 1
168
+ else:
169
+ row = self.row_in_modification
170
+
171
+ self.tw_converters.setItem(row, 0, QTableWidgetItem(self.le_converter_name.text()))
172
+ self.tw_converters.setItem(row, 1, QTableWidgetItem(self.le_converter_description.text()))
173
+ self.tw_converters.setItem(row, 2, QTableWidgetItem(self.pteCode.toPlainText().replace("\n", "@")))
174
+
175
+ self.row_in_modification = -1
176
+
177
+ for w in [self.le_converter_name, self.le_converter_description, self.pteCode]:
178
+ w.setEnabled(False)
179
+ w.clear()
180
+ self.pb_save_converter.setEnabled(False)
181
+ self.pb_cancel_converter.setEnabled(False)
182
+ self.tw_converters.setEnabled(True)
183
+
184
+ self.flag_modified = True
185
+
186
+ # enable buttons
187
+ for w in [
188
+ self.pb_add_converter,
189
+ self.pb_modify_converter,
190
+ self.pb_delete_converter,
191
+ self.pb_load_from_file,
192
+ self.pb_load_from_repo,
193
+ self.tw_converters,
194
+ ]:
195
+ w.setEnabled(True)
196
+
197
+
198
+ def cancel_converter(self):
199
+ """Cancel converter"""
200
+
201
+ for w in [self.le_converter_name, self.le_converter_description, self.pteCode]:
202
+ w.setEnabled(False)
203
+ w.clear()
204
+ self.pb_save_converter.setEnabled(False)
205
+ self.pb_cancel_converter.setEnabled(False)
206
+
207
+ # enable buttons
208
+ for w in [
209
+ self.pb_add_converter,
210
+ self.pb_modify_converter,
211
+ self.pb_delete_converter,
212
+ self.pb_load_from_file,
213
+ self.pb_load_from_repo,
214
+ self.tw_converters,
215
+ ]:
216
+ w.setEnabled(True)
217
+
218
+
219
+ def delete_converter(self):
220
+ """
221
+ Delete selected converter
222
+ """
223
+
224
+ if self.tw_converters.selectedIndexes():
225
+ if dialog.MessageDialog("BORIS", "Confirm converter deletion", [cfg.CANCEL, cfg.OK]) == cfg.OK:
226
+ self.tw_converters.removeRow(self.tw_converters.selectedIndexes()[0].row())
227
+ else:
228
+ QMessageBox.warning(self, cfg.programName, "Select a converter in the table")
229
+
230
+
231
+ def load_converters_from_file_repo(self, mode: str):
232
+ """
233
+ Load converters from file (JSON) or BORIS remote repository
234
+ Args:
235
+ mode (str): string "repo" or "file"
236
+ """
237
+
238
+ converters_from_file = {}
239
+ if mode == "file":
240
+ file_name, _ = QFileDialog.getOpenFileName(self, "Load converters from file", "", "All files (*)")
241
+
242
+ if file_name:
243
+ with open(file_name, "r") as f_in:
244
+ try:
245
+ converters_from_file = json.loads(f_in.read())["BORIS converters"]
246
+ except Exception:
247
+ QMessageBox.critical(self, cfg.programName, "This file does not contain converters...")
248
+ return
249
+
250
+ if mode == "repo":
251
+ converters_repo_URL = "https://www.boris.unito.it/static/converters.json"
252
+ try:
253
+ converters_from_repo = urllib.request.urlopen(converters_repo_URL).read().strip().decode("utf-8")
254
+ except Exception:
255
+ QMessageBox.critical(self, cfg.programName, "An error occured during retrieving converters from BORIS remote repository")
256
+ return
257
+
258
+ try:
259
+ converters_from_file = eval(converters_from_repo)["BORIS converters"]
260
+ except Exception:
261
+ QMessageBox.critical(self, cfg.programName, "An error occured during retrieving converters from BORIS remote repository")
262
+ return
263
+
264
+ if converters_from_file:
265
+ diag_choose_conv = dialog.ChooseObservationsToImport("Choose the converters to load:", sorted(list(converters_from_file.keys())))
266
+
267
+ if diag_choose_conv.exec_():
268
+ selected_converters = diag_choose_conv.get_selected_observations()
269
+ if selected_converters:
270
+ # extract converter names from table
271
+ converter_names = []
272
+ for row in range(self.tw_converters.rowCount()):
273
+ converter_names.append(self.tw_converters.item(row, 0).text())
274
+
275
+ for converter in selected_converters:
276
+ converter_name = converter
277
+
278
+ if converter in converter_names:
279
+ while True:
280
+ text, ok = QInputDialog.getText(
281
+ self,
282
+ "Converter conflict",
283
+ "The converter already exists<br>Rename it:",
284
+ QLineEdit.Normal,
285
+ converter,
286
+ )
287
+ if not ok:
288
+ break
289
+ if text in converter_names:
290
+ QMessageBox.critical(self, cfg.programName, "This name already exists in converters")
291
+
292
+ if not text.replace("_", "a").isalnum():
293
+ QMessageBox.critical(
294
+ self,
295
+ cfg.programName,
296
+ "This name contains forbidden character(s).<br>Use a..z, A..Z, 0..9 _",
297
+ )
298
+
299
+ if text != converter and text not in converter_names and text.replace("_", "a").isalnum():
300
+ break
301
+
302
+ if ok:
303
+ converter_name = text
304
+ else:
305
+ continue
306
+ # test if code does not produce error
307
+ function = code_2_func(self, name=converter_name, code=converters_from_file[converter]["code"])
308
+
309
+ try:
310
+ exec(function)
311
+ except Exception:
312
+ QMessageBox.critical(
313
+ self,
314
+ "BORIS",
315
+ (f"The code of {converter_name} converter produces an error: <br><b>{sys.exc_info()[1]}</b>"),
316
+ )
317
+
318
+ self.tw_converters.setRowCount(self.tw_converters.rowCount() + 1)
319
+ self.tw_converters.setItem(self.tw_converters.rowCount() - 1, 0, QTableWidgetItem(converter_name))
320
+ self.tw_converters.setItem(
321
+ self.tw_converters.rowCount() - 1,
322
+ 1,
323
+ QTableWidgetItem(converters_from_file[converter]["description"]),
324
+ )
325
+ self.tw_converters.setItem(
326
+ self.tw_converters.rowCount() - 1,
327
+ 2,
328
+ QTableWidgetItem(converters_from_file[converter]["code"].replace("\n", "@")),
329
+ )
330
+
331
+ self.flag_modified = True
332
+
333
+ [self.tw_converters.resizeColumnToContents(idx) for idx in [0, 1]]
boris/converters_ui.py ADDED
@@ -0,0 +1,225 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ################################################################################
4
+ ## Form generated from reading UI file 'converters.ui'
5
+ ##
6
+ ## Created by: Qt User Interface Compiler version 6.8.0
7
+ ##
8
+ ## WARNING! All changes made in this file will be lost when recompiling UI file!
9
+ ################################################################################
10
+
11
+ from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
12
+ QMetaObject, QObject, QPoint, QRect,
13
+ QSize, QTime, QUrl, Qt)
14
+ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
15
+ QFont, QFontDatabase, QGradient, QIcon,
16
+ QImage, QKeySequence, QLinearGradient, QPainter,
17
+ QPalette, QPixmap, QRadialGradient, QTransform)
18
+ from PySide6.QtWidgets import (QAbstractItemView, QApplication, QHBoxLayout, QHeaderView,
19
+ QLabel, QLineEdit, QPlainTextEdit, QPushButton,
20
+ QSizePolicy, QSpacerItem, QTableWidget, QTableWidgetItem,
21
+ QVBoxLayout, QWidget)
22
+
23
+ class Ui_converters(object):
24
+ def setupUi(self, converters):
25
+ if not converters.objectName():
26
+ converters.setObjectName(u"converters")
27
+ converters.resize(1029, 530)
28
+ self.verticalLayout = QVBoxLayout(converters)
29
+ self.verticalLayout.setObjectName(u"verticalLayout")
30
+ self.label_4 = QLabel(converters)
31
+ self.label_4.setObjectName(u"label_4")
32
+
33
+ self.verticalLayout.addWidget(self.label_4)
34
+
35
+ self.tw_converters = QTableWidget(converters)
36
+ if (self.tw_converters.columnCount() < 3):
37
+ self.tw_converters.setColumnCount(3)
38
+ __qtablewidgetitem = QTableWidgetItem()
39
+ self.tw_converters.setHorizontalHeaderItem(0, __qtablewidgetitem)
40
+ __qtablewidgetitem1 = QTableWidgetItem()
41
+ self.tw_converters.setHorizontalHeaderItem(1, __qtablewidgetitem1)
42
+ __qtablewidgetitem2 = QTableWidgetItem()
43
+ self.tw_converters.setHorizontalHeaderItem(2, __qtablewidgetitem2)
44
+ self.tw_converters.setObjectName(u"tw_converters")
45
+ self.tw_converters.setEditTriggers(QAbstractItemView.NoEditTriggers)
46
+ self.tw_converters.setSelectionMode(QAbstractItemView.SingleSelection)
47
+ self.tw_converters.setSelectionBehavior(QAbstractItemView.SelectRows)
48
+ self.tw_converters.setSortingEnabled(True)
49
+
50
+ self.verticalLayout.addWidget(self.tw_converters)
51
+
52
+ self.horizontalLayout = QHBoxLayout()
53
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
54
+ self.pb_add_converter = QPushButton(converters)
55
+ self.pb_add_converter.setObjectName(u"pb_add_converter")
56
+
57
+ self.horizontalLayout.addWidget(self.pb_add_converter)
58
+
59
+ self.pb_modify_converter = QPushButton(converters)
60
+ self.pb_modify_converter.setObjectName(u"pb_modify_converter")
61
+
62
+ self.horizontalLayout.addWidget(self.pb_modify_converter)
63
+
64
+ self.pb_delete_converter = QPushButton(converters)
65
+ self.pb_delete_converter.setObjectName(u"pb_delete_converter")
66
+
67
+ self.horizontalLayout.addWidget(self.pb_delete_converter)
68
+
69
+ self.pb_load_from_file = QPushButton(converters)
70
+ self.pb_load_from_file.setObjectName(u"pb_load_from_file")
71
+
72
+ self.horizontalLayout.addWidget(self.pb_load_from_file)
73
+
74
+ self.pb_load_from_repo = QPushButton(converters)
75
+ self.pb_load_from_repo.setObjectName(u"pb_load_from_repo")
76
+
77
+ self.horizontalLayout.addWidget(self.pb_load_from_repo)
78
+
79
+ self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
80
+
81
+ self.horizontalLayout.addItem(self.horizontalSpacer)
82
+
83
+
84
+ self.verticalLayout.addLayout(self.horizontalLayout)
85
+
86
+ self.horizontalLayout_3 = QHBoxLayout()
87
+ self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
88
+ self.label_2 = QLabel(converters)
89
+ self.label_2.setObjectName(u"label_2")
90
+ self.label_2.setMinimumSize(QSize(120, 0))
91
+
92
+ self.horizontalLayout_3.addWidget(self.label_2)
93
+
94
+ self.le_converter_name = QLineEdit(converters)
95
+ self.le_converter_name.setObjectName(u"le_converter_name")
96
+
97
+ self.horizontalLayout_3.addWidget(self.le_converter_name)
98
+
99
+ self.horizontalSpacer_3 = QSpacerItem(10, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
100
+
101
+ self.horizontalLayout_3.addItem(self.horizontalSpacer_3)
102
+
103
+
104
+ self.verticalLayout.addLayout(self.horizontalLayout_3)
105
+
106
+ self.horizontalLayout_5 = QHBoxLayout()
107
+ self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
108
+ self.label_3 = QLabel(converters)
109
+ self.label_3.setObjectName(u"label_3")
110
+ self.label_3.setMinimumSize(QSize(120, 0))
111
+
112
+ self.horizontalLayout_5.addWidget(self.label_3)
113
+
114
+ self.le_converter_description = QLineEdit(converters)
115
+ self.le_converter_description.setObjectName(u"le_converter_description")
116
+
117
+ self.horizontalLayout_5.addWidget(self.le_converter_description)
118
+
119
+ self.horizontalSpacer_4 = QSpacerItem(10, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
120
+
121
+ self.horizontalLayout_5.addItem(self.horizontalSpacer_4)
122
+
123
+
124
+ self.verticalLayout.addLayout(self.horizontalLayout_5)
125
+
126
+ self.horizontalLayout_2 = QHBoxLayout()
127
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
128
+ self.verticalLayout_3 = QVBoxLayout()
129
+ self.verticalLayout_3.setObjectName(u"verticalLayout_3")
130
+ self.label = QLabel(converters)
131
+ self.label.setObjectName(u"label")
132
+
133
+ self.verticalLayout_3.addWidget(self.label)
134
+
135
+ self.pb_code_help = QPushButton(converters)
136
+ self.pb_code_help.setObjectName(u"pb_code_help")
137
+
138
+ self.verticalLayout_3.addWidget(self.pb_code_help)
139
+
140
+ self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
141
+
142
+ self.verticalLayout_3.addItem(self.verticalSpacer_2)
143
+
144
+
145
+ self.horizontalLayout_2.addLayout(self.verticalLayout_3)
146
+
147
+ self.pteCode = QPlainTextEdit(converters)
148
+ self.pteCode.setObjectName(u"pteCode")
149
+ font = QFont()
150
+ font.setFamilies([u"Monospace"])
151
+ self.pteCode.setFont(font)
152
+
153
+ self.horizontalLayout_2.addWidget(self.pteCode)
154
+
155
+ self.verticalLayout_2 = QVBoxLayout()
156
+ self.verticalLayout_2.setObjectName(u"verticalLayout_2")
157
+ self.pb_save_converter = QPushButton(converters)
158
+ self.pb_save_converter.setObjectName(u"pb_save_converter")
159
+
160
+ self.verticalLayout_2.addWidget(self.pb_save_converter)
161
+
162
+ self.pb_cancel_converter = QPushButton(converters)
163
+ self.pb_cancel_converter.setObjectName(u"pb_cancel_converter")
164
+
165
+ self.verticalLayout_2.addWidget(self.pb_cancel_converter)
166
+
167
+ self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
168
+
169
+ self.verticalLayout_2.addItem(self.verticalSpacer)
170
+
171
+
172
+ self.horizontalLayout_2.addLayout(self.verticalLayout_2)
173
+
174
+
175
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
176
+
177
+ self.horizontalLayout_4 = QHBoxLayout()
178
+ self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
179
+ self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
180
+
181
+ self.horizontalLayout_4.addItem(self.horizontalSpacer_2)
182
+
183
+ self.pb_cancel_widget = QPushButton(converters)
184
+ self.pb_cancel_widget.setObjectName(u"pb_cancel_widget")
185
+
186
+ self.horizontalLayout_4.addWidget(self.pb_cancel_widget)
187
+
188
+ self.pbOK = QPushButton(converters)
189
+ self.pbOK.setObjectName(u"pbOK")
190
+
191
+ self.horizontalLayout_4.addWidget(self.pbOK)
192
+
193
+
194
+ self.verticalLayout.addLayout(self.horizontalLayout_4)
195
+
196
+
197
+ self.retranslateUi(converters)
198
+
199
+ QMetaObject.connectSlotsByName(converters)
200
+ # setupUi
201
+
202
+ def retranslateUi(self, converters):
203
+ converters.setWindowTitle(QCoreApplication.translate("converters", u"Time converters", None))
204
+ self.label_4.setText(QCoreApplication.translate("converters", u"Converters", None))
205
+ ___qtablewidgetitem = self.tw_converters.horizontalHeaderItem(0)
206
+ ___qtablewidgetitem.setText(QCoreApplication.translate("converters", u"Name", None));
207
+ ___qtablewidgetitem1 = self.tw_converters.horizontalHeaderItem(1)
208
+ ___qtablewidgetitem1.setText(QCoreApplication.translate("converters", u"Description", None));
209
+ ___qtablewidgetitem2 = self.tw_converters.horizontalHeaderItem(2)
210
+ ___qtablewidgetitem2.setText(QCoreApplication.translate("converters", u"Code", None));
211
+ self.pb_add_converter.setText(QCoreApplication.translate("converters", u"Add new converter", None))
212
+ self.pb_modify_converter.setText(QCoreApplication.translate("converters", u"Modify converter", None))
213
+ self.pb_delete_converter.setText(QCoreApplication.translate("converters", u"Delete converter", None))
214
+ self.pb_load_from_file.setText(QCoreApplication.translate("converters", u"Load converters from file", None))
215
+ self.pb_load_from_repo.setText(QCoreApplication.translate("converters", u"Load converters from BORIS repository", None))
216
+ self.label_2.setText(QCoreApplication.translate("converters", u"Name", None))
217
+ self.label_3.setText(QCoreApplication.translate("converters", u"Description", None))
218
+ self.label.setText(QCoreApplication.translate("converters", u"Python code", None))
219
+ self.pb_code_help.setText(QCoreApplication.translate("converters", u"Help", None))
220
+ self.pb_save_converter.setText(QCoreApplication.translate("converters", u"Save converter", None))
221
+ self.pb_cancel_converter.setText(QCoreApplication.translate("converters", u"Cancel", None))
222
+ self.pb_cancel_widget.setText(QCoreApplication.translate("converters", u"Cancel", None))
223
+ self.pbOK.setText(QCoreApplication.translate("converters", u"OK", None))
224
+ # retranslateUi
225
+