]> git.zarvox.org Git - wp3.git/blob - mergephotos.py
Added Python docstrings for all classes.
[wp3.git] / mergephotos.py
1 from PyQt4.QtCore import *
2 from PyQt4.QtGui import *
3 from PyQt4.QtSql import *
4
5 class MergePreview(QDialog):
6         def __init__(self, parent=None):
7                 QDialog.__init__(self, parent)
8                 self.layout = QGridLayout()
9                 self.okay = QPushButton("Import")
10                 self.bad = QPushButton("Cancel")
11                 self.scrollarea = QScrollArea()
12                 self.scrollcontents = QWidget()
13                 self.scrollcontents.resize(700,600)
14                 self.innerlayout = QGridLayout()
15                 self.scrollcontents.setLayout(self.innerlayout)
16                 self.layout.addWidget(self.scrollarea, 0, 0, 1, 2)
17                 self.layout.addWidget(self.okay, 1, 0 )
18                 self.layout.addWidget(self.bad, 1, 1 )
19                 self.scrollarea.setWidget(self.scrollcontents)
20                 QObject.connect( self.okay, SIGNAL("clicked()"), self.accept )
21                 QObject.connect( self.bad, SIGNAL("clicked()"), self.reject )
22                 self.setLayout(self.layout)
23                 self.resize(750,600)
24         def addToList(self, name, photo):
25                 row = self.innerlayout.rowCount()
26                 name_label = QLabel(name)
27                 name_label.setAlignment(Qt.AlignCenter)
28                 self.innerlayout.addWidget(name_label, row, 0)
29                 pic_label = QLabel()
30                 pic_label.setAlignment(Qt.AlignCenter)
31                 pic_label.setPixmap(photo)
32                 self.innerlayout.addWidget(pic_label, row, 1)
33         def doneAdding(self):
34                 self.scrollcontents.resize(700, (self.innerlayout.rowCount()+1) * 300)
35
36
37 class MergePhotos(QWidget):
38         def __init__(self, parent=None, db=None):
39                 QWidget.__init__(self, parent)
40                 self.db = db
41                 self.layout = QGridLayout()
42                 self.label = QLabel("<h2>Importing Photos</h2>")
43                 self.label.setAlignment(Qt.AlignCenter)
44
45                 self.button1 = QCommandLinkButton("Merge photos by NetID", "Add photos to records without photos by importing photos named <netid>.jpg")
46                 self.button2 = QCommandLinkButton("Merge photos by timestamp", "Add photos to records without photos by sorting records and photos by creation time and matching pairs")
47                 self.button3 = QCommandLinkButton("Cancel", "Return to main menu")
48                 QObject.connect( self.button1, SIGNAL("clicked()"), self.beginMergeNetID )
49                 QObject.connect( self.button2, SIGNAL("clicked()"), self.beginMergeTimestamp )
50                 QObject.connect( self.button3, SIGNAL("clicked()"), self.done )
51                 self.layout.addWidget(self.label,0,0,1,3)
52                 self.layout.addWidget(self.button1,1,1,1,1)
53                 self.layout.addWidget(self.button2,2,1,1,1)
54                 self.layout.addWidget(self.button3,3,1,1,1)
55                 self.setLayout(self.layout)
56
57         def beginMergeNetID(self):
58                 """Import photos from a user-specified folder by filename; namely <netid>.JPG."""
59                 foldertext = QFileDialog.getExistingDirectory( None, "Pick folder of photos", ".")
60                 if not foldertext.isEmpty():
61                         print foldertext
62                         dir = QDir(foldertext)
63                         #dir.setNameFilters(["*.jpg", "*.JPG"])
64                         q = QSqlQuery(self.db)
65                         q.exec_("SELECT id, netid, forename, surname FROM people WHERE photo IS NULL")
66                         rec = q.record()
67                         col_id = rec.indexOf("id")
68                         col_netid = rec.indexOf("netid")
69                         col_forename = rec.indexOf("forename")
70                         col_surname = rec.indexOf("surname")
71                         people = [] # Tuples of (id, netid, name)
72                         while q.next(): # Fetch results
73                                 people.append( (q.value(col_id).toString(), q.value(col_netid).toString(), q.value(col_forename).toString() + QString(" ") + q.value(col_surname).toString() ) )
74                         merging_people = [] # records that have matching photos
75                         filelist = [] # the corresponding photos
76                         for person in people:
77                                 fileName = person[1] + QString(".jpg")
78                                 if not dir.exists(fileName):
79                                         fileName = person[1] + QString(".JPG")
80                                 if dir.exists(fileName):
81                                         merging_people.append(person)
82                                         filelist.append(fileName)
83                         # Generate preview
84                         preview = MergePreview(self)
85                         progress = QProgressDialog("Preparing preview...", "Cancel", 0, len(merging_people))
86                         progress.setWindowModality(Qt.WindowModal)
87                         for i in xrange(len(merging_people)):
88                                 progress.setValue(i)
89                                 if progress.wasCanceled():
90                                         progress.setValue(len(merging_people))
91                                         return
92                                 image = QImage()
93                                 image.load(dir.absoluteFilePath(filelist[i]))
94                                 preview.addToList( merging_people[i][2] + QString("\n") + merging_people[i][1], QPixmap.fromImage(image.scaled(QSize(400,300), Qt.KeepAspectRatio)) )
95                         progress.setValue(len(merging_people))
96                         preview.doneAdding()
97
98                         if not preview.exec_():
99                                 return
100
101                         # If preview accepted, proceed with the import
102                         progress = QProgressDialog("Importing photos...", "Can't cancel!", 0, len(merging_people))
103                         progress.setWindowModality(Qt.WindowModal)
104                         for i in xrange(len(merging_people)):
105                                 progress.setValue(i)
106                                 progress.setLabelText(QString("Importing photo for %1...").arg(merging_people[i][2]) )
107                                 # if netid.jpg exists, load it, write it to a QByteArray, bind it, and update that row
108                                 image = QImage()
109                                 image.load(dir.absoluteFilePath(filelist[i]))
110                                 ba = QByteArray()
111                                 buffer = QBuffer(ba)
112                                 buffer.open(QIODevice.WriteOnly)
113                                 image.save(buffer, "JPG")
114                                 buffer.close()
115                                 update_q = QSqlQuery(self.db)
116                                 update_q.prepare("UPDATE people SET photo=:photo WHERE id=:id")
117                                 update_q.bindValue(":photo", QVariant(ba))
118                                 update_q.bindValue(":id", merging_people[i][0])
119                                 update_q.exec_()
120                                 print "Auto-imported photo for", merging_people[i][2]
121                         progress.setValue(len(merging_people))
122                         self.done()
123
124         def beginMergeTimestamp(self):
125                 """Import photos from a user-specified folder by timestamp.  This relies on the folder containing the same number of pictures as records in the database missing photos, as well as the photos and database records being created in the same order."""
126                 foldertext = QFileDialog.getExistingDirectory( None, "Pick folder of photos", ".")
127                 if not foldertext.isEmpty():
128                         print foldertext
129                         dir = QDir(foldertext)
130                         dir.setNameFilters(["*.jpg", "*.JPG"]) # Only import jpegs (for now)
131                         dir.setFilter(QDir.Files | QDir.Readable) # Don't list subdirectories
132                         dir.setSorting(QDir.Time) # Sort by mtime (should probably make this sort by ctime, or better, EXIF ctime, but in the general case this should work)
133                         q = QSqlQuery(self.db)
134                         q.exec_("SELECT id, forename, surname FROM people WHERE photo IS NULL ORDER BY createtime")
135                         rec = q.record()
136                         col_id = rec.indexOf("id")
137                         col_forename = rec.indexOf("forename")
138                         col_surname = rec.indexOf("surname")
139                         people = [] # List of tuples (id, name)
140                         while q.next():
141                                 people.append( (q.value(col_id).toString(), q.value(col_forename).toString() + QString(" ") + q.value(col_surname).toString() ) )
142                         photofiles = dir.entryList()
143                         print len(people), "people without photos"
144                         print len(photofiles), "photo files provided"
145                         if len(people) == len(photofiles):
146                                 print "merging by timestamp possible, generating preview"
147                                 preview = MergePreview(self)
148                                 # do some progress bar magic
149                                 progress = QProgressDialog("Preparing preview...", "Cancel", 0, len(people) )
150                                 progress.setWindowModality(Qt.WindowModal)
151                                 for i in xrange(len(people)):
152                                         progress.setValue(i)
153                                         if progress.wasCanceled():
154                                                 progress.setValue(len(people))
155                                                 return
156                                         image = QImage()
157                                         image.load(dir.absoluteFilePath(photofiles[i]))
158                                         preview.addToList(people[i][1], QPixmap.fromImage(image.scaled(QSize(400,300), Qt.KeepAspectRatio)) )
159                                 progress.setValue(len(people))
160                                 preview.doneAdding()
161
162                                 if not preview.exec_():
163                                         return
164
165                                 # If preview accepted, proceed with the import
166                                 progress = QProgressDialog("Importing photos...", "Abort", 0, len(people))
167                                 progress.setWindowModality(Qt.WindowModal)
168                                 for i in xrange(len(people)):
169                                         progress.setValue(i)
170                                         progress.setLabelText(QString("Importing photo for %1...").arg(people[i][1] ))
171                                         if progress.wasCanceled():
172                                                 progress.setValue(len(people))
173                                                 return
174                                         image = QImage()
175                                         image.load(dir.absoluteFilePath(photofiles[i]))
176                                         ba = QByteArray()
177                                         buffer = QBuffer(ba)
178                                         buffer.open(QIODevice.WriteOnly)
179                                         image.save(buffer, "JPG")
180                                         buffer.close()
181                                         update_q = QSqlQuery(self.db)
182                                         update_q.prepare("UPDATE people SET photo=:photo WHERE id=:id")
183                                         update_q.bindValue(":photo", QVariant(ba))
184                                         update_q.bindValue(":id", people[i][0])
185                                         update_q.exec_()
186                                         print "Auto-imported photo for", people[i][1]
187                                 progress.setValue(len(people))
188                                 self.done()
189                         else:
190                                 print "incorrect number of photos, can't merge"
191
192         def done(self):
193                 """Indicate to parent widget to switch to main menu."""
194                 self.emit(SIGNAL("done()") )
195