forked from viktor-sarge/digitalasagor
/
playergui.py
354 lines (277 loc) · 12.4 KB
/
playergui.py
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# Copyright 2013 Regionbibliotek Halland
#
# This file is part of Digitala sagor.
#
# Digitala sagor is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Digitala sagor is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Digitala sagor. If not, see <http://www.gnu.org/licenses/>.
from Tkinter import NW, NORMAL, HIDDEN, StringVar, Canvas
from tkMessageBox import showerror
import PIL.Image as Image
import PIL.ImageTk as ImageTk
from test.test_iterlen import len
Image._initialized=2
from language import lang
import language as lng
import ini
from playerframe import PlayerFrame
from previewframe import PreviewFrame
from datacollector import DataCollector
from transpbtn import TransparentButton
from common import updateProgress, HylteSettings
from progressdlg import DataModelLoader
_preview = 'preview'
_play = 'play'
class PlayerGui:
"""The main GUI of the Digitala sagor player"""
def __init__(self, root, datamodel, config):
"""Initiate the program
Arguments
root -- the Tk instance of the application
datamodel -- the data model instance to show
config -- ConfigParser containing application settings
"""
self._datamodel = datamodel
#Create string variables
self._lblTitle = StringVar()
self._lblAuthor = StringVar()
self._activeInPreview = []
self._activeInPlay = []
self._datacollector = DataCollector()
self._userstop = False
#Initiate GUI and data
self._setupDisplay(root, config)
self._initData(config)
#Create transparent buttons
self._setupCategories(config)
self._setupControls(root, config)
#Create player
self._playerFrame = PlayerFrame(root, self._settings.playersize, self._playbackStopped)
self._playerFrame.setClbClicked(self._clbClicked)
self._playerwnd = self._canvas.create_window(self._settings.playerpos[0],
self._settings.playerpos[1],
window = self._playerFrame,
anchor = NW)
#Create preview
self._previewFrame = PreviewFrame(root, self._datacollector, self._canvas, self._settings)
self._previewFrame.setClbActivate(self._clbClicked)
self._previewFrame.previewsubset(self._currentMovies)
self._updateBrowseButtons()
self._setPreviewMode()
self._dml = DataModelLoader(root, self._datamodel)
#after_idle does not work here; 100 ms is an arbitrarily chosen delay time
root.after(100, self._dml.load)
def _setupDisplay(self, root, config):
"""Set screen size and add background image
root -- the Tk instance of the application
config -- ConfigParser containing application settings
"""
fullscreen = config.getboolean(ini.general, ini.fullscreen)
bgimage = ini.getPath(config.get(ini.general, ini.bgimage))
image = Image.open(bgimage)
backgroundIm = ImageTk.PhotoImage(image)
if(fullscreen):
screenwidth = root.winfo_screenwidth()
screenheight = root.winfo_screenheight()
(w, h) = image.size
self._scalefactor = (screenwidth / float(w), screenheight / float(h))
image = image.resize((screenwidth, screenheight))
else:
(screenwidth, screenheight) = image.size
self._scalefactor = (1, 1)
geom = "{}x{}+{}+{}".format(screenwidth, screenheight, 0, 0)
root.geometry(geom)
root.overrideredirect(1)
background = Canvas(root, width = screenwidth, height = screenheight)
self._canvas = background
background.pack()
backgroundIm = ImageTk.PhotoImage(image)
self._backgroundIm = backgroundIm
background.create_image(0,0, image = backgroundIm, anchor = NW)
def _setupCategories(self, config):
"""Add buttons to browse between categories
config -- ConfigParser containing application settings
"""
#Get all button data
lines = []
ctr = 1
option = ini.image + str(ctr)
while(config.has_option(ini.year, option)):
line = config.get(ini.year, option)
lines.append(line)
ctr += 1
option = ini.image + str(ctr)
#Create as many buttons as needed
buttondata = zip(lines, self._years)
if(len(buttondata) < len(self._years)):
print('Warning! There are more categories than category buttons - some categories will not be shown')
ctr = 0
for (line, year) in buttondata:
tb = TransparentButton(self._canvas, self._settings.generalfont, line, self._scalefactor)
tb.setText(year)
tb.setCommand(self._ehYear)
tb.index = ctr
ctr += 1
self._activeInPreview.append(tb)
def _setupControls(self, root, config):
"""Initiate control buttons
root -- the Tk instance of the application
config -- ConfigParser containing application settings
"""
iniline = config.get(ini.controls, ini.prev)
tb = TransparentButton(self._canvas, self._settings.generalfont, iniline, self._scalefactor)
tb.setCommand(self._ehPrev)
self._btnPrev = tb
self._activeInPreview.append(tb)
iniline = config.get(ini.controls, ini.next)
tb = TransparentButton(self._canvas, self._settings.generalfont, iniline, self._scalefactor)
tb.setCommand(self._ehNext)
self._btnNext = tb
self._activeInPreview.append(tb)
iniline = config.get(ini.controls, ini.start)
tb = TransparentButton(self._canvas, self._settings.generalfont, iniline, self._scalefactor)
self._btnPlay = tb
def _initData(self, config):
"""Initiate internal variables
config -- ConfigParser containing application settings
"""
#Check that there are movies
if(self._datamodel.isEmpty()):
showerror(lang[lng.txtNoMoviesTitle], lang[lng.txtNoMovies])
raise Exception(lang[lng.txtNoMoviesTitle])
#Initiate variables
self._settings = HylteSettings(config, self._scalefactor)
self._subsetSize = self._settings.previewcolumns * 2
self._currentYearIx = 0
self._years = sorted(self._datamodel.allMovies.iterkeys())
self._updateYearState()
def _updateBrowseButtons(self):
"""Set the text of the browse buttons to indicate current subsets"""
prev = self._currentSubsetIndex
next = self._currentSubsetIndex + 2
if(prev == 0):
prev = self._currentSubsetCount
if(next > self._currentSubsetCount):
next = 1
text = lang[lng.txtPage] + " {}/{}".format(prev, self._currentSubsetCount)
self._btnPrev.setText(text)
text = lang[lng.txtPage] + " {}/{}".format(next, self._currentSubsetCount)
self._btnNext.setText(text)
def _updateYearState(self):
"""Update internal variables that depend on the selected category"""
self._currentMovies = self._datamodel.allMovies.get(self._years[self._currentYearIx])
self._currentMovieCount = len(self._currentMovies)
self._currentSubsetIndex = 0
self._currentSubsetCount = (len(self._currentMovies) + self._subsetSize - 1) / self._subsetSize
def _setPlayMode(self):
"""Enable player, disable preview"""
for button in self._activeInPreview:
button.setEnabled(False)
self._btnPlay.setText(lang[lng.txtStop])
self._btnPlay.setCommand(self._ehStop)
self._canvas.itemconfigure(self._playerwnd, state = NORMAL)
self._previewFrame.setVisible(False)
self._mode = _play
def _setPreviewMode(self):
"""Enable preview, disable player"""
for button in self._activeInPreview:
button.setEnabled(True)
self._btnPlay.setText(lang[lng.txtPlay])
self._btnPlay.setCommand(self._ehPlay)
self._canvas.itemconfigure(self._playerwnd, state = HIDDEN)
self._previewFrame.setVisible(True)
self._mode = _preview
def _setSubset(self):
"""Display a subset of the available media items"""
self._updateBrowseButtons()
start = self._subsetSize * self._currentSubsetIndex
stop = min(start + self._subsetSize + 1, self._currentMovieCount)
self._previewFrame.previewsubset(self._currentMovies[start:stop])
def _play(self):
"""Start playback"""
self._setPlayMode()
self._datacollector.addStatisticLine(lng.txtPlaybackStarted)
try:
self._playerFrame.play(self._previewFrame.selecteditem)
except:
self._playbackStopped()
def _stop(self):
"""Stop playback"""
#Update time to ensure that the session doesn't end because of the movie length
self._datacollector.reset()
self._datacollector.addStatisticLine(lng.txtUserStoppedPlayback)
self._userstop = True
self._playerFrame.stop()
#Callbacks
def _playbackStopped(self):
"""Handle user data collection and set preview mode"""
if(not self._userstop):
self._datacollector.reset()
self._datacollector.addStatisticLine(lng.txtPlaybackFinished)
self._userstop = False
self._setPreviewMode()
def _clbClicked(self):
"""Switch between playback and preview depending on mode"""
if(self._mode == _preview):
self._play()
elif(self._mode == _play):
self._stop()
#Event handlers
def _ehPlay(self, event, tb):
"""Handle play event
Arguments
event -- event object
tb -- transparent button instance
"""
self._play()
def _ehStop(self, event, tb):
"""Handle stop event
Arguments
event -- event object
tb -- transparent button instance
"""
self._stop()
def _ehYear(self, event, tb):
"""Handle an event from one of the category buttons
Arguments
event -- event object
tb -- transparent button instance
"""
self._datacollector.detect()
self._currentYearIx = tb.index
self._updateYearState()
self._updateBrowseButtons()
self._previewFrame.previewsubset(self._currentMovies[:self._subsetSize])
def _ehPrev(self, event, tb):
"""Handle an event from the browse backward button
Arguments
event -- event object
tb -- transparent button instance
"""
self._datacollector.detect()
if(self._currentSubsetIndex > 0):
self._currentSubsetIndex -= 1
else:
self._currentSubsetIndex = self._currentSubsetCount - 1
self._setSubset()
def _ehNext(self, event, tb):
"""Handle an event from the browse forward button
Arguments
event -- event object
tb -- transparent button instance
"""
self._datacollector.detect()
if(self._currentSubsetIndex < (self._currentSubsetCount - 1)):
self._currentSubsetIndex += 1
else:
self._currentSubsetIndex = 0
self._setSubset()