1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
from fbs_runtime.application_context.PyQt5 import ApplicationContext
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QMimeData
from PyQt5.QtGui import QIcon, QDrag
from PyQt5.QtWidgets import (QBoxLayout, QDockWidget, QGridLayout, QLineEdit,
QScrollArea, QToolButton, QWidget, QApplication)
from re import search, IGNORECASE
from .data import toolbarItems
from .funcs import fileImporter
from .layout import flowLayout
# resourceManager = ApplicationContext() #Used to load images, mainly toolbar icons
class toolbar(QDockWidget):
"""
Defines the right side toolbar, using QDockWidget.
"""
toolbuttonClicked = pyqtSignal(dict) #signal for any object button pressed
def __init__(self, parent = None):
super(toolbar, self).__init__(parent)
self.toolbarButtonDict = dict() #initializes empty dict to store toolbar buttons
self.toolbarItems(toolbarItems.keys()) #creates all necessary buttons
self.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)
#mainly used to disable closeability of QDockWidget
#declare main widget and layout
self.widget = QWidget(self)
self.layout = QBoxLayout(QBoxLayout.TopToBottom, self.widget)
self.setAllowedAreas(Qt.AllDockWidgetAreas)
self.searchBox = QLineEdit(self.widget) #search box to search through componenets
#connect signal to filter slot, add searchbar to toolbar
self.searchBox.textChanged.connect(self.searchQuery)
self.layout.addWidget(self.searchBox, alignment=Qt.AlignHCenter)
#create a scrollable area to house all buttons
self.diagArea = QScrollArea(self)
self.layout.addWidget(self.diagArea)
self.diagAreaWidget = QWidget(self.diagArea) #inner widget for scroll area
#custom layout for inner widget
self.diagAreaLayout = flowLayout(self.diagAreaWidget)
# self.diagArea.setWidget() #set inner widget to scroll area
self.setWidget(self.widget) #set main widget to dockwidget
def clearLayout(self):
# used to clear all items from toolbar, by parenting it to the toolbar instead
# this works because changing parents moves widgets to be the child of the new
# parent, setting it to none, would have qt delete them to free memory
for i in reversed(range(self.diagAreaLayout.count())):
# since changing parent would effect indexing, its important to go in reverse
self.diagAreaLayout.itemAt(i).widget().setParent(self)
def populateToolbar(self, list):
#called everytime the button box needs to be updated(incase of a filter)
self.clearLayout() #clears layout
for item in list:
self.diagAreaLayout.addWidget(self.toolbarButtonDict[item])
def searchQuery(self):
# shorten toolbaritems list with search items
# self.populateToolbar() # populate with toolbar items
text = self.searchBox.text() #get text
if text == '':
self.populateToolbar(self.toolbarItemList) # restore everything on empty string
else:
# use regex to search filter through button list and add the remainder to toolbar
self.populateToolbar(filter(lambda x: search(text, x, IGNORECASE), self.toolbarItemList))
def resize(self):
# called when main window resizes, overloading resizeEvent caused issues.
parent = self.parentWidget() #used to get parent dimensions
self.layout.setDirection(QBoxLayout.TopToBottom) # here so that a horizontal toolbar can be implemented later
self.setFixedWidth(.12*parent.width()) #12% of parent width
self.setFixedHeight(self.height()) #span available height
width = .12*parent.width() #12% of parent width
self.diagAreaWidget.setFixedWidth(width) #set inner widget width
# the following line, sets the required height for the current width, so that blank space doesnt occur
self.diagAreaWidget.setFixedHeight(self.diagAreaLayout.heightForWidth(width))
def toolbarItems(self, items):
#helper functions to create required buttons
for item in items:
obj = toolbarItems[item]
button = toolbarButton(self, obj)
button.clicked.connect(lambda : self.toolbuttonClicked.emit(obj))
self.toolbarButtonDict[item] = button
@property
def toolbarItemList(self):
#generator to iterate over all buttons
for i in self.toolbarButtonDict.keys():
yield i
class toolbarButton(QToolButton):
"""
Custom buttons for components that implements drag and drop functionality
item -> dict from toolbarItems dict, had 4 properties, name, object, icon and default arguments.
"""
def __init__(self, parent = None, item = None):
super(toolbarButton, self).__init__(parent)
#uses fbs resource manager to get icons
self.setIcon(QIcon(fileImporter(f'toolbar/{item["icon"]}')))
self.setIconSize(QSize(40, 40)) #unecessary but left for future references
self.dragStartPosition = None #intialize value for drag event
self.itemObject = item['object'] #refer current item object, to handle drag mime
self.setText(item["name"]) #button text
self.setToolTip(item["name"]) #button tooltip
def mousePressEvent(self, event):
#check if button was pressed or there was a drag intent
super(toolbarButton, self).mousePressEvent(event)
if event.button() == Qt.LeftButton:
self.dragStartPosition = event.pos() #set dragstart position
def mouseMoveEvent(self, event):
#handles drag
if not (event.buttons() and Qt.LeftButton):
return #ignore if left click is not held
if (event.pos() - self.dragStartPosition).manhattanLength() < QApplication.startDragDistance():
return #check if mouse was dragged enough, manhattan length is a rough and quick method in qt
drag = QDrag(self) #create drag object
mimeData = QMimeData() #create drag mime
mimeData.setText(self.itemObject) # set mime value for view to accept
drag.setMimeData(mimeData) # attach mime to drag
drag.exec(Qt.CopyAction) #execute drag
def sizeHint(self):
#defines button size
return self.minimumSizeHint()
def minimumSizeHint(self):
#defines button size
return QSize(30, 30)
|