def __init__(self, dataPath, version) : QtGui.QDialog.__init__(self) self.setWindowTitle(self.tr("enVote %1 - Votation simulator").arg(version)) self._dataPath = dataPath files = sorted(glob.glob(self.dataPath("*"))) if not files : QtGui.QMessageBox.critical(self, self.tr("Error loading data"), self.tr("The application is unable to find out the data set")) raise RuntimeError("The application is unable to find the data set") self.cases = sorted([ (os.path.splitext(os.path.basename(name))[0], Simulador.Resultats(file(name))) for name in files ]) self.currentCase = 0 self.case = None colors = dict( abstencion="darkgrey", blancos="white", nulos="#F44", CiU="blue", CIU="blue", PP="cyan", ERC="orange", ESQUERRA="orange", ** { "PSOE" : "red", "PSC" : "red", "PSC-PSOE" : "red", "ICV-EUiA" : "green", "IC-V" : "green", "IC" : "green", "IC-EV" : "green", "IU" : "green", "PSUC" : "green", "PSUC-PCE" : "green", }) layout = QtGui.QVBoxLayout() self.setLayout(layout) chartLayout = QtGui.QGridLayout() layout.addLayout(chartLayout) self.votesChart = PieChart() self.votesChart.setToolTip(self.tr("Whole census")) chartLayout.addWidget(self.votesChart,0,0) self.votesChart.setSectorColors(colors) self.candidaturesChart = PieChart() self.candidaturesChart.setToolTip(self.tr("Just votes to candidatures")) chartLayout.addWidget(self.candidaturesChart, 0,1) self.candidaturesChart.setSectorColors(colors) self.sconsChart = PieChart() self.sconsChart.setToolTip(self.tr("Hondt distribution (legal)")) chartLayout.addWidget(self.sconsChart,0,2) self.sconsChart.setSectorColors(colors) self.proportionalChart = PieChart() self.proportionalChart.setToolTip(self.tr("Hamilton distribution (proportional)")) chartLayout.addWidget(self.proportionalChart, 0,3) self.proportionalChart.setSectorColors(colors) titleBox = QtGui.QHBoxLayout() layout.addLayout(titleBox) self.title = QtGui.QComboBox() titleBox.addWidget(self.title) self.title.setToolTip(self.tr("Current case being displayed")) self.title.addItems([name for name, case in self.cases]) self.title.currentIndexChanged.connect(self.titleChanged) titleBox.addWidget(QtGui.QWidget()) # spacer prevButton = QtGui.QPushButton(self.tr("&Previous")) prevButton.setToolTip(self.tr("Switch to the previous case")) titleBox.addWidget(prevButton) nextButton = QtGui.QPushButton(self.tr("&Next")) nextButton.setToolTip(self.tr("Switch to the next case")) titleBox.addWidget(nextButton) buttonBox = QtGui.QHBoxLayout() layout.addLayout(buttonBox) self._from = QtGui.QComboBox() self._from.setToolTip(self.tr("Vote transfer origin")) self._to = QtGui.QComboBox() self._to.setToolTip(self.tr("Vote transfer destination")) transfer = QtGui.QPushButton(self.tr("&Transfer")) transfer.setToolTip(self.tr("Transfer votes")) invert = QtGui.QPushButton(self.tr("&Invert")) invert.setToolTip(self.tr("Swap transfer sides")) self._tranferSize = QtGui.QSpinBox() self._tranferSize.setToolTip(self.tr("Number of votes to be transferred")) self._tranferSize.setMinimum(1) self._tranferSize.setMaximum(1000000) self._tranferSize.setValue(10000) self._tranferSize.setSuffix(self.tr(" votes")) arrow = QtGui.QLabel("->") arrow.setSizePolicy( QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed, ) buttonBox.addWidget(self._from) buttonBox.addWidget(arrow) buttonBox.addWidget(self._to) buttonBox.addWidget(self._tranferSize) buttonBox.addWidget(transfer) buttonBox.addWidget(invert) buttonBox.addWidget(QtGui.QWidget()) # spacer copyButton = QtGui.QPushButton(self.tr("&Save As")) copyButton.setToolTip(self.tr("Save a copy the current case with a new name")) buttonBox.addWidget(copyButton) resetButton = QtGui.QPushButton(self.tr("&Reset")) resetButton.setToolTip(self.tr("Restore the case to the latest saved state")) buttonBox.addWidget(resetButton) saveButton = QtGui.QPushButton(self.tr("&Save")) saveButton.setToolTip(self.tr("Save the changes done to the case")) buttonBox.addWidget(saveButton) statLayout = QtGui.QHBoxLayout() layout.addLayout(statLayout) def label() : label=QtGui.QLabel() statLayout.addWidget(label) return label self.censusLabel = label() self.abstentionLabel = label() self.nullLabel = label() self.blankLabel = label() self.thresholdLabel = label() self.seatsLabel = label() self.seatsSpiner = QtGui.QSpinBox() self.seatsSpiner.setMinimum(1) self.seatsSpiner.setMaximum(99999) statLayout.addWidget(self.seatsSpiner) self.restLabelHondt = QtGui.QLabel() layout.addWidget(self.restLabelHondt) self.restLabelProportional = QtGui.QLabel() layout.addWidget(self.restLabelProportional) transfer.clicked.connect(self.transfer) invert.clicked.connect(self.invertTransfer) nextButton.clicked.connect(self.nextDatabase) prevButton.clicked.connect(self.previousDatabase) saveButton.clicked.connect(self.save) resetButton.clicked.connect(self.reset) copyButton.clicked.connect(self.copy) self.votesChart.sectorLeftClicked.connect(self.selectFrom) self.votesChart.sectorRightClicked.connect(self.selectTo) self.candidaturesChart.sectorLeftClicked.connect(self.selectFrom) self.candidaturesChart.sectorRightClicked.connect(self.selectTo) self.proportionalChart.sectorLeftClicked.connect(self.selectFrom) self.proportionalChart.sectorRightClicked.connect(self.selectTo) self.sconsChart.sectorLeftClicked.connect(self.selectFrom) self.sconsChart.sectorRightClicked.connect(self.selectTo) self.seatsSpiner.valueChanged.connect(self.changeSeats) self.hondtTable = HondtTable() layout.addWidget(self.hondtTable) self.updateDatabase()
class Envoter(QtGui.QDialog) : def __init__(self, dataPath, version) : QtGui.QDialog.__init__(self) self.setWindowTitle(self.tr("enVote %1 - Votation simulator").arg(version)) self._dataPath = dataPath files = sorted(glob.glob(self.dataPath("*"))) if not files : QtGui.QMessageBox.critical(self, self.tr("Error loading data"), self.tr("The application is unable to find out the data set")) raise RuntimeError("The application is unable to find the data set") self.cases = sorted([ (os.path.splitext(os.path.basename(name))[0], Simulador.Resultats(file(name))) for name in files ]) self.currentCase = 0 self.case = None colors = dict( abstencion="darkgrey", blancos="white", nulos="#F44", CiU="blue", CIU="blue", PP="cyan", ERC="orange", ESQUERRA="orange", ** { "PSOE" : "red", "PSC" : "red", "PSC-PSOE" : "red", "ICV-EUiA" : "green", "IC-V" : "green", "IC" : "green", "IC-EV" : "green", "IU" : "green", "PSUC" : "green", "PSUC-PCE" : "green", }) layout = QtGui.QVBoxLayout() self.setLayout(layout) chartLayout = QtGui.QGridLayout() layout.addLayout(chartLayout) self.votesChart = PieChart() self.votesChart.setToolTip(self.tr("Whole census")) chartLayout.addWidget(self.votesChart,0,0) self.votesChart.setSectorColors(colors) self.candidaturesChart = PieChart() self.candidaturesChart.setToolTip(self.tr("Just votes to candidatures")) chartLayout.addWidget(self.candidaturesChart, 0,1) self.candidaturesChart.setSectorColors(colors) self.sconsChart = PieChart() self.sconsChart.setToolTip(self.tr("Hondt distribution (legal)")) chartLayout.addWidget(self.sconsChart,0,2) self.sconsChart.setSectorColors(colors) self.proportionalChart = PieChart() self.proportionalChart.setToolTip(self.tr("Hamilton distribution (proportional)")) chartLayout.addWidget(self.proportionalChart, 0,3) self.proportionalChart.setSectorColors(colors) titleBox = QtGui.QHBoxLayout() layout.addLayout(titleBox) self.title = QtGui.QComboBox() titleBox.addWidget(self.title) self.title.setToolTip(self.tr("Current case being displayed")) self.title.addItems([name for name, case in self.cases]) self.title.currentIndexChanged.connect(self.titleChanged) titleBox.addWidget(QtGui.QWidget()) # spacer prevButton = QtGui.QPushButton(self.tr("&Previous")) prevButton.setToolTip(self.tr("Switch to the previous case")) titleBox.addWidget(prevButton) nextButton = QtGui.QPushButton(self.tr("&Next")) nextButton.setToolTip(self.tr("Switch to the next case")) titleBox.addWidget(nextButton) buttonBox = QtGui.QHBoxLayout() layout.addLayout(buttonBox) self._from = QtGui.QComboBox() self._from.setToolTip(self.tr("Vote transfer origin")) self._to = QtGui.QComboBox() self._to.setToolTip(self.tr("Vote transfer destination")) transfer = QtGui.QPushButton(self.tr("&Transfer")) transfer.setToolTip(self.tr("Transfer votes")) invert = QtGui.QPushButton(self.tr("&Invert")) invert.setToolTip(self.tr("Swap transfer sides")) self._tranferSize = QtGui.QSpinBox() self._tranferSize.setToolTip(self.tr("Number of votes to be transferred")) self._tranferSize.setMinimum(1) self._tranferSize.setMaximum(1000000) self._tranferSize.setValue(10000) self._tranferSize.setSuffix(self.tr(" votes")) arrow = QtGui.QLabel("->") arrow.setSizePolicy( QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed, ) buttonBox.addWidget(self._from) buttonBox.addWidget(arrow) buttonBox.addWidget(self._to) buttonBox.addWidget(self._tranferSize) buttonBox.addWidget(transfer) buttonBox.addWidget(invert) buttonBox.addWidget(QtGui.QWidget()) # spacer copyButton = QtGui.QPushButton(self.tr("&Save As")) copyButton.setToolTip(self.tr("Save a copy the current case with a new name")) buttonBox.addWidget(copyButton) resetButton = QtGui.QPushButton(self.tr("&Reset")) resetButton.setToolTip(self.tr("Restore the case to the latest saved state")) buttonBox.addWidget(resetButton) saveButton = QtGui.QPushButton(self.tr("&Save")) saveButton.setToolTip(self.tr("Save the changes done to the case")) buttonBox.addWidget(saveButton) statLayout = QtGui.QHBoxLayout() layout.addLayout(statLayout) def label() : label=QtGui.QLabel() statLayout.addWidget(label) return label self.censusLabel = label() self.abstentionLabel = label() self.nullLabel = label() self.blankLabel = label() self.thresholdLabel = label() self.seatsLabel = label() self.seatsSpiner = QtGui.QSpinBox() self.seatsSpiner.setMinimum(1) self.seatsSpiner.setMaximum(99999) statLayout.addWidget(self.seatsSpiner) self.restLabelHondt = QtGui.QLabel() layout.addWidget(self.restLabelHondt) self.restLabelProportional = QtGui.QLabel() layout.addWidget(self.restLabelProportional) transfer.clicked.connect(self.transfer) invert.clicked.connect(self.invertTransfer) nextButton.clicked.connect(self.nextDatabase) prevButton.clicked.connect(self.previousDatabase) saveButton.clicked.connect(self.save) resetButton.clicked.connect(self.reset) copyButton.clicked.connect(self.copy) self.votesChart.sectorLeftClicked.connect(self.selectFrom) self.votesChart.sectorRightClicked.connect(self.selectTo) self.candidaturesChart.sectorLeftClicked.connect(self.selectFrom) self.candidaturesChart.sectorRightClicked.connect(self.selectTo) self.proportionalChart.sectorLeftClicked.connect(self.selectFrom) self.proportionalChart.sectorRightClicked.connect(self.selectTo) self.sconsChart.sectorLeftClicked.connect(self.selectFrom) self.sconsChart.sectorRightClicked.connect(self.selectTo) self.seatsSpiner.valueChanged.connect(self.changeSeats) self.hondtTable = HondtTable() layout.addWidget(self.hondtTable) self.updateDatabase() def changeSeats(self, seats) : print "Value changed" if self._updatingSeatsSpinner : return self.case.representants = self.seatsSpiner.value() self.case.recomputeSeats() self.updateViews() def dataPath(self, name) : return os.path.join(self._dataPath, name+".csv") def reset(self) : name, case = self.cases[self.currentCase] dataFile = file(self.dataPath(name)) self.cases[self.currentCase] = (name, Simulador.Resultats(dataFile)) self.updateDatabase() def selectFrom(self, candidature) : index = self._from.findText(candidature) if index < 0 : return self._from.setCurrentIndex(index) def selectTo(self, candidature) : index = self._to.findText(candidature) if index < 0 : return self._to.setCurrentIndex(index) def copy(self) : name, case = self.cases[self.currentCase] newName, ok = QtGui.QInputDialog.getText(self, self.tr("Copy Data Set as"), self.tr("Give a new name for the copied case"), QtGui.QLineEdit.Normal, name+self.tr("-copy","default file suffix")) if not ok : return newName = str(newName) dataFile = file(self.dataPath(newName), "w") case.save(dataFile) dataFile = file(self.dataPath(newName), "r") self.cases.append((newName, Simulador.Resultats(dataFile))) self.currentCase=len(self.cases)-1 self.title.addItem(newName) self.title.setCurrentIndex(self.currentCase) def save(self) : name, case = self.cases[self.currentCase] dataFile = file(self.dataPath(name), "w") case.save(dataFile) def previousDatabase(self) : self.currentCase -= 1 self.currentCase %= len(self.cases) self.title.setCurrentIndex(self.currentCase) def nextDatabase(self) : self.currentCase += 1 self.currentCase %= len(self.cases) self.title.setCurrentIndex(self.currentCase) def titleChanged(self, current) : self.currentCase = current self.updateDatabase() def transfer(self) : vots = self.case.vots fromOption = str(self._from.currentText()) toOption = str(self._to.currentText()) transferSize = min(vots[fromOption], self._tranferSize.value()) vots[fromOption] -= transferSize vots[toOption] += transferSize self.case.recomputeSeats() self.updateViews() def invertTransfer(self) : fromOption = self._from.currentIndex() toOption = self._to.currentIndex() self._from.setCurrentIndex(toOption) self._to.setCurrentIndex(fromOption) def updateDatabase(self) : name, self.case = self.cases[self.currentCase] print "Changint to", name options = sorted(self.case.vots.keys()) self._to.clear() self._to.addItems(options) self._from.clear() self._from.addItems(options) self.updateViews() def updateViews(self) : print "Computing proportional" toCandidatures = dict(( (party, votes) for party, votes in self.case.vots.iteritems() if party not in [ 'abstencion', 'blancos', 'nulos', ] )) print "Computing Seats" s = Simulador.Simulador(self.case.representants, **self.case.vots) scons = self.case.scons if not sum(self.case.scons.values()) : scons = s.repartiment(self.case.representants) proportional = s.repartimentProporcional(self.case.representants) integer = s.repartimentSencer(self.case.representants) restosHondt = [ (party, scons[party] - integer[party]) for party in s.partidos() if scons[party] != integer[party] ] restosProportional = [ (party, proportional[party] - integer[party]) for party in s.partidos() if proportional[party] != integer[party] ] self.restLabelHondt.setText( self.tr("Rests Hondt: ")+ ("".join([" %s: %+i"%rest for rest in sorted(restosHondt)]))) self.restLabelProportional.setText( self.tr("Rests Hamilton: ")+ ("".join([" %s: %+i"%rest for rest in sorted(restosProportional)]))) print "Updating Pies" self.votesChart.setSectorDescriptions(self.case.descripcions) self.votesChart.setSectorValues(**self.case.vots) self.candidaturesChart.setSectorDescriptions(self.case.descripcions) self.candidaturesChart.setSectorValues(**toCandidatures) self.proportionalChart.setSectorDescriptions(self.case.descripcions) self.proportionalChart.setSectorValues(**proportional) self.sconsChart.setSectorDescriptions(self.case.descripcions) self.sconsChart.setSectorValues(**scons) print "Hondt Table" self.hondtTable.threshold = self.case.votsValids * threshold self.hondtTable.nSeats = self.case.representants self.hondtTable.feedDescriptions(self.case.descripcions) self.hondtTable.feedVotations(toCandidatures) print "Stats" census = sum(self.case.vots.values()) def displayStats(widget, text, quantity) : percent = 100.*quantity/census widget.setText("<b>%s:</b> %i (%.2f%%)"%( text, quantity, percent)) self.censusLabel.setText("<b>%s:</b> %s"%(self.tr("Census"),census)) displayStats(self.abstentionLabel, self.tr("Abstention"), self.case.vots['abstencion']) displayStats(self.nullLabel, self.tr("Nulls"), self.case.vots['nulos']) displayStats(self.blankLabel, self.tr("Blanks"), self.case.vots['blancos']) displayStats(self.thresholdLabel, self.tr("Threshold (%0%)").arg(threshold*100), threshold*(census-self.case.vots['abstencion']-self.case.vots['nulos'])) self.seatsLabel.setText(self.tr("<b>Seats:</b>")) self.seatsLabel.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter) print "Updating seat spinner" self._updatingSeatsSpinner = True self.seatsSpiner.setValue(self.case.representants) self._updatingSeatsSpinner = False print "end"