-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain_gui.py
executable file
·353 lines (274 loc) · 11.8 KB
/
main_gui.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
#!/usr/bin/env python
"""
- The error message should show the traceback if there is one, not just
a generic message.
= New Problem: Catching an exception from a thread in the main thread.
Found some ressources. look at them later.
- then figure out why special chars
like ( or ) are not working in adding files to the list...
=> This is a problem with pyPDF
"""
import sys
import os
import threading
from gi.repository import Gtk, GObject
import pdf_operations as pdf_ops
HOMEDIR = os.path.dirname(os.path.abspath(__file__))
class PyDF_Chain:
def __init__(self):
self.running = False # important variable for the progressbar & pulse
try:
builder = Gtk.Builder()
builder.add_from_file(os.path.join(HOMEDIR, "main_gui.glade"))#"main_gui.glade")
except:
self.error_message("Failed to load UI XML file: main_gui.glade")
sys.exit(1)
self.window = builder.get_object("main_window")
# Merge PDF Tab
self.merge_model = builder.get_object("merge_pdf_liststore")
self.merge_view = builder.get_object("merge_pdf_treeview")
# columns
self.source_col = self.merge_view.get_column(0)
self.pw_col = self.merge_view.get_column(1)
self.numPage_col = self.merge_view.get_column(2)
# Password related Gtk.Text entries
self.owner_pw = builder.get_object("owner_pw_entry")
self.owner_pw.set_visibility(False)
self.user_pw = builder.get_object("user_pw_entry")
self.user_pw.set_visibility(False)
# Encryption radio buttons
self.radio_none = builder.get_object("encrypt_radio_none")
#self.radio_none.set_label("None")
self.radio_128 = builder.get_object("encrypt_radio_128")
#self.radio_128.set_label("128")
self.radio_40 = builder.get_object("encrypt_radio_40")
#self.radio_40.set_label("40")
self.radio_group = self.radio_128.get_group()
self.radio_none.set_active(True)
# Progress
self.progressbar = builder.get_object("progressbar")
# connect the signals
builder.connect_signals(self)
def on_timeout(self, user_data):
"""
Update value on the progress bar
"""
# As this is a timeout function, return True so that it
# continues to get called
return True
def on_main_window_delete_event(self, *args):
Gtk.main_quit()
def refresh_treeview(self, pdfs):
# clear the treeview - CRUCIAL - Otherwise duplication in a row
self.source_col.clear()
self.pw_col.clear()
self.numPage_col.clear()
# Renderer
source_render = Gtk.CellRendererText()
pw_render = Gtk.CellRendererText()
pw_render.set_property("editable", True)
pw_render.connect("edited", self.update_pw_entry)
numPage_render = Gtk.CellRendererText()
# I have no idea how that works
# see harishankar.org/blog/entry.php/python-gtk-howto-cell-rendering-a-treeview-created-in-glade
self.source_col.pack_start(source_render, False)
self.pw_col.pack_start(pw_render, False)
self.numPage_col.pack_start(numPage_render, False)
self.source_col.add_attribute(source_render, "text", 0)
self.pw_col.add_attribute(pw_render, "text", 1)
self.numPage_col.add_attribute(numPage_render, "text", 2)
def update_pw_entry(self, widget, path, new_pw):
"""
Updates the password entry
"""
self.merge_model[path][1] = new_pw
def get_selected_iters(self):
"""
Get selected items and return respective iters
"""
# see http://harishankar.org/blog/entry.php/python-gtk-howto-deleting-multiple-selected-items-from-a-gtk-treeview
# get the selected rows as paths
sel_model, sel_rows = self.merge_view.get_selection().get_selected_rows()
# store the treeiters from paths
iters = []
for row in sel_rows:
iters.append(self.merge_model.get_iter(row))
return iters
def get_active_radio(self, radio_group):
"""
Returns the active radio_button's label.
"""
for radio in radio_group:
if radio.get_active():
return radio.get_label()
def get_encryption_details(self):
"""
Checks if the user wants an encryption level.
Returns parameters for either case.
Factors:
- Radio button not None.
- Owner or user pw != ''
"""
o_pw = self.owner_pw.get_text()
u_pw = self.user_pw.get_text()
radio_label = self.get_active_radio(self.radio_group)
if (o_pw != '' or u_pw != '') and radio_label != 'None':
# from merge_pdf params
# encryp=False, user_pw="", owner_pw=None, lvl=128
if o_pw == '':
return True, u_pw, None, int(radio_label)
if u_pw == '':
return True, o_pw, None, int(radio_label)
else:
return True, u_pw, o_pw, int(radio_label)
else:
return True, '', None, 128
def on_addbutton_clicked(self, button):
"""
Add one or many pdf files to the treeview.
"""
dialog = Gtk.FileChooserDialog("Please choose a file", self.window, #builder.get_object("main_window"),
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
self.add_pdf_filters(dialog)
dialog.set_select_multiple(True)
response = dialog.run()
if response == Gtk.ResponseType.OK:
pdfs = dialog.get_filenames()
for pdf in pdfs:
# add to the list store
info = pdf_ops.get_pdfinfo(pdf)
if info != None:
self.merge_model.append([info["filepath"],"",
info["numPages"]])
else:
self.merge_model.append([pdf,"", 0])
self.refresh_treeview(pdfs)
dialog.destroy()
def add_pdf_filters(self, dialog):
"""
Apply filter to only allow pdf files to be chosen.
"""
filter_pdf = Gtk.FileFilter()
filter_pdf.set_name("PDF files")
filter_pdf.add_mime_type("application/pdf")
dialog.add_filter(filter_pdf)
def on_removebutton_clicked(self, button):
"""
Remove one or many pdf files to the treeview.
"""
iters = self.get_selected_iters()
# remove the rows (treeiters)
for i in iters:
if i is not None:
self.merge_model.remove(i)
def on_copybutton_clicked(self, button):
"""
Creates a copy right behind each selection.
"""
iters = self.get_selected_iters()
for i in iters:
if i is not None:
# Need to supply list not Gtk.Treeiter
self.merge_model.append(self.merge_model[i][:])
def on_upbutton_clicked(self, button):
"""
Moves each selection one position up.
"""
iters = self.get_selected_iters()
# Make sure that not none or the first element is selected
if iters != []:
for i in iters:
prev_iter = self.merge_model.iter_previous(i)
if i is not None and prev_iter is not None:
self.merge_model.swap(i, prev_iter)
def on_downbutton_clicked(self, button):
"""
Moves each selection one position down.
"""
iters = self.get_selected_iters()
# Make sure that something is selected
if iters != []:
for i in iters:
next_iter = self.merge_model.iter_next(i)
if i is not None and next_iter is not None:
self.merge_model.swap(i, next_iter)
def on_savebutton_clicked(self, button):
"""
Saves the merged pdf file.
"""
if len(self.merge_model) == 0:
dialog = Gtk.MessageDialog(self.window, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK, "Please add pdf files to your list")
dialog.format_secondary_text(
"There are no valid pdf files to be merged.")
response = dialog.run()
dialog.destroy()
else:
dialog = Gtk.FileChooserDialog("Please choose a file", self.window, #builder.get_object("main_window"),
Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
self.add_pdf_filters(dialog)
response = dialog.run()
if response == Gtk.ResponseType.OK:
# Check if file exists and if .pdf suffix
if os.path.exists(dialog.get_filename()):
filename = os.path.split(dialog.get_filename())[-1]
dlg = Gtk.MessageDialog(self.window, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK_CANCEL, ('The file "' + filename + '" already exists!'))
dlg.format_secondary_text("Are you sure you want to overwrite?")
yes_no = dlg.run()
if yes_no == Gtk.ResponseType.CANCEL:
yes_no.destroy()
dlg.destroy()
return
dlg.destroy()
try:
self.t = threading.Thread(target=self.merge_pdfs,
args=(dialog.get_filename(),)).start()
GObject.timeout_add(100, self.pulse)
except pdf_ops.PasswordError:
print "Huzzah"
self.raise_error_dlg("Invalid Password!")
except Exception, err:
print "Nuzzuh"
#dlg.destroy() # Sub Dialog
self.running = False
dialog.destroy() # Main Dialog
def raise_error_dlg(self, traceback):
"""
Raises an error dialog.
"""
error_dlg = Gtk.MessageDialog(self.window, 0, Gtk.MessageType.ERROR,
Gtk.ButtonsType.OK, ("An error has occurred. Process is aborted."))
error_dlg.format_secondary_text(traceback)
just_run = error_dlg.run()
error_dlg.destroy()
def merge_pdfs(self, save_path):
"""
Merges the pdfs given in self.merge_model
Utilises pdf_operations.merge_pdf function to achieve its goal.
"""
self.running = True
if not save_path.endswith(".pdf"):
save_path = save_path + ".pdf"
pdfs = []
for row in self.merge_model:
pdfs.append((row[0], row[1])) # path and pw
encryp, user_pw, owner_pw, lvl = self.get_encryption_details()
pdf_ops.merge_pdf(save_path, pdfs, encryp, user_pw, owner_pw, lvl)
self.running = False
def pulse(self):
self.progressbar.pulse()
return self.running # True = repeat, False = stop
def error_message(self, message):
raise IOError(message)
def run(self):
self.window.show_all()
Gtk.main()
if __name__ == "__main__":
GObject.threads_init()
pydf_chain = PyDF_Chain()
pydf_chain.run()