CoolFace
Apppublic

neuralcomputation/batik

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
behavior.py292 linesDownload Raw Back to utils
1# behavior.py
2"""
3Overview comment here
4"""
5
6from qtpy.QtCore import QAbstractItemModel, QAbstractTableModel, QModelIndex, QObject, Qt, Signal, Slot
7from qtpy.QtGui import QColor
8import os
9
10class Behavior(QObject):
11    """
12    An annotation behavior, which is quite simple.  It comprises:
13    name        The name of the behavior, which is displayed on various UI widgets
14    hot_key     The case-sensitive key stroke used to start and stop instances of the behavior
15    color       The color with which to display this behavior
16    """
17
18    def __init__(self, name: str, hot_key: str = '', color: QColor = QColor('gray'), active = False, visible = True):
19        super().__init__()
20        self._name = name
21        self._hot_key = '' if hot_key == '_' else hot_key
22        self._color = color
23        self._visible = visible
24        self._active = active
25        self._get_functions = {
26            'hot_key': self.get_hot_key,
27            'name': self.get_name,
28            'color': self.get_color,
29            'active': self.is_active,
30            'visible': self.is_visible
31            }
32        self._set_functions = {
33            'hot_key': self.set_hot_key,
34            'name': self.set_name,
35            'color': self.set_color,
36            'active': self.set_active,
37            'visible': self.set_visible
38            }
39
40    def __repr__(self):
41        return f"Behavior: name={self._name}, hot_key={self._hot_key}, color={self._color}, active={self._active}, visible={self._visible}"
42
43    def get(self, key):
44        # may raise KeyError
45        return self._get_functions[key]()
46
47    def set(self, key, value):
48        # may raise KeyError
49        self._set_functions[key](value)
50
51    def get_hot_key(self):
52        return self._hot_key
53
54    def set_hot_key(self, hot_key: str):
55        self._hot_key = hot_key
56
57    def get_color(self):
58        return self._color
59
60    def set_color(self, color: QColor):
61        self._color = color
62
63    def is_active(self):
64        return self._active
65
66    @Slot(bool)
67    def set_active(self, active):
68        self._active = active
69
70    def is_visible(self):
71        return self._visible
72
73    @Slot(bool)
74    def set_visible(self, visible):
75        self._visible = visible
76
77    def get_name(self):
78        return self._name
79
80    def set_name(self, name):
81        self._name = name
82
83    def toDict(self):
84        return {
85            'hot_key': '_' if self._hot_key == '' else self._hot_key,
86            'color': self._color,
87            'name': self._name,
88            'active': self._active,
89            'visible': self._visible
90            }
91
92class Behaviors(QAbstractTableModel):
93    """
94    A set of behaviors, which represent "all" possible behaviors.
95    The class supports reading from and writing to profile files that specify
96    the default hot_key and color for each behavior, along with the name.
97
98    Derives from QAbstractTableModel so that it can be viewed and edited
99    directly in a QTableView widget.
100
101    Use getattr(name) to get the Behavior instance for a given name.
102    Use from_hot_key(key) to get the behavior(s) given the hot key.
103        Returns None if the hot_key isn't defined.
104    """
105
106    behaviors_changed = Signal()
107    layout_changed = Signal()
108
109    def __init__(self):
110        super().__init__()
111        self._items = []
112        self._by_name = {}
113        self._by_hot_key = {}
114        self._header = ['hot_key', 'color', 'name', 'active', 'visible']
115        self._searchList = [self._by_hot_key, None, self._by_name, None, None]
116        self._delete_behavior = Behavior('_delete', color = QColor('black'))
117        self._immutableColumns = set()
118        self._booleanColumns = set([self._header.index('active'), self._header.index('visible')])
119        self._role_to_str = {
120            Qt.DisplayRole: "DisplayRole",
121            Qt.DecorationRole: "DecorationRole",
122            Qt.EditRole: "EditRole",
123            Qt.ToolTipRole: "ToolTipRole",
124            Qt.StatusTipRole: "StatusTipRole",
125            Qt.WhatsThisRole: "WhatsThisRole",
126            Qt.SizeHintRole: "SizeHintRole",
127            Qt.FontRole: "FontRole",
128            Qt.TextAlignmentRole: "TextAlignmentRole",
129            Qt.BackgroundRole: "BackgroundRole",
130            Qt.ForegroundRole: "ForegroundRole",
131            Qt.CheckStateRole: "CheckStateRole",
132            Qt.InitialSortOrderRole: "InitialSortOrderRole",
133            Qt.AccessibleTextRole: "AccessibleTextRole",
134            Qt.UserRole: "UserRole"
135        }
136
137    def add(self, beh: Behavior, row=-1):
138        if row < 0:
139            row = self.rowCount()
140        self.beginInsertRows(QModelIndex(), row, row)
141        self._items.insert(row, beh)
142        self._by_name[beh.get_name()] = beh
143        hot_key = beh.get_hot_key()
144        if hot_key:
145            if hot_key not in self._by_hot_key.keys():
146                self._by_hot_key[hot_key] = []
147            assert(isinstance(self._by_hot_key[hot_key], list))
148            self._by_hot_key[hot_key].append(beh)
149        self.endInsertRows()
150        self.dataChanged.emit(
151            self.index(row, 0, QModelIndex()),
152            self.index(row, self.columnCount()-1, QModelIndex()),
153            [Qt.DisplayRole, Qt.EditRole])
154        self.behaviors_changed.emit()
155
156    def load(self, f):
157        line = f.readline()
158        while line:
159            hot_key, name, r, g, b = line.strip().split(' ')
160            if hot_key == '_':
161                hot_key = ''
162            self.add(Behavior(name, hot_key, QColor.fromRgbF(float(r), float(g), float(b))))
163            line = f.readline()
164
165    def save(self, f):
166        for beh in self._items:
167            h = beh.get_hot_key()
168            if h == '':
169                h = '_'
170            color = beh.get_color()
171            f.write(f"{h} {beh.get_name()} {color.redF()} {color.greenF()} {color.blueF()}" + os.linesep)
172
173    def get(self, name):
174        if name not in self._by_name.keys():
175            return None
176        return self._by_name[name]
177
178    def from_hot_key(self, key):
179        """
180        Return the list of behaviors associated with this hot key, if any
181        """
182        try:
183            return self._by_hot_key[key]
184        except KeyError:
185            return None
186
187    def len(self):
188        return len(self._items)
189
190    def header(self):
191        return self._header
192
193    def colorColumns(self):
194        return [self._header.index('color')]
195
196    def __iter__(self):
197        return iter(self._items)
198
199    def getDeleteBehavior(self):
200        return self._delete_behavior
201
202    def addIfMissing(self, nameToAdd):
203        if nameToAdd not in self._by_name:
204            self.add(Behavior(nameToAdd, '', QColor('gray')))
205            return True
206        return False
207
208    def isImmutable(self, index):
209        return index.column() in self._immutableColumns
210
211    def setImmutable(self, column):
212        self._immutableColumns.add(column)
213
214    # QAbstractTableModel API methods
215
216    def headerData(self, col, orientation, role):
217        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
218            return self._header[col]
219        return None
220
221    def rowCount(self, parent=None):
222        return len(self._items)
223
224    def columnCount(self, parent=None):
225        return len(self._header)
226
227    def data(self, index, role=Qt.DisplayRole):
228        datum = self._items[index.row()].get(self._header[index.column()])
229        if isinstance(datum, bool):
230            if role in [Qt.CheckStateRole, Qt.EditRole]:
231                return Qt.Checked if datum else Qt.Unchecked
232            return None
233        if role in [Qt.DisplayRole, Qt.EditRole]:
234            return self._items[index.row()].get(self._header[index.column()])
235        return None
236
237    def setData(self, index, value, role=Qt.EditRole):
238        if not role in [Qt.CheckStateRole, Qt.EditRole]:
239            return False
240        if role == Qt.CheckStateRole:
241            value = bool(value)
242        beh = self._items[index.row()]
243        key = self._header[index.column()]
244        name = beh.get_name()
245        hot_key = beh.get_hot_key()
246        beh.set(key, value)
247        if key == 'hot_key' and value != hot_key:
248            # disassociate this behavior from the hot_key
249            # and associate with the new hot_key if not ''
250            if hot_key != '':
251                del(self._by_hot_key[hot_key])
252            if value != '':
253                if value not in self._by_hot_key.keys():
254                    self._by_hot_key[value] = []
255                assert(isinstance(self._by_hot_key[value], list))
256                self._by_hot_key[value].append(beh)
257        elif key == 'name' and value != name:
258            if name in self._by_name.keys():
259                del(self._by_name[name])
260            self._by_name[value] = beh
261        self.behaviors_changed.emit()
262        self.dataChanged.emit(index, index, [role])
263        return True
264
265    def insertRows(self, row, count, parent):
266        if count < 1 or row < 0 or row > self.rowCount():
267            return False
268        self.beginInsertRows(QModelIndex(), row, row)
269        for r in range(count):
270            self._items.insert(row, Behavior('', active=True))
271        self.endInsertRows()
272        return True
273
274    def removeRows(self, row, count, parent=QModelIndex()):
275        if count <= 0 or row < 0 or row + count > self.rowCount(parent):
276            return False
277        self.beginRemoveRows(parent, row, row + count - 1)
278        for item in self._items[row:row+count-1]:
279            self._by_name.pop(item.name)
280            self._by_hot_key.pop(item.hot_key)
281        for i in range(count):
282            self._items.pop(row)
283        self.endRemoveRows()
284
285    def flags(self, index):
286        f = super().flags(index)
287        if index.column() not in self._immutableColumns:
288            f |= Qt.ItemIsEditable
289        if index.column() in self._booleanColumns:
290            f = (f & ~(Qt.ItemIsSelectable | Qt.ItemIsEditable)) | Qt.ItemIsUserCheckable
291        return f
292