first objects added

This commit is contained in:
Dominik Demuth 2023-02-04 16:36:55 +01:00
parent 8e2fdcef4c
commit 793786fda8
4 changed files with 337 additions and 138 deletions

View File

@ -0,0 +1,249 @@
from __future__ import annotations
from PyQt5 import QtCore, QtGui, QtWidgets
from gui_qt.lib.delegates import ColorListEditor
class ObjectWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.layout = QtWidgets.QGridLayout()
self.layout.setContentsMargins(3, 3, 3, 3)
self.color_label = QtWidgets.QLabel('Color')
self.layout.addWidget(self.color_label, 0, 0, 1, 1)
self.color_box = ColorListEditor(self)
self.layout.addWidget(self.color_box, 0, 1, 1, 2)
self.setLayout(self.layout)
def collect_args(self) -> dict:
return {'color': self.color_box.currentData(QtCore.Qt.UserRole)}
@staticmethod
def parse_point(x_widget: QtWidgets.QLineEdit, y_widget: QtWidgets.QLineEdit) -> None | tuple[float, float]:
x = x_widget.text()
if not x:
return
y = y_widget.text()
if not y:
return
try:
return float(x), float(y)
except ValueError:
return
class LineWidget(ObjectWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.pos_label = QtWidgets.QLabel('Position')
self.layout.addWidget(self.pos_label, 1, 0, 1, 1)
self.pos_lineedit = QtWidgets.QLineEdit()
self.pos_lineedit.setValidator(QtGui.QDoubleValidator())
self.layout.addWidget(self.pos_lineedit, 1, 1, 1, 2)
self.orientation_label = QtWidgets.QLabel('Orientation')
self.layout.addWidget(self.orientation_label, 2, 0, 1, 1)
self.orient_combobox = QtWidgets.QComboBox()
self.orient_combobox.addItems(['Horizontal', 'Vertical'])
self.layout.addWidget(self.orient_combobox, 2, 1, 1, 2)
self.layout.setRowStretch(3, 1)
def collect_args(self):
dic = super().collect_args()
pos = self.pos_lineedit.text()
if not pos:
return
try:
dic['pos'] = float(pos)
except ValueError:
return
dic['angle'] = self.orient_combobox.currentIndex() * 90
return dic
class MultiPointWidget(ObjectWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.loop_checkbox = QtWidgets.QCheckBox('Close loop')
self.layout.addWidget(self.loop_checkbox, 1, 0, 1, 3)
self.table_widget = QtWidgets.QTableWidget()
self.table_widget.setColumnCount(2)
self.table_widget.setHorizontalHeaderLabels(['x', 'y'])
header = self.table_widget.horizontalHeader()
header.setStretchLastSection(True)
self.layout.addWidget(self.table_widget, 2, 0, 1, 3)
self.addButton = QtWidgets.QPushButton('Add point')
self.layout.addWidget(self.addButton, 3, 1, 1, 1)
self.layout.setRowStretch(3, 1)
self.addButton.clicked.connect(self.new_point)
self.removeButton = QtWidgets.QPushButton('Remove point')
self.layout.addWidget(self.removeButton, 3, 2, 1, 1)
self.layout.setRowStretch(3, 1)
self.removeButton.clicked.connect(self.less_point)
def new_point(self, _):
row = self.table_widget.rowCount()
self.table_widget.setRowCount(row+1)
placeholder = ['x', 'y']
for column in range(2):
line_edit = QtWidgets.QLineEdit()
line_edit.setFrame(False)
line_edit.setPlaceholderText(placeholder[column])
line_edit.setValidator(QtGui.QDoubleValidator())
self.table_widget.setCellWidget(row, column, line_edit)
def less_point(self, _):
self.table_widget.removeRow(self.table_widget.rowCount()-1)
def collect_args(self):
dic = super().collect_args()
pts = []
if self.table_widget.rowCount() <= 1:
return
for row in range(self.table_widget.rowCount()):
next_pt = self.parse_point(self.table_widget.cellWidget(row, 0), self.table_widget.cellWidget(row, 1))
if next_pt is None:
return
pts.append(next_pt)
if pts:
dic['pts'] = pts
dic['closed'] = self.loop_checkbox.isChecked()
return dic
else:
return
class RectangleWidget(ObjectWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.leftbottom_label = QtWidgets.QLabel('Lower left')
self.layout.addWidget(self.leftbottom_label, 1, 0, 1, 1)
self.left_x = QtWidgets.QLineEdit()
self.left_x.setPlaceholderText('x')
self.layout.addWidget(self.left_x, 1, 1, 1, 1)
self.left_y = QtWidgets.QLineEdit()
self.left_y.setPlaceholderText('y')
self.left_x.setValidator(QtGui.QDoubleValidator())
self.left_y.setValidator(QtGui.QDoubleValidator())
self.layout.addWidget(self.left_y, 1, 2, 1, 1)
self.righttop_label = QtWidgets.QLabel('Upper right')
self.layout.addWidget(self.righttop_label, 2, 0, 1, 1)
self.right_x = QtWidgets.QLineEdit()
self.right_x.setPlaceholderText('x')
self.layout.addWidget(self.right_x, 2, 1, 1, 1)
self.right_y = QtWidgets.QLineEdit()
self.right_y.setPlaceholderText('y')
self.right_x.setValidator(QtGui.QDoubleValidator())
self.right_y.setValidator(QtGui.QDoubleValidator())
self.layout.addWidget(self.right_y, 2, 2, 1, 1)
self.layout.setRowStretch(3, 1)
def collect_args(self):
dic = super().collect_args()
left = self.parse_point(self.left_x, self.left_y)
if left is None:
return
dic['left'] = left
right = self.parse_point(self.right_x, self.right_y)
if right is None:
return
dic['right'] = right
return dic
class EllipseWidget(ObjectWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.centre_label = QtWidgets.QLabel('Centre')
self.layout.addWidget(self.centre_label, 1, 0, 1, 1)
self.centre_x = QtWidgets.QLineEdit()
self.centre_x.setPlaceholderText('x')
self.layout.addWidget(self.centre_x, 1, 1, 1, 1)
self.centre_y = QtWidgets.QLineEdit()
self.centre_y.setPlaceholderText('y')
self.centre_x.setValidator(QtGui.QDoubleValidator())
self.centre_y.setValidator(QtGui.QDoubleValidator())
self.layout.addWidget(self.centre_y, 1, 2, 1, 1)
self.axes_label = QtWidgets.QLabel('Axes')
self.layout.addWidget(self.axes_label, 2, 0, 1, 1)
self.width = QtWidgets.QLineEdit()
self.width.setPlaceholderText('width')
self.layout.addWidget(self.width, 2, 1, 1, 1)
self.height = QtWidgets.QLineEdit()
self.height.setPlaceholderText('Height')
self.width.setValidator(QtGui.QDoubleValidator())
self.width.setValidator(QtGui.QDoubleValidator())
self.layout.addWidget(self.height, 2, 2, 1, 1)
self.layout.setRowStretch(3, 1)
def collect_args(self):
dic = super().collect_args()
centre = self.parse_point(self.centre_x, self.centre_y)
if centre is None:
return
dic['centre'] = centre
axes = self.parse_point(self.width, self.height)
if axes is None:
return
dic['axes'] = axes
return dic
class TextWidget(ObjectWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.centre_label = QtWidgets.QLabel('Centre')
self.layout.addWidget(self.centre_label, 1, 0, 1, 1)
self.centre_x = QtWidgets.QLineEdit()
self.centre_x.setPlaceholderText('x')
self.layout.addWidget(self.centre_x, 1, 1, 1, 1)
self.centre_y = QtWidgets.QLineEdit()
self.centre_y.setPlaceholderText('y')
self.centre_x.setValidator(QtGui.QDoubleValidator())
self.centre_y.setValidator(QtGui.QDoubleValidator())
self.layout.addWidget(self.centre_y, 1, 2, 1, 1)
self.text_label = QtWidgets.QLabel('Text')
self.layout.addWidget(self.text_label, 2, 0, 1, 1)
self.text_lineedit =QtWidgets.QLineEdit()
self.layout.addWidget(self.text_lineedit, 2, 1, 1, 2)
self.layout.setRowStretch(3, 1)
def collect_args(self):
dic = super().collect_args()
centre = self.parse_point(self.centre_x, self.centre_y)
if centre is None:
return
dic['centre'] = centre
dic['text'] = self.text_lineedit.text()
return dic

View File

@ -0,0 +1,41 @@
from __future__ import annotations
import uuid
from pyqtgraph import mkPen
from gui_qt.lib.pg_objects import LogInfiniteLine, PlotItem
class LineObject:
def __init__(self, **kwargs):
self.id = str(uuid.uuid4())
self.pos = kwargs['pos']
self.angle = kwargs['angle']
self.color = kwargs['color']
self.drawing = LogInfiniteLine(pos=self.pos, angle=self.angle, pen=mkPen(color=self.color.rgb()))
def __str__(self):
return f'{"x" if self.angle==90 else "y"}={self.pos}'
class MultipointObject:
def __init__(self, **kwargs):
self.id = str(uuid.uuid4())
self.color = kwargs['color']
x, y = zip(*kwargs['pts'])
self.closed = kwargs['closed']
if self.closed:
x += (x[0],)
y += (y[0],)
self._x = x
self._y = y
self.drawing = PlotItem(x=self._x, y=self._y, pen=mkPen(color=self.color.rgb()))
def __str__(self):
return f'{len(self._y)-int(self.closed)}-pts'

View File

@ -1,5 +1,9 @@
from ..Qt import QtWidgets, QtCore
from .._py.guidelinewidget import Ui_Form
from __future__ import annotations
from gui_qt.Qt import QtWidgets, QtCore
from gui_qt._py.guidelinewidget import Ui_Form
from gui_qt.graphs.draw_inputs import EllipseWidget, LineWidget, MultiPointWidget, RectangleWidget, TextWidget
from gui_qt.graphs.draw_objects import LineObject, MultipointObject
class DrawingsWidget(QtWidgets.QWidget, Ui_Form):
@ -7,13 +11,25 @@ class DrawingsWidget(QtWidgets.QWidget, Ui_Form):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.connected_figure=None
self.setupUi(self)
def __call__(self, graphs):
for gid, name in graphs:
self.graph_comboBox.addItem(name, userData=gid)
self.widgets = [LineWidget(self), MultiPointWidget(self), TextWidget(self), RectangleWidget(self), EllipseWidget(self)]
for w in self.widgets:
self.stackedWidget.addWidget(w)
self.graphs = None
def update_tree(self):
for gid, windows in self.graphs.items():
item = QtWidgets.QTreeWidgetItem([windows.title])
item.setData(0, QtCore.Qt.UserRole, gid)
for d in windows.drawings.values():
child = QtWidgets.QTreeWidgetItem([d])
item.addChild(child)
self.treeWidget_2.addTopLevelItem(item)
# self.graph_comboBox.addItem(name, userData=gid)
def clear(self):
self.graph_comboBox.clear()
@ -22,140 +38,33 @@ class DrawingsWidget(QtWidgets.QWidget, Ui_Form):
def change_draw_type(self, idx: int):
self.stackedWidget.setCurrentIndex(idx)
"""
self.lines = {}
self.comments = {}
self.vh_pos_lineEdit.setValidator(QtGui.QDoubleValidator())
self.tableWidget.installEventFilter(self)
@QtCore.pyqtSlot(name='on_pushButton_clicked')
def make_line(self):
invalid = True
idx = self.mode_comboBox.currentIndex()
try:
pos = float(self.vh_pos_lineEdit.text())
# Vertical: idx=0; horizontal: idx = 1
angle = 90*abs(1-idx)
invalid = False
except ValueError:
pos = None
angle = None
pass
if invalid:
QtWidgets.QMessageBox().information(self, 'Invalid input', 'Input is not a valid number')
@QtCore.pyqtSlot(name='on_pushButton_3_clicked')
def make_drawing(self):
dic = self.stackedWidget.currentWidget().collect_args()
if dic is None:
QtWidgets.QMessageBox.information(self, 'Not working', 'Something is missing to create this object')
return
idx = self.treeWidget_2.selectedIndexes()
if idx:
item = self.treeWidget_2.itemFromIndex(idx[0])
graph_id = item.data(0, QtCore.Qt.UserRole)
if self.mode_comboBox.currentIndex() == 0:
new_obj = LineObject(**dic)
elif self.mode_comboBox.currentIndex() == 1:
new_obj = MultipointObject(**dic)
child = QtWidgets.QTreeWidgetItem([str(new_obj)])
child.setData(0, QtCore.Qt.UserRole, new_obj.id)
item.addChild(child)
qcolor = QtGui.QColor.fromRgb(*self.color_comboBox.value.rgb())
comment = self.comment_lineEdit.text()
line = LogInfiniteLine(pos=pos, angle=angle, movable=self.drag_checkBox.isChecked(), pen=qcolor)
line.sigPositionChanged.connect(self.move_line)
self.make_table_row(pos, angle, qcolor, comment)
graph_id = self.graph_comboBox.currentData()
try:
self.lines[graph_id].append(line)
self.comments[graph_id].append(comment)
except KeyError:
self.lines[graph_id] = [line]
self.comments[graph_id] = [comment]
self.line_created.emit(line, graph_id)
def set_graphs(self, graphs: list):
for graph_id, name in graphs:
self.graph_comboBox.addItem(name, userData=graph_id)
def remove_graph(self, graph_id: str):
idx = self.graph_comboBox.findData(graph_id)
if idx != -1:
self.graph_comboBox.removeItem(idx)
if graph_id in self.lines:
del self.lines[graph_id]
@QtCore.pyqtSlot(int, name='on_graph_comboBox_currentIndexChanged')
def change_graph(self, idx: int):
self.tableWidget.clear()
self.tableWidget.setRowCount(0)
graph_id = self.graph_comboBox.itemData(idx)
if graph_id in self.lines:
lines = self.lines[graph_id]
comments = self.comments[graph_id]
for i, line in enumerate(lines):
self.make_table_row(line.pos(), line.angle, line.pen.color(), comments[i])
def make_table_row(self, position, angle, color, comment):
if angle == 0:
try:
pos_label = 'x = ' + str(position.y())
except AttributeError:
pos_label = 'x = {position}'
elif angle == 90:
try:
pos_label = f'y = {position.x()}'
except AttributeError:
pos_label = f'y = {position}'
self.graphs[graph_id].addDrawing(new_obj)
else:
raise ValueError('Only horizontal or vertical lines are supported')
QtWidgets.QMessageBox.information(self, 'Not working', 'No graph is selected to add this object.')
item = QtWidgets.QTableWidgetItem(pos_label)
item.setFlags(QtCore.Qt.ItemIsSelectable)
item.setForeground(QtGui.QBrush(QtGui.QColor('black')))
self.treeWidget_2.expandAll()
row_count = self.tableWidget.rowCount()
self.tableWidget.setRowCount(row_count+1)
self.tableWidget.setItem(row_count, 0, item)
item2 = QtWidgets.QTableWidgetItem(comment)
self.tableWidget.setItem(row_count, 1, item2)
colitem = QtWidgets.QTableWidgetItem(' ')
colitem.setBackground(QtGui.QBrush(color))
colitem.setFlags(QtCore.Qt.ItemIsSelectable)
self.tableWidget.setVerticalHeaderItem(row_count, colitem)
def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool:
if evt.type() == QtCore.QEvent.KeyPress:
if evt.key() == QtCore.Qt.Key_Delete:
self.delete_line()
return True
return super().eventFilter(src, evt)
def delete_line(self):
remove_rows = sorted([item.row() for item in self.tableWidget.selectedItems()])
graph_id = self.graph_comboBox.currentData()
current_lines = self.lines[graph_id]
print(remove_rows)
for i in reversed(remove_rows):
print(i)
self.tableWidget.removeRow(i)
self.line_deleted.emit(current_lines[i], graph_id)
current_lines.pop(i)
self.comments[graph_id].pop(i)
@QtCore.pyqtSlot(object)
def move_line(self, line: InfiniteLine):
current_idx = self.graph_comboBox.currentData()
graphs = self.lines[current_idx]
i = -1
for i, line_i in enumerate(graphs):
if line == line_i:
break
pos = line.value()
text_item = self.tableWidget.item(i, 0)
text_item.setText(text_item.text()[:4]+f'{pos:.4g}')
"""
if __name__ == '__main__':
app = QtWidgets.QApplication([])
w = DrawingsWidget()
w.show()
app.exec()

View File