-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathQDisplayFrame.py
More file actions
328 lines (244 loc) · 11.5 KB
/
QDisplayFrame.py
File metadata and controls
328 lines (244 loc) · 11.5 KB
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
from typing import List, Tuple, Union
from PIL import Image, ImageQt
from PyQt5 import QtCore, QtGui, QtWidgets
class QDisplayFrame(QtWidgets.QFrame):
### Constatnts
spacing = 6
font = 16
textHeight = int(font * 1.4) # aproximate font to pixle conversion
pageLimit = 3
outlineWidth = 5
sliderTimerDelay = 20
sliderHoverAreaSize = 50
sliderMoveSpeed = 20
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#Button graphics
self.deleteButton = Button(ImageQt.toqpixmap(Image.open("DeleteButton.png")))
self.rightButton = Button(ImageQt.toqpixmap(Image.open("RightButton.png")))
self.leftButton = Button(ImageQt.toqpixmap(Image.open("LeftButton.png")))
### Containers
self.pages = []
### Variables
self.sliderPosition = 0
self.sliderDelta = 0
self.selectedIndex = -1
# Set up
self.setMouseTracking(True)
#######################################################################################
### Controller
def mouseMoveEvent(self, event):
self.sliderCheck(event.pos())
self.hoverCheck(event.pos())
#checks if the cursor is hovering over the "move left" or "move right" areas at the edges of the frame
def sliderCheck(self, pos):
if pos.x() < self.sliderHoverAreaSize and self.sliderPosition > 0:
if self.sliderDelta == 0:
self.sliderDelta = - self.sliderMoveSpeed
self.sliderTimerID = self.startTimer( self.sliderTimerDelay, QtCore.Qt.CoarseTimer )
elif pos.x() > self.width() - self.sliderHoverAreaSize:
if self.sliderDelta == 0:
self.sliderDelta = self.sliderMoveSpeed
self.sliderTimerID = self.startTimer( self.sliderTimerDelay, QtCore.Qt.CoarseTimer )
else:
if self.sliderDelta != 0:
self.sliderDelta = 0
self.killTimer(self.sliderTimerID)#stop counting
#layz substitute for a mouse clicked event
def mouseReleaseEvent(self, event):
# something is selected
if self.selectedIndex >= 0:
if self.deleteButton.boundaries.contains(event.pos()):
self.deletePage()
elif self.leftButton.boundaries.contains(event.pos()):
self.swapPages(self.selectedIndex, self.selectedIndex - 1)
elif self.rightButton.boundaries.contains(event.pos()):
self.swapPages(self.selectedIndex, self.selectedIndex + 1)
self.repaint()
#Checks to see if the cursor is hovering over a page
def hoverCheck(self, pos):
#no hover checks while slider is in motion
if self.sliderDelta != 0:
return
#default case
self.selectedIndex = -1
#check for hovering
for index, page in enumerate(self.pages):
if page.boundaries.contains(pos):
self.selectedIndex = index
self.positionButtons(page.boundaries, index)
self.repaint()
#positions the applicable buttons onto the page
def positionButtons(self, pageBoundaries, index):
#delete
delX = pageBoundaries.x() + ((pageBoundaries.width() - self.deleteButton.boundaries.width() + 1) / 2)
delY = pageBoundaries.height() - self.deleteButton.boundaries.height() - 10
self.deleteButton.setPosition(delX, delY)
#left and right
leftX = pageBoundaries.x()
rightX = pageBoundaries.x() + pageBoundaries.width() - self.deleteButton.boundaries.width()
lrY = pageBoundaries.y() + ((pageBoundaries.height() - self.deleteButton.boundaries.height() + 1) / 2)
self.leftButton.setPosition(leftX, lrY)
self.rightButton.setPosition(rightX, lrY)
#disable the left and right buttons if this is the last page in that direction
self.leftButton.enabled = (index != 0)
self.rightButton.enabled = (index != (len(self.pages) - 1))
#don't retain selection when leaving the frame
def leaveEvent(self, event):
self.selectedIndex = -1
#remove any slide timers
if self.sliderDelta != 0:
self.sliderDelta = 0
self.killTimer(self.sliderTimerID)
self.repaint()
#Scale pages to match resize
def resizeEvent(self, event):
self.calculatePageBoundries()
self.checkBoundaries()
self.calculateSliderPositions()
#timer used for "hover and hold" scrolling left and right
def timerEvent(self, event):
if len(self.pages) > self.pageLimit:
#add initial movement
self.sliderPosition += self.sliderDelta
#disallow selecting while moving
if self.selectedIndex >= 0:
self.selectedIndex = -1
self.checkBoundaries()
self.calculateSliderPositions()
self.repaint()
#corrects the scroll position if it has gone too far in one direction
def checkBoundaries(self, forceRightmost = False):
#no pages, return to default
if len(self.pages) == 0:
self.sliderPosition = 0
return
#check right boundary
lastPageBoundaries = self.pages[len(self.pages) - 1].boundaries
rightBoundry = lastPageBoundaries.x() + lastPageBoundaries.width()
if self.width() > rightBoundry or forceRightmost:
self.sliderPosition = (self.sliderPosition + rightBoundry) - self.width()
#check left boundary
if self.sliderPosition < 0:
self.sliderPosition = 0
#######################################################################################
### Model
#adds a new page on the rightmost position
def addPage( self, image, width, height):
self.pages.append( Page(ImageQt.toqpixmap(image), width, height))
self.calculatePageBoundries()
#True option makes sure the new scan is in view
self.checkBoundaries(True)
self.calculateSliderPositions()
self.repaint()
#deletes the page at selectedIndex and repositions remaining pages
def deletePage(self):
del self.pages[self.selectedIndex]
self.selectedIndex = -1
# first call to move pages right of the deleted
self.calculatePageBoundries()
self.checkBoundaries()
self.calculateSliderPositions()
#swaps the position of 2 pages given their indexes. used for reordering pages
def swapPages(self, index1, index2):
pageCount = len(self.pages)
# dont allow swaps with self or invalid indecies
if (index1 >= 0 and index2 >= 0 and
index1 < pageCount and index2 < pageCount and
index1 != index2):
tempPage = self.pages[index1]
self.pages[index1] = self.pages[index2]
self.pages[index2] = tempPage
self.selectedIndex = -1
self.calculatePageBoundries()
self.repaint()
#calculates where and how large pages should be given the current frame size
def calculatePageBoundries(self):
#return if there is nothing to calculate
if len(self.pages) == 0:
return
maxWidth, maxHeight = self.getMeasurements()
for count, page in enumerate(self.pages):
page.scaledPixMap = page.originalPixMap.scaled(maxWidth, maxHeight, QtCore.Qt.KeepAspectRatio)
self.calculateSliderPositions()
def calculateSliderPositions(self):
runningWidth = 0
for count, page in enumerate(self.pages):
xPosition = runningWidth - self.sliderPosition
page.boundaries = QtCore.QRect(xPosition, 0, page.scaledPixMap.width(), page.scaledPixMap.height())
runningWidth += page.scaledPixMap.width() + self.spacing
#calculates the largest area a single page is allowed to inhabit
def getMeasurements(self)-> Union[int, int]:
totalWidth = self.width()
totalheight = self.height()
totalPages = len(self.pages)
if totalPages > self.pageLimit:
totalPages = self.pageLimit
maxWidth = ((totalWidth - ((totalPages - 1) * self.spacing) ) / totalPages)
maxHeight = totalheight - self.textHeight
return maxWidth, maxHeight
#returns a list of the scanned pages
def getPages(self)-> List[Tuple[ImageQt.QPixmap, int, int]]:
scannedPages = []
for p in self.pages:
scannedPages.append((p.originalPixMap, p.width, p.height))
return scannedPages
#######################################################################################
### View
#paints all elements to the screen
def paintEvent(self, event):
painter = QtGui.QPainter(self)
self.clearScreen(painter)
#return if there is nothing to draw
if len(self.pages) == 0:
return
for index, page in enumerate(self.pages):
#this page is off the screen on the left and doesnt need to be drawn
if page.boundaries.x() + page.boundaries.width() < 0:
continue
#this page is off the screen on the right. no more pages need to be drawn.
if page.boundaries.x() > self.width():
break
# Draw scanned image
painter.drawPixmap(QtCore.QPoint(page.boundaries.x(), page.boundaries.y()), page.scaledPixMap)
if index == self.selectedIndex:
#draw outline
pen = QtGui.QPen(QtCore.Qt.green)
pen.setWidth(self.outlineWidth)
painter.drawRect(page.boundaries)
#draw buttons
painter.drawPixmap(QtCore.QPoint(self.deleteButton.boundaries.x(), self.deleteButton.boundaries.y()), self.deleteButton.pixMap)
if self.leftButton.enabled == True:
painter.drawPixmap(QtCore.QPoint(self.leftButton.boundaries.x(), self.leftButton.boundaries.y()), self.leftButton.pixMap)
if self.rightButton.enabled == True:
painter.drawPixmap(QtCore.QPoint(self.rightButton.boundaries.x(), self.rightButton.boundaries.y()), self.rightButton.pixMap)
# Draw page number
painter.setFont(QtGui.QFont("Arial", self.font))
painter.drawText(QtCore.QRect(page.boundaries.x(),
page.boundaries.height(),
page.boundaries.width(),
self.textHeight),
QtCore.Qt.AlignCenter,
str(index + 1))
painter.end()
#draws background color over entire screen
def clearScreen(self, painter):
painter.drawRect(QtCore.QRect(-1,-1,
self.width() + 1,
self.height() + 1))
#######################################################################################
### Helper Classes
class Page():
def __init__(self, pixMap, width, height):
self.originalPixMap = pixMap
self.scaledPixMap = pixMap
self.width = width
self.height = height
self.boundaries = QtCore.QRect()
class Button():
def __init__(self, pixMap):
self.pixMap = pixMap
self.boundaries = QtCore.QRect(0, 0, pixMap.width(), pixMap.height())
self.enabled = True
def setPosition(self, x, y):
self.boundaries = QtCore.QRect(x, y, self.pixMap.width(), self.pixMap.height())