Skip to content
Snippets Groups Projects
distribution_overview_controller.py 8.91 KiB
Newer Older
from timeit import default_timer as timer
Jakub Štercl's avatar
Jakub Štercl committed

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
Jakub Štercl's avatar
Jakub Štercl committed
from utils.widgets.drag_drop_table_unassigned import DragDropTableUnassigned
from utils.widgets.editable_title_widget import EditableTitle
from utils.widgets.members_drag_drop_table import MembersDragDropTable

from controllers.base_controller import BaseController
from model.distribution import Distribution
from model.member import Member
from model.team import Team
Jakub Štercl's avatar
Jakub Štercl committed
from utils.widgets.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)