src/eric7/EricWidgets/EricModelToolBar.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a tool bar populated from a QAbstractItemModel.
8 """
9
10 from PyQt6.QtCore import pyqtSignal, Qt, QModelIndex, QPoint, QEvent
11 from PyQt6.QtGui import QDrag, QIcon
12 from PyQt6.QtWidgets import QApplication, QToolBar, QToolButton
13
14
15 class EricModelToolBar(QToolBar):
16 """
17 Class implementing a tool bar populated from a QAbstractItemModel.
18
19 @signal activated(QModelIndex) emitted when an action has been triggered
20 """
21 activated = pyqtSignal(QModelIndex)
22
23 def __init__(self, title=None, parent=None):
24 """
25 Constructor
26
27 @param title title for the tool bar (string)
28 @param parent reference to the parent widget (QWidget)
29 """
30 if title is not None:
31 super().__init__(title, parent)
32 else:
33 super().__init__(parent)
34
35 self.__model = None
36
37 self.__root = QModelIndex()
38 self.__dragStartPosition = QPoint()
39
40 if self.isVisible():
41 self._build()
42
43 self.setAcceptDrops(True)
44
45 self._mouseButton = Qt.MouseButton.NoButton
46 self._keyboardModifiers = Qt.KeyboardModifier.NoModifier
47 self.__dropRow = -1
48 self.__dropIndex = None
49
50 def setModel(self, model):
51 """
52 Public method to set the model for the tool bar.
53
54 @param model reference to the model (QAbstractItemModel)
55 """
56 if self.__model is not None:
57 self.__model.modelReset.disconnect(self._build)
58 self.__model.rowsInserted[QModelIndex, int, int].disconnect(
59 self._build)
60 self.__model.rowsRemoved[QModelIndex, int, int].disconnect(
61 self._build)
62 self.__model.dataChanged.disconnect(
63 self._build)
64
65 self.__model = model
66
67 if self.__model is not None:
68 self.__model.modelReset.connect(self._build)
69 self.__model.rowsInserted[QModelIndex, int, int].connect(
70 self._build)
71 self.__model.rowsRemoved[QModelIndex, int, int].connect(
72 self._build)
73 self.__model.dataChanged.connect(
74 self._build)
75
76 def model(self):
77 """
78 Public method to get a reference to the model.
79
80 @return reference to the model (QAbstractItemModel)
81 """
82 return self.__model
83
84 def setRootIndex(self, idx):
85 """
86 Public method to set the root index.
87
88 @param idx index to be set as the root index (QModelIndex)
89 """
90 self.__root = idx
91
92 def rootIndex(self):
93 """
94 Public method to get the root index.
95
96 @return root index (QModelIndex)
97 """
98 return self.__root
99
100 def _build(self):
101 """
102 Protected slot to build the tool bar.
103 """
104 if self.__model is None:
105 return
106
107 self.clear()
108
109 for i in range(self.__model.rowCount(self.__root)):
110 idx = self.__model.index(i, 0, self.__root)
111
112 title = idx.data(Qt.ItemDataRole.DisplayRole)
113 icon = idx.data(Qt.ItemDataRole.DecorationRole)
114 if icon == NotImplemented or icon is None:
115 icon = QIcon()
116 folder = self.__model.hasChildren(idx)
117
118 act = self.addAction(icon, title)
119 act.setData(idx)
120
121 button = self.widgetForAction(act)
122 button.installEventFilter(self)
123
124 if folder:
125 menu = self._createMenu()
126 menu.setModel(self.__model)
127 menu.setRootIndex(idx)
128 button.setMenu(menu)
129 button.setPopupMode(
130 QToolButton.ToolButtonPopupMode.InstantPopup)
131 button.setToolButtonStyle(
132 Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
133
134 def index(self, action):
135 """
136 Public method to get the index of an action.
137
138 @param action reference to the action to get the index for (QAction)
139 @return index of the action (QModelIndex)
140 """
141 if action is None:
142 return QModelIndex()
143
144 idx = action.data()
145 if idx is None:
146 return QModelIndex()
147
148 if not isinstance(idx, QModelIndex):
149 return QModelIndex()
150
151 return idx
152
153 def _createMenu(self):
154 """
155 Protected method to create the menu for a tool bar action.
156
157 @return menu for a tool bar action (EricModelMenu)
158 """
159 from .EricModelMenu import EricModelMenu
160 return EricModelMenu(self)
161
162 def eventFilter(self, obj, evt):
163 """
164 Public method to handle event for other objects.
165
166 @param obj reference to the object (QObject)
167 @param evt reference to the event (QEvent)
168 @return flag indicating that the event should be filtered out (boolean)
169 """
170 if evt.type() == QEvent.Type.MouseButtonRelease:
171 self._mouseButton = evt.button()
172 self._keyboardModifiers = evt.modifiers()
173 act = obj.defaultAction()
174 idx = self.index(act)
175 if idx.isValid():
176 self.activated[QModelIndex].emit(idx)
177 elif (
178 evt.type() == QEvent.Type.MouseButtonPress and
179 evt.buttons() & Qt.MouseButton.LeftButton
180 ):
181 self.__dragStartPosition = self.mapFromGlobal(
182 evt.globalPosition().toPoint())
183
184 return False
185
186 def dragEnterEvent(self, evt):
187 """
188 Protected method to handle drag enter events.
189
190 @param evt reference to the event (QDragEnterEvent)
191 """
192 if self.__model is not None:
193 mimeTypes = self.__model.mimeTypes()
194 for mimeType in mimeTypes:
195 if evt.mimeData().hasFormat(mimeType):
196 evt.acceptProposedAction()
197
198 super().dragEnterEvent(evt)
199
200 def dropEvent(self, evt):
201 """
202 Protected method to handle drop events.
203
204 @param evt reference to the event (QDropEvent)
205 @exception RuntimeError raised to indicate an invalid model index
206 """
207 if self.__model is not None:
208 act = self.actionAt(evt.position().toPoint())
209 parentIndex = self.__root
210 if act is None:
211 row = self.__model.rowCount(self.__root)
212 else:
213 idx = self.index(act)
214 if not idx.isValid():
215 raise RuntimeError("invalid index")
216 row = idx.row()
217 if self.__model.hasChildren(idx):
218 parentIndex = idx
219 row = self.__model.rowCount(idx)
220
221 self.__dropRow = row
222 self.__dropIndex = parentIndex
223 evt.acceptProposedAction()
224 self.__model.dropMimeData(evt.mimeData(), evt.dropAction(),
225 row, 0, parentIndex)
226
227 super().dropEvent(evt)
228
229 def mouseMoveEvent(self, evt):
230 """
231 Protected method to handle mouse move events.
232
233 @param evt reference to the event (QMouseEvent)
234 @exception RuntimeError raised to indicate an invalid model index
235 """
236 if self.__model is None:
237 super().mouseMoveEvent(evt)
238 return
239
240 if not (evt.buttons() & Qt.MouseButton.LeftButton):
241 super().mouseMoveEvent(evt)
242 return
243
244 manhattanLength = (evt.position().toPoint() -
245 self.__dragStartPosition).manhattanLength()
246 if manhattanLength <= QApplication.startDragDistance():
247 super().mouseMoveEvent(evt)
248 return
249
250 act = self.actionAt(self.__dragStartPosition)
251 if act is None:
252 super().mouseMoveEvent(evt)
253 return
254
255 idx = self.index(act)
256 if not idx.isValid():
257 raise RuntimeError("invalid index")
258
259 drag = QDrag(self)
260 drag.setMimeData(self.__model.mimeData([idx]))
261 actionRect = self.actionGeometry(act)
262 drag.setPixmap(self.grab(actionRect))
263
264 if drag.exec() == Qt.DropAction.MoveAction:
265 row = idx.row()
266 if self.__dropIndex == idx.parent() and self.__dropRow <= row:
267 row += 1
268 self.__model.removeRow(row, self.__root)
269
270 def hideEvent(self, evt):
271 """
272 Protected method to handle hide events.
273
274 @param evt reference to the hide event (QHideEvent)
275 """
276 self.clear()
277 super().hideEvent(evt)
278
279 def showEvent(self, evt):
280 """
281 Protected method to handle show events.
282
283 @param evt reference to the hide event (QHideEvent)
284 """
285 if len(self.actions()) == 0:
286 self._build()
287 super().showEvent(evt)
288
289 def resetFlags(self):
290 """
291 Public method to reset the saved internal state.
292 """
293 self._mouseButton = Qt.MouseButton.NoButton
294 self._keyboardModifiers = Qt.KeyboardModifier.NoModifier

eric ide

mercurial