Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
beat
beat.editor
Commits
05cf694a
Commit
05cf694a
authored
Nov 21, 2019
by
Flavio TARSETTI
Browse files
[widgets][toolchaineditor] block add/edition
parent
4ae1dcca
Changes
1
Hide whitespace changes
Inline
Side-by-side
beat/editor/widgets/toolchaineditor.py
View file @
05cf694a
...
...
@@ -26,12 +26,14 @@
import
simplejson
as
json
from
enum
import
Enum
from
functools
import
partial
from
PyQt5.QtCore
import
Qt
from
PyQt5.QtCore
import
QRect
from
PyQt5.QtCore
import
QRectF
from
PyQt5.QtCore
import
QPointF
from
PyQt5.QtCore
import
pyqtSignal
from
PyQt5.QtCore
import
pyqtSlot
from
PyQt5.QtGui
import
QColor
from
PyQt5.QtGui
import
QBrush
...
...
@@ -43,20 +45,30 @@ from PyQt5.QtGui import QTransform
from
PyQt5.QtGui
import
QIcon
from
PyQt5.QtWidgets
import
QHBoxLayout
from
PyQt5.QtWidgets
import
QFormLayout
from
PyQt5.QtWidgets
import
QWidget
from
PyQt5.QtWidgets
import
QGraphicsView
from
PyQt5.QtWidgets
import
QGraphicsItem
from
PyQt5.QtWidgets
import
QGraphicsPathItem
from
PyQt5.QtWidgets
import
QGraphicsObject
from
PyQt5.QtWidgets
import
QToolBar
from
PyQt5.QtWidgets
import
QAction
from
PyQt5.QtWidgets
import
QLabel
from
PyQt5.QtWidgets
import
QDialog
from
PyQt5.QtWidgets
import
QDialogButtonBox
from
PyQt5.QtWidgets
import
QComboBox
from
PyQt5.QtWidgets
import
QPushButton
from
PyQt5.QtWidgets
import
QMenu
from
PyQt5.QtWidgets
import
QLineEdit
from
..backend.asset
import
Asset
from
..backend.asset
import
AssetType
from
..backend.assetmodel
import
AssetModel
from
..decorators
import
frozen
from
.editor
import
AbstractAssetEditor
from
.drawing_space
import
DrawingSpace
from
..backend.resourcemodels
import
AlgorithmResourceModel
from
..backend.resourcemodels
import
DatasetResourceModel
class
BasePin
(
QGraphicsObject
):
...
...
@@ -351,6 +363,84 @@ class Connection(QGraphicsPathItem):
self
.
end_block
.
blockMoved
.
connect
(
self
.
set_moved_block_pins_coordinates
)
class
BlockEditionDialog
(
QDialog
):
"""Dialog to edit a block"""
def
__init__
(
self
,
block
,
parent
=
None
):
"""Constructor
:param block: current block
:param parent QWidget: parent widget
"""
super
().
__init__
(
parent
)
toolchain
=
block
.
toolchain
self
.
setWindowTitle
(
self
.
tr
(
"Block Edition"
))
self
.
name_lineedit
=
QLineEdit
(
block
.
name
)
self
.
channel_combobox
=
QComboBox
()
no_channel_label
=
QLabel
(
self
.
tr
(
"No input connections yet!"
))
no_dataset_channel_label
=
QLabel
(
self
.
tr
(
"No synchronization for datasets"
))
layout
=
QFormLayout
(
self
)
layout
.
addRow
(
self
.
tr
(
"Name:"
),
self
.
name_lineedit
)
channels
=
[]
if
block
.
type
==
BlockType
.
DATASETS
.
name
:
layout
.
addRow
(
self
.
tr
(
"Channel:"
),
no_dataset_channel_label
)
elif
block
.
synchronized_channel
is
None
:
layout
.
addRow
(
self
.
tr
(
"Channel:"
),
no_channel_label
)
else
:
for
connection
in
toolchain
.
connections
:
if
connection
.
end_block
==
block
and
connection
.
channel
not
in
channels
:
channels
.
append
(
connection
.
channel
)
self
.
channel_combobox
.
addItems
(
channels
)
# set current channel if exists
index
=
self
.
channel_combobox
.
findText
(
str
(
block
.
synchronized_channel
),
Qt
.
MatchFixedString
)
if
index
>=
0
:
self
.
channel_combobox
.
setCurrentIndex
(
index
)
layout
.
addRow
(
self
.
tr
(
"Channel:"
),
self
.
channel_combobox
)
# OK and Cancel buttons
self
.
buttons
=
QDialogButtonBox
(
QDialogButtonBox
.
Ok
|
QDialogButtonBox
.
Cancel
,
Qt
.
Horizontal
,
self
)
# layout.addWidget(self.buttons)
layout
.
addRow
(
self
.
buttons
)
# Signals/Slots connection
self
.
buttons
.
accepted
.
connect
(
self
.
accept
)
self
.
buttons
.
rejected
.
connect
(
self
.
reject
)
def
value
(
self
):
"""Returns the value selected"""
return
{
"name"
:
self
.
name_lineedit
.
text
(),
"channel"
:
self
.
channel_combobox
.
currentText
(),
}
@
staticmethod
def
getNewBlockSettings
(
block
,
parent
=
None
):
"""Static method to create the dialog and return qdialog accepted/spinbox value
:param block: current block
:param parent QWidget: parent widget
"""
dialog
=
BlockEditionDialog
(
block
,
parent
)
result
=
dialog
.
exec_
()
value
=
None
if
result
==
QDialog
.
Accepted
:
value
=
dialog
.
value
()
return
(
value
,
result
)
class
BlockType
(
Enum
):
"""All possible block types"""
...
...
@@ -403,7 +493,9 @@ class Block(QGraphicsObject):
def
load
(
self
,
toolchain
,
block_details
):
self
.
toolchain
=
toolchain
self
.
name
=
block_details
[
"name"
]
if
"name"
in
block_details
:
self
.
name
=
block_details
[
"name"
]
if
self
.
type
!=
BlockType
.
DATASETS
.
name
:
self
.
inputs
=
block_details
[
"inputs"
]
...
...
@@ -587,6 +679,108 @@ class Block(QGraphicsObject):
self
.
blockMoved
.
emit
()
self
.
dataChanged
.
emit
()
def
mouseDoubleClickEvent
(
self
,
event
):
"""Update block information"""
value
=
None
ok
=
False
block_updated
=
False
value
,
ok
=
BlockEditionDialog
.
getNewBlockSettings
(
self
)
old_name
=
self
.
name
old_channel
=
self
.
synchronized_channel
if
ok
:
if
self
.
name
!=
value
[
"name"
]:
self
.
name
=
value
[
"name"
]
block_updated
=
True
if
(
self
.
synchronized_channel
!=
value
[
"channel"
]
and
self
.
type
!=
BlockType
.
DATASETS
.
name
):
self
.
synchronized_channel
=
value
[
"channel"
]
block_updated
=
True
if
block_updated
:
# Update blocks
block_item
=
{}
block_item
[
"inputs"
]
=
self
.
inputs
block_item
[
"outputs"
]
=
self
.
outputs
block_item
[
"name"
]
=
self
.
name
block_item
[
"synchronized_channel"
]
=
self
.
synchronized_channel
self
.
toolchain
.
blocks
.
remove
(
self
)
self
.
scene
().
removeItem
(
self
)
self
.
load
(
self
.
toolchain
,
block_item
)
self
.
dataChanged
.
connect
(
self
.
toolchain
.
dataChanged
)
self
.
toolchain
.
blocks
.
append
(
self
)
self
.
toolchain
.
scene
.
addItem
(
self
)
# if type is dataset: update sync channels everywhere
if
self
.
type
==
BlockType
.
DATASETS
.
name
:
for
block
in
self
.
toolchain
.
blocks
:
if
(
block
.
type
!=
BlockType
.
DATASETS
.
name
and
block
.
synchronized_channel
==
old_name
):
block
.
synchronized_channel
=
self
.
name
# Update connections
for
connection
in
self
.
toolchain
.
connections
:
# Name update
if
connection
.
start_block_name
==
old_name
:
connection
.
start_block_name
=
self
.
name
if
connection
.
end_block_name
==
old_name
:
connection
.
end_block_name
=
self
.
name
if
connection
.
channel
==
old_name
:
connection
.
channel
=
self
.
name
# Complete toolchain channel update from current block
if
old_channel
!=
self
.
synchronized_channel
:
self
.
toolchain
.
update_channel_path
(
self
,
old_channel
,
self
.
synchronized_channel
)
# Update representation
web_representation
=
self
.
toolchain
.
web_representation
for
key
,
value
in
web_representation
.
items
():
for
sub_key
,
sub_value
in
web_representation
[
key
].
items
():
if
sub_key
==
old_name
:
# blocks and channel_colors
new_sub_key
=
sub_key
.
replace
(
old_name
,
self
.
name
)
web_representation
[
key
][
new_sub_key
]
=
web_representation
[
key
].
pop
(
sub_key
)
elif
"/"
in
sub_key
:
# connections
new_sub_key
=
sub_key
left_part
=
sub_key
.
split
(
"/"
)[
0
]
right_part
=
sub_key
.
split
(
"/"
)[
1
]
if
left_part
.
split
(
"."
)[
0
]
==
old_name
:
new_sub_key
=
(
self
.
name
+
"."
+
left_part
.
split
(
"."
)[
1
]
+
"/"
+
right_part
)
if
right_part
.
split
(
"."
)[
0
]
==
old_name
:
new_sub_key
=
(
left_part
+
"/"
+
self
.
name
+
"."
+
right_part
.
split
(
"."
)[
1
]
)
if
new_sub_key
!=
sub_key
:
web_representation
[
key
][
new_sub_key
]
=
web_representation
[
key
].
pop
(
sub_key
)
self
.
dataChanged
.
emit
()
def
paint
(
self
,
painter
,
option
,
widget
):
"""Paint the block"""
...
...
@@ -715,22 +909,24 @@ class Toolchain(QWidget):
self
.
toolbar
=
QToolBar
()
dataset_action
=
QAction
(
QIcon
(
"beat/editor/widgets/dataset_icon.png"
),
"&Dataset"
,
self
)
dataset_action
.
triggered
.
connect
(
lambda
:
self
.
add_block
(
BlockType
.
DATASETS
))
block_action
=
QAction
(
QIcon
(
"beat/editor/widgets/block_icon.png"
),
"&Block"
,
self
)
block_action
.
triggered
.
connect
(
lambda
:
self
.
add_block
(
BlockType
.
BLOCKS
))
analyzer_action
=
QAction
(
QIcon
(
"beat/editor/widgets/analyzer_icon.png"
),
"&Analyzer"
,
self
)
analyzer_action
.
triggered
.
connect
(
lambda
:
self
.
add_block
(
BlockType
.
ANALYZERS
))
self
.
dataset_button
=
QPushButton
()
self
.
dataset_button
.
setToolTip
(
"Dataset"
)
self
.
dataset_button
.
setIcon
(
QIcon
(
"beat/editor/widgets/dataset_icon.png"
))
self
.
dataset_edit_menu
=
QMenu
(
self
)
self
.
block_button
=
QPushButton
()
self
.
block_button
.
setToolTip
(
"Block"
)
self
.
block_button
.
setIcon
(
QIcon
(
"beat/editor/widgets/block_icon.png"
))
self
.
block_edit_menu
=
QMenu
(
self
)
self
.
toolbar
.
addAction
(
dataset_action
)
self
.
toolbar
.
addAction
(
block_action
)
self
.
toolbar
.
addAction
(
analyzer_action
)
self
.
analyzer_button
=
QPushButton
()
self
.
analyzer_button
.
setToolTip
(
"Analyzer"
)
self
.
analyzer_button
.
setIcon
(
QIcon
(
"beat/editor/widgets/analyzer_icon.png"
))
self
.
analyzer_edit_menu
=
QMenu
(
self
)
self
.
toolbar
.
addWidget
(
self
.
dataset_button
)
self
.
toolbar
.
addWidget
(
self
.
block_button
)
self
.
toolbar
.
addWidget
(
self
.
analyzer_button
)
self
.
toolbar
.
setOrientation
(
Qt
.
Vertical
)
...
...
@@ -738,11 +934,111 @@ class Toolchain(QWidget):
layout
.
addWidget
(
self
.
toolbar
)
layout
.
addWidget
(
self
.
view
)
def
add_block
(
self
,
block_type
):
self
.
new_block
=
Block
(
block_type
.
name
,
self
.
block_config
,
self
.
connection_config
)
self
.
scene
.
addItem
(
self
.
new_block
)
def
update_channel_path
(
self
,
block
,
old_channel
,
new_channel
):
# check if current block is synchronized on old_channel
for
connection
in
self
.
connections
:
if
(
block
.
name
==
connection
.
start_block_name
and
connection
.
channel
==
old_channel
):
# update connection channel
connection
.
channel
=
new_channel
self
.
scene
.
removeItem
(
connection
)
# Find the corresponding channel
connection_settings
=
{}
connection_settings
[
"channel"
]
=
connection
.
channel
# Create the connection
connection_settings
[
"from"
]
=
(
connection
.
start_block_name
+
"."
+
connection
.
start_pin_name
)
connection_settings
[
"to"
]
=
(
connection
.
end_block_name
+
"."
+
connection
.
end_pin_name
)
channel_colors
=
self
.
json_object
.
get
(
"representation"
,
{}).
get
(
"channel_colors"
)
connection
.
load
(
self
,
connection_settings
,
channel_colors
)
self
.
scene
.
addItem
(
connection
)
def
button_item_selected
(
self
,
block_type
,
name
):
inputs
=
[]
outputs
=
[]
if
block_type
==
BlockType
.
DATASETS
.
name
:
inputs
=
None
name_split
=
name
.
split
(
"/"
)
database_name
=
name_split
[
0
]
+
"/"
+
name_split
[
1
]
asset
=
Asset
(
self
.
prefix_path
,
AssetType
.
DATABASE
,
database_name
)
for
protocol
in
asset
.
declaration
[
"protocols"
]:
if
protocol
[
"name"
]
==
name_split
[
2
]:
for
_set
in
protocol
[
"sets"
]:
if
_set
[
"name"
]
==
name_split
[
3
]:
for
key
in
_set
[
"outputs"
].
keys
():
outputs
.
append
(
key
)
else
:
if
block_type
==
BlockType
.
ANALYZERS
.
name
:
outputs
=
None
asset
=
Asset
(
self
.
prefix_path
,
AssetType
.
ALGORITHM
,
name
)
declaration
=
asset
.
declaration
for
group
in
declaration
[
"groups"
]:
if
"inputs"
in
group
:
for
key
in
group
[
"inputs"
].
keys
():
inputs
.
append
(
key
)
if
"outputs"
in
group
:
for
key
in
group
[
"outputs"
].
keys
():
outputs
.
append
(
key
)
block_item
=
{}
block_item
[
"inputs"
]
=
inputs
block_item
[
"outputs"
]
=
outputs
init_name_count
=
0
init_name
=
self
.
tr
(
"CHANGE_ME"
)
for
block
in
self
.
blocks
:
if
block
.
name
.
find
(
init_name
)
>
-
1
:
init_name_count
+=
1
block_item
[
"name"
]
=
init_name
+
"_"
+
str
(
init_name_count
)
block
=
Block
(
block_type
,
self
.
block_config
,
self
.
connection_config
)
block
.
load
(
self
,
block_item
)
block
.
dataChanged
.
connect
(
self
.
dataChanged
)
self
.
blocks
.
append
(
block
)
self
.
scene
.
addItem
(
block
)
def
set_prefix_databases_algorithms_lists
(
self
,
prefix_path
,
dataset_list
,
algorithm_list
,
analyzer_list
):
self
.
prefix_path
=
prefix_path
for
dataset_name
in
dataset_list
:
self
.
dataset_edit_menu
.
addAction
(
self
.
tr
(
dataset_name
),
partial
(
self
.
button_item_selected
,
BlockType
.
DATASETS
.
name
,
dataset_name
),
)
for
algorithm_name
in
algorithm_list
:
self
.
block_edit_menu
.
addAction
(
self
.
tr
(
algorithm_name
),
partial
(
self
.
button_item_selected
,
BlockType
.
BLOCKS
.
name
,
algorithm_name
),
)
for
analyzer_name
in
analyzer_list
:
self
.
analyzer_edit_menu
.
addAction
(
self
.
tr
(
analyzer_name
),
partial
(
self
.
button_item_selected
,
BlockType
.
ANALYZERS
.
name
,
analyzer_name
),
)
self
.
dataset_button
.
setMenu
(
self
.
dataset_edit_menu
)
self
.
block_button
.
setMenu
(
self
.
block_edit_menu
)
self
.
analyzer_button
.
setMenu
(
self
.
analyzer_edit_menu
)
def
clear_space
(
self
):
self
.
scene
.
clear
()
...
...
@@ -853,7 +1149,35 @@ class ToolchainEditor(AbstractAssetEditor):
self
.
layout
().
addWidget
(
self
.
toolchain
,
2
)
self
.
layout
().
addStretch
()
self
.
algorithm_list
=
[]
self
.
analyzer_list
=
[]
self
.
dataset_list
=
[]
self
.
toolchain
.
dataChanged
.
connect
(
self
.
dataChanged
)
self
.
contextChanged
.
connect
(
self
.
__onContextChanged
)
@
pyqtSlot
()
def
__onContextChanged
(
self
):
algorithm_model
=
AlgorithmResourceModel
()
algorithm_model
.
setAnalyzerEnabled
(
False
)
dataset_model
=
DatasetResourceModel
()
self
.
dataset_list
=
[
dataset_model
.
index
(
i
,
0
).
data
()
for
i
in
range
(
dataset_model
.
rowCount
())
]
self
.
algorithm_list
=
[
algorithm_model
.
index
(
i
,
0
).
data
()
for
i
in
range
(
algorithm_model
.
rowCount
())
]
algorithm_model
.
setAnalyzerEnabled
(
True
)
self
.
analyzer_list
=
[
algorithm_model
.
index
(
i
,
0
).
data
()
for
i
in
range
(
algorithm_model
.
rowCount
())
]
self
.
toolchain
.
set_prefix_databases_algorithms_lists
(
self
.
prefix_path
,
self
.
dataset_list
,
self
.
algorithm_list
,
self
.
analyzer_list
)
def
_load_json
(
self
,
json_object
):
"""Load the json object passed as parameter"""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment