from timeit import default_timer as timer from PyQt5.QtCore import pyqtSlot, QPoint from PyQt5.QtGui import QPainter, QPicture from PyQt5.QtPrintSupport import QPrintDialog from PyQt5.QtWidgets import QDialog, QMenu, QFileDialog, QWidget, QMessageBox from controllers.base_controller import BaseController from model.distribution import Distribution from model.member import Member from model.team import Team from utils.drag_drop_table_unassigned import DragDropTableUnassigned from utils.editable_title_widget import EditableTitle from utils.members_drag_drop_table import MembersDragDropTable from utils.print_widget import PrintWidget from windows import distribution_overview class DistributionOverview(BaseController, QWidget, distribution_overview.Ui_Form): def __init__(self, main_window, teams, group, parent=None): """ :param main_window: MainWindow that this belongs to :param teams: dictionary of teams {"team name": [Member1, Member2, ...], ...} :param group: Group that has been distributed in those teams :param parent: parent widget for qt """ super(DistributionOverview, self).__init__(parent) self.setupUi(self) self.main_window = main_window self.group = group self.teamTables = [] for i, team_name in enumerate(teams): table = MembersDragDropTable(i, teams[team_name], team_name, self) table.model.sourceModel().droppedMemberFrom.connect(self.removeMember) table.deleteClicked.connect(self.removeTeam) table.contextMenuRequested.connect(self.onContextMenuRequested) self.teamTables.append(table) self.gridTeams.addWidget(table, i // 2, i % 2) self.tableOthers = DragDropTableUnassigned( -1, self.getUnassigned(teams), self.tr("Nezařazeni"), self ) self.tableOthers.model.sourceModel().droppedMemberFrom.connect(self.removeMember) self.tableOthers.contextMenuRequested.connect(self.onContextMenuRequested) self.gridTeams.addWidget(self.tableOthers, len(self.teamTables) // 2, len(self.teamTables) % 2) self.distribution_name = self.tr("Nepojmenované rozdělení") title = EditableTitle(self.distribution_name) self.layoutName.replaceWidget(self.titleWidget, title) self.titleWidget = title # signals self.btnAddTeam.clicked.connect(self.addTeam) self.btnSave.clicked.connect(self.save) self.btnPrint.clicked.connect(self.printDistribution) self.btnExport.clicked.connect(self.exportToFile) self.titleWidget.textConfirmed.connect(self.changeDistName) def getUnassigned(self, teams): """ :param teams: teams in distribution (dict) :return: list of unassigned members from group """ res = [] for member in self.group.members: for team in teams.values(): if member in team: break else: res.append(member) return res @pyqtSlot(str) def changeDistName(self, name): """ pyqt slot that changes name of distribution :param name: new distribution name """ self.distribution_name = name @pyqtSlot() def addTeam(self): """ add new empty team to the table of teams """ table_id = len(self.teamTables) table = MembersDragDropTable(table_id, [], "Tým " + str(table_id + 1), self) table.model.sourceModel().droppedMemberFrom.connect(self.removeMember) table.deleteClicked.connect(self.removeTeam) self.teamTables.append(table) self.gridTeams.replaceWidget(self.tableOthers, table) self.gridTeams.addWidget(self.tableOthers, len(self.teamTables) // 2, len(self.teamTables) % 2 ) @pyqtSlot(int, int) def removeMember(self, member_id, table_id): """ remove member from target table (table represents team) :param member_id: id of the member that should be removed :param table_id: id of table that we should remove the member from (or -1 for 'unassigned' """ if table_id == -1: self.tableOthers.removeMember(member_id) else: self.teamTables[table_id].removeMember(member_id) @pyqtSlot(int) def removeTeam(self, table_id): """ remove table (represents team) from the screen, only removes it if the table is empty :param table_id: id of table that should be removed """ if self.teamTables[table_id].isEmpty(): self.teamTables[table_id].hide() self.gridTeams.replaceWidget(self.teamTables[len(self.teamTables) - 1],self.tableOthers) for i in range(len(self.teamTables) - 1, table_id, -1): self.gridTeams.replaceWidget(self.teamTables[i-1], self.teamTables[i]) self.teamTables[i].id_ -= 1 self.teamTables.pop(table_id) else: msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setWindowTitle("Chyba") msg.setText("Nelze smazat neprázdný tým!") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() @pyqtSlot() def save(self): """ create Distribution instance and save it to database """ teams = [] for table in self.teamTables: team = Team(table.id_, table.name, table.getAllData()) teams.append(team) distribution = Distribution(self.distribution_name, teams, self.group.id_) start = timer() distribution.saveToDb() print('total time:', timer() - start) self.main_window.reset() @pyqtSlot() def printDistribution(self): """ show print dialog, then print the distribution """ dialog = QPrintDialog() if dialog.exec_() != QDialog.Accepted: return widget = PrintWidget(self.distribution_name, self.teamTables) # first we print into QPicture, that way we can rescale without a big resolution loss pic = QPicture() print(pic.size()) pic_painter = QPainter(pic) widget.render(pic_painter, flags=widget.DrawChildren) pic_painter.end() pic.setBoundingRect(widget.rect()) printer = dialog.printer() painter = QPainter() painter.begin(printer) # rescale the picture to page and print x_scale = printer.pageRect().width() / pic.width() y_scale = printer.pageRect().height() / pic.height() scale = min([x_scale, y_scale]) painter.translate(printer.paperRect().x() + printer.pageRect().width() / 2, printer.paperRect().y() + printer.pageRect().height() / 2) painter.scale(scale, scale) painter.translate(-pic.width() / 2, -pic.height() / 2) painter.drawPicture(0, 0, pic) painter.end() @pyqtSlot() def exportToFile(self): """ show file choose dialog, then export distribution to file """ file, options = QFileDialog.getSaveFileName(self, self.tr("Exportovat do")) if file == '': return with open(file, 'w', encoding='utf-8') as f: f.write(self.distribution_name + '\n') for table in self.teamTables: team = table.getAllData() f.write(table.name + ': ') for member in team: f.write(member.name + ', ') f.write('\n') @pyqtSlot(Member, int, QPoint) def onContextMenuRequested(self, member, from_table_id, point): """ construct and display context menu allowing to move members from a team to different one :param member: member that the user is pointing at :param from_table_id: id of table that the member is in :param point: point on screen, so that we show the menu where the user right clicked """ menu = QMenu(self.sender()) submenu = menu.addMenu(self.tr("Přesunout do týmu:")) for table in self.teamTables: if table.id_ != from_table_id: submenu.addAction(table.name, lambda i=table.id_: self.moveMember(member, from_table_id, i)) if from_table_id != -1: menu.addSeparator() menu.addAction(self.tr("Vyřadit z rozdělení"), lambda: self.moveMember(member, from_table_id, -1)) menu.exec_(point) def moveMember(self, member, from_table_id, to_table_id): """ move member from one table (team) to another :param member: member that we're moving :param from_table_id: id of table in which the member currently is :param to_table_id: id of target table """ self.removeMember(member.id_, from_table_id) if to_table_id == -1: self.tableOthers.insertMember(member) else: self.teamTables[to_table_id].insertMember(member)