eric7/EricWidgets/EricModelToolBar.py

branch
eric7
changeset 8358
144a6b854f70
parent 8356
68ec9c3d4de5
child 8366
2a9f5153c438
equal deleted inserted replaced
8357:a081458cc57b 8358:144a6b854f70
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2021 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(evt.globalPosition().toPoint())
182
183 return False
184
185 def dragEnterEvent(self, evt):
186 """
187 Protected method to handle drag enter events.
188
189 @param evt reference to the event (QDragEnterEvent)
190 """
191 if self.__model is not None:
192 mimeTypes = self.__model.mimeTypes()
193 for mimeType in mimeTypes:
194 if evt.mimeData().hasFormat(mimeType):
195 evt.acceptProposedAction()
196
197 super().dragEnterEvent(evt)
198
199 def dropEvent(self, evt):
200 """
201 Protected method to handle drop events.
202
203 @param evt reference to the event (QDropEvent)
204 @exception RuntimeError raised to indicate an invalid model index
205 """
206 if self.__model is not None:
207 act = self.actionAt(evt.position().toPoint())
208 parentIndex = self.__root
209 if act is None:
210 row = self.__model.rowCount(self.__root)
211 else:
212 idx = self.index(act)
213 if not idx.isValid():
214 raise RuntimeError("invalid index")
215 row = idx.row()
216 if self.__model.hasChildren(idx):
217 parentIndex = idx
218 row = self.__model.rowCount(idx)
219
220 self.__dropRow = row
221 self.__dropIndex = parentIndex
222 evt.acceptProposedAction()
223 self.__model.dropMimeData(evt.mimeData(), evt.dropAction(),
224 row, 0, parentIndex)
225
226 super().dropEvent(evt)
227
228 def mouseMoveEvent(self, evt):
229 """
230 Protected method to handle mouse move events.
231
232 @param evt reference to the event (QMouseEvent)
233 @exception RuntimeError raised to indicate an invalid model index
234 """
235 if self.__model is None:
236 super().mouseMoveEvent(evt)
237 return
238
239 if not (evt.buttons() & Qt.MouseButton.LeftButton):
240 super().mouseMoveEvent(evt)
241 return
242
243 manhattanLength = (evt.position().toPoint() -
244 self.__dragStartPosition).manhattanLength()
245 if manhattanLength <= QApplication.startDragDistance():
246 super().mouseMoveEvent(evt)
247 return
248
249 act = self.actionAt(self.__dragStartPosition)
250 if act is None:
251 super().mouseMoveEvent(evt)
252 return
253
254 idx = self.index(act)
255 if not idx.isValid():
256 raise RuntimeError("invalid index")
257
258 drag = QDrag(self)
259 drag.setMimeData(self.__model.mimeData([idx]))
260 actionRect = self.actionGeometry(act)
261 drag.setPixmap(self.grab(actionRect))
262
263 if drag.exec() == Qt.DropAction.MoveAction:
264 row = idx.row()
265 if self.__dropIndex == idx.parent() and self.__dropRow <= row:
266 row += 1
267 self.__model.removeRow(row, self.__root)
268
269 def hideEvent(self, evt):
270 """
271 Protected method to handle hide events.
272
273 @param evt reference to the hide event (QHideEvent)
274 """
275 self.clear()
276 super().hideEvent(evt)
277
278 def showEvent(self, evt):
279 """
280 Protected method to handle show events.
281
282 @param evt reference to the hide event (QHideEvent)
283 """
284 if len(self.actions()) == 0:
285 self._build()
286 super().showEvent(evt)
287
288 def resetFlags(self):
289 """
290 Public method to reset the saved internal state.
291 """
292 self._mouseButton = Qt.MouseButton.NoButton
293 self._keyboardModifiers = Qt.KeyboardModifier.NoModifier

eric ide

mercurial