Skip to content
Snippets Groups Projects
distribution_overview_controller.py 8.88 KiB
Newer Older
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
Jakub Štercl's avatar
Jakub Štercl committed
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)
            -1,
            self.getUnassigned(teams),
            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)

Jakub Štercl's avatar
Jakub Štercl committed
        self.distribution_name = self.tr("Nepojmenované rozdělení")
Jakub Štercl's avatar
Jakub Štercl committed
        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)
Jakub Štercl's avatar
Jakub Štercl committed
        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

Jakub Štercl's avatar
Jakub Štercl committed
    @pyqtSlot(str)
    def changeDistName(self, name):
        """
        pyqt slot that changes name of distribution
        :param name: new distribution name
        """
Jakub Štercl's avatar
Jakub Štercl committed
        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.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)
Jakub Štercl's avatar
Jakub Štercl committed
        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):
        dialog = QPrintDialog()
        if dialog.exec_() != QDialog.Accepted:
            return
Jakub Štercl's avatar
Jakub Štercl committed
        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()

        """
        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(table.name + ', ')
    @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)