1 from PyQt4.QtCore import *
2 from PyQt4.QtGui import *
3 from PyQt4.QtSql import *
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)
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)
30 pic_label.setAlignment(Qt.AlignCenter)
31 pic_label.setPixmap(photo)
32 self.innerlayout.addWidget(pic_label, row, 1)
34 self.scrollcontents.resize(700, (self.innerlayout.rowCount()+1) * 300)
37 class MergePhotos(QWidget):
38 def __init__(self, parent=None, db=None):
39 QWidget.__init__(self, parent)
41 self.layout = QGridLayout()
42 self.label = QLabel("<h2>Importing Photos</h2>")
43 self.label.setAlignment(Qt.AlignCenter)
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)
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():
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")
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
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)
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)):
89 if progress.wasCanceled():
90 progress.setValue(len(merging_people))
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))
98 if not preview.exec_():
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)):
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
109 image.load(dir.absoluteFilePath(filelist[i]))
112 buffer.open(QIODevice.WriteOnly)
113 image.save(buffer, "JPG")
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])
120 print "Auto-imported photo for", merging_people[i][2]
121 progress.setValue(len(merging_people))
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():
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")
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)
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)):
153 if progress.wasCanceled():
154 progress.setValue(len(people))
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))
162 if not preview.exec_():
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)):
170 progress.setLabelText(QString("Importing photo for %1...").arg(people[i][1] ))
171 if progress.wasCanceled():
172 progress.setValue(len(people))
175 image.load(dir.absoluteFilePath(photofiles[i]))
178 buffer.open(QIODevice.WriteOnly)
179 image.save(buffer, "JPG")
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])
186 print "Auto-imported photo for", people[i][1]
187 progress.setValue(len(people))
190 print "incorrect number of photos, can't merge"
193 """Indicate to parent widget to switch to main menu."""
194 self.emit(SIGNAL("done()") )