Ejemplo n.º 1
0
    def __init__(self, *args, **kwargs):
        super(AboutDialog, self).__init__(*args, **kwargs)

        QBtn = QDialogButtonBox.Ok  # No cancel
        self.buttonBox = QDialogButtonBox(QBtn)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        layout = QVBoxLayout()

        title = QLabel("MooseAche")
        font = title.font()
        font.setPointSize(20)
        title.setFont(font)

        layout.addWidget(title)

        logo = QLabel()
        logo.setPixmap(QPixmap(os.path.join('images', 'ma-icon-128.png')))
        layout.addWidget(logo)

        layout.addWidget(QLabel("Version 23.35.211.233232"))
        layout.addWidget(QLabel("Copyright 2015 MooseAche Inc."))

        for i in range(0, layout.count()):
            layout.itemAt(i).setAlignment(Qt.AlignHCenter)

        layout.addWidget(self.buttonBox)

        self.setLayout(layout)
Ejemplo n.º 2
0
    def __init__(self, app_context):
        super(AboutDialog, self).__init__()

        ok_btn = QDialogButtonBox.Ok
        self.button_box = QDialogButtonBox(ok_btn)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        layout = QVBoxLayout()

        title = QLabel("Melbourne")
        font = title.font()
        font.setPointSize(20)
        font.setBold(True)
        title.setFont(font)

        logo = QLabel()
        logo.setPixmap(QPixmap(app_context.get_resource("logo_128.png")))

        layout.addWidget(title)
        layout.addWidget(logo)
        layout.addWidget(QLabel("Version 5.0.1"))
        layout.addWidget(QLabel("Copyright © Aditya Duri"))

        for i in range(0, layout.count()):
            layout.itemAt(i).setAlignment(Qt.AlignHCenter)

        layout.addWidget(self.button_box)
        self.setLayout(layout)
Ejemplo n.º 3
0
    def __init__(self):
        super().__init__()

        QBtn = QDialogButtonBox.Ok  # No cancel
        self.buttonBox = QDialogButtonBox(QBtn)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        layout = QVBoxLayout()

        title = QLabel("Mozzarella Ashbadger")
        font = title.font()
        font.setPointSize(20)
        title.setFont(font)

        layout.addWidget(title)

        logo = QLabel()
        logo.setPixmap(QPixmap(os.path.join("icons", "ma-icon-128.png")))
        layout.addWidget(logo)

        layout.addWidget(QLabel("Version 23.35.211.233232"))
        layout.addWidget(QLabel("Copyright 2015 Mozzarella Inc."))

        for i in range(0, layout.count()):
            layout.itemAt(i).setAlignment(Qt.AlignHCenter)

        layout.addWidget(self.buttonBox)

        self.setLayout(layout)
Ejemplo n.º 4
0
class StockList(Updatable, QGroupBox):
    updateComplete = Signal(bool)  #emit this signal when an update is done.

    def __init__(self, kernel, parent):
        super(StockList, self).__init__()
        self.kernel = kernel
        self.addParent(parent)
        self.addUpdateFunction(self.updateStockWidgets)
        self.addUpdateFunction(self.update)
        self.kernel.getUpdateGraph().addUpdatable(self)
        self.initUI()

    def initUI(self):
        self.createElements()
        self.createLayout()
        self.createActions()

    def createElements(self):
        self.stockWidgets = []
        self.securities = set()
        for security in self.kernel.getCurrentUser().getSecuritiesOwned():
            self.securities.add(security)
            self.stockWidgets.append(Stock(security))

    def createLayout(self):
        self.layout = QVBoxLayout()
        for w in self.stockWidgets:
            self.layout.addWidget(w)
        self.setLayout(self.layout)

    def createActions(self):
        pass

    def updateStockWidgets(self):
        for security in self.kernel.getCurrentUser().getSecuritiesOwned():
            # remove old widgets
            for i in reversed(range(self.layout.count())):
                self.layout.itemAt(i).widget().setParent(None)

            # create new widgets
            if security not in self.securities:
                self.securities.add(security)
                newWidget = Stock(security)
                self.stockWidgets.append(newWidget)
                self.layout.addWidget(newWidget)

    def __str__(self):
        return "StockList widget"

    def runUpdates(self):
        updateStatus = super().runUpdates()
        self.updateComplete.emit(updateStatus)
Ejemplo n.º 5
0
class ButtonList(QGroupBox):
    def __init__(self):
        QGroupBox.__init__(self)
        self.createElements(5)
        self.createLayout()
        self.createActions()

    def createElements(self, numElements):
        self.numElements = numElements

        self.lineEdit = QLineEdit()
        self.add = QPushButton("Add another button")
        self.widgets = []
        for i in range(self.numElements):
            self.widgets.append(MyButton("button {}".format(i + 1)))

    def createLayout(self):
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.layout.addWidget(self.lineEdit)
        self.layout.addWidget(self.add)
        for i in range(self.numElements):
            self.layout.addWidget(self.widgets[i])

    def createActions(self):
        self.lineEdit.returnPressed.connect(self.update)
        self.add.clicked.connect(self.addButton)

    def addButton(self):
        self.widgets[0] = MyButton("New Button")
        #self.widgets.append(MyButton("New Button"))
        self.layout.addWidget(self.widgets[-1])
        #self.numElements += 1

    def update(self):
        print(self.lineEdit.text())
        n = int(self.lineEdit.text())
        for i in reversed(range(self.layout.count())):
            self.layout.itemAt(i).widget().setParent(None)
        self.createElements(n)
        self.layout.addWidget(self.lineEdit)
        self.layout.addWidget(self.add)
        for i in range(self.numElements):
            self.layout.addWidget(self.widgets[i])
        self.createActions()
Ejemplo n.º 6
0
class QGroundObjectMenu(QDialog):
    def __init__(self, parent, ground_object: TheaterGroundObject,
                 buildings: [], cp: ControlPoint, game: Game):
        super(QGroundObjectMenu, self).__init__(parent)
        self.setMinimumWidth(350)
        self.ground_object = ground_object
        self.buildings = buildings
        self.cp = cp
        self.game = game
        self.setWindowTitle("Location " + self.ground_object.obj_name)
        self.setWindowIcon(EVENT_ICONS["capture"])
        self.intelBox = QGroupBox("Units :")
        self.buildingBox = QGroupBox("Buildings :")
        self.intelLayout = QGridLayout()
        self.buildingsLayout = QGridLayout()
        self.init_ui()

    def init_ui(self):

        self.mainLayout = QVBoxLayout()
        self.budget = QBudgetBox(self.game)
        self.budget.setGame(self.game)

        self.doLayout()

        if self.ground_object.dcs_identifier == "AA":
            self.mainLayout.addWidget(self.intelBox)
        else:
            self.mainLayout.addWidget(self.buildingBox)
        self.setLayout(self.mainLayout)

    def doLayout(self):
        self.intelBox = QGroupBox("Units :")
        self.intelLayout = QGridLayout()
        i = 0
        for g in self.ground_object.groups:
            if not hasattr(g, "units_losts"):
                g.units_losts = []
            for u in g.units:
                self.intelLayout.addWidget(
                    QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) +
                           "</b>"), i, 0)
                i = i + 1

            for u in g.units_losts:

                utype = unit_type_of(u)
                if utype in PRICES:
                    price = PRICES[utype]
                else:
                    price = 6

                self.intelLayout.addWidget(
                    QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) +
                           "</b> [DEAD]"), i, 0)
                if self.cp.captured:
                    repair = QPushButton("Repair [" + str(price) + "M]")
                    repair.setProperty("style", "btn-success")
                    repair.clicked.connect(
                        lambda u=u, g=g, p=price: self.repair_unit(g, u, p))
                    self.intelLayout.addWidget(repair, i, 1)
                i = i + 1

        self.buildingBox = QGroupBox("Buildings :")
        self.buildingsLayout = QGridLayout()
        j = 0
        for i, building in enumerate(self.buildings):
            if building.dcs_identifier not in FORTIFICATION_BUILDINGS:
                self.buildingsLayout.addWidget(
                    QBuildingInfo(building, self.ground_object), j / 3, j % 3)
                j = j + 1

        self.buildingBox.setLayout(self.buildingsLayout)
        self.intelBox.setLayout(self.intelLayout)

    def do_refresh_layout(self):
        try:
            for i in range(self.mainLayout.count()):
                self.mainLayout.removeItem(self.mainLayout.itemAt(i))
            self.doLayout()
            if len(self.ground_object.groups) > 0:
                self.mainLayout.addWidget(self.intelBox)
            else:
                self.mainLayout.addWidget(self.buildingBox)
        except Exception as e:
            print(e)

    def repair_unit(self, group, unit, price):
        if self.game.budget > price:
            self.game.budget -= price
            group.units_losts = [
                u for u in group.units_losts if u.id != unit.id
            ]
            group.units.append(unit)
            GameUpdateSignal.get_instance().updateGame(self.game)

            # Remove destroyed units in the vicinity
            destroyed_units = self.game.get_destroyed_units()
            for d in destroyed_units:
                p = Point(d["x"], d["z"])
                if p.distance_to_point(unit.position) < 15:
                    destroyed_units.remove(d)
                    logging.info("Removed destroyed units " + str(d))
            logging.info("Repaired unit : " + str(unit.id) + " " +
                         str(unit.type))

        self.do_refresh_layout()

    def closeEvent(self, closeEvent: QCloseEvent):
        GameUpdateSignal.get_instance().updateGame(self.game)
Ejemplo n.º 7
0
class QGroundObjectMenu(QDialog):
    def __init__(
        self,
        parent,
        ground_object: TheaterGroundObject,
        buildings: Optional[List[TheaterGroundObject]],
        cp: ControlPoint,
        game: Game,
    ):
        super().__init__(parent)
        self.setMinimumWidth(350)
        self.ground_object = ground_object
        if buildings is None:
            self.buildings = []
        else:
            self.buildings = buildings
        self.cp = cp
        self.game = game
        self.setWindowTitle(
            f"Location - {self.ground_object.obj_name} ({self.cp.name})"
        )
        self.setWindowIcon(EVENT_ICONS["capture"])
        self.intelBox = QGroupBox("Units :")
        self.buildingBox = QGroupBox("Buildings :")
        self.intelLayout = QGridLayout()
        self.buildingsLayout = QGridLayout()
        self.sell_all_button = None
        self.total_value = 0
        self.init_ui()

    def init_ui(self):

        self.mainLayout = QVBoxLayout()
        self.budget = QBudgetBox(self.game)
        self.budget.setGame(self.game)

        self.doLayout()

        if isinstance(self.ground_object, BuildingGroundObject):
            self.mainLayout.addWidget(self.buildingBox)
            if self.cp.captured:
                self.mainLayout.addWidget(self.financesBox)
        else:
            self.mainLayout.addWidget(self.intelBox)

        self.actionLayout = QHBoxLayout()

        self.sell_all_button = QPushButton("Disband (+" + str(self.total_value) + "M)")
        self.sell_all_button.clicked.connect(self.sell_all)
        self.sell_all_button.setProperty("style", "btn-danger")

        self.buy_replace = QPushButton("Buy/Replace")
        self.buy_replace.clicked.connect(self.buy_group)
        self.buy_replace.setProperty("style", "btn-success")

        if self.ground_object.purchasable:
            if self.total_value > 0:
                self.actionLayout.addWidget(self.sell_all_button)
            self.actionLayout.addWidget(self.buy_replace)

        if self.cp.captured and self.ground_object.purchasable:
            self.mainLayout.addLayout(self.actionLayout)
        self.setLayout(self.mainLayout)

    def doLayout(self):

        self.update_total_value()
        self.intelBox = QGroupBox("Units :")
        self.intelLayout = QGridLayout()
        i = 0
        for g in self.ground_object.groups:
            if not hasattr(g, "units_losts"):
                g.units_losts = []
            for unit in g.units:
                unit_display_name = unit.type
                dcs_unit_type = vehicles.vehicle_map.get(unit.type)
                if dcs_unit_type is not None:
                    # Hack: Don't know which variant is used.
                    try:
                        unit_display_name = next(
                            GroundUnitType.for_dcs_type(dcs_unit_type)
                        ).name
                    except StopIteration:
                        pass
                self.intelLayout.addWidget(
                    QLabel(
                        "<b>Unit #"
                        + str(unit.id)
                        + " - "
                        + str(unit_display_name)
                        + "</b>"
                    ),
                    i,
                    0,
                )
                i = i + 1

            for unit in g.units_losts:
                dcs_unit_type = vehicles.vehicle_map.get(unit.type)
                if dcs_unit_type is None:
                    continue

                # Hack: Don't know which variant is used.

                try:
                    unit_type = next(GroundUnitType.for_dcs_type(dcs_unit_type))
                    name = unit_type.name
                    price = unit_type.price
                except StopIteration:
                    name = dcs_unit_type.name
                    price = 0

                self.intelLayout.addWidget(
                    QLabel(f"<b>Unit #{unit.id} - {name}</b> [DEAD]"), i, 0
                )
                if self.cp.captured:
                    repair = QPushButton(f"Repair [{price}M]")
                    repair.setProperty("style", "btn-success")
                    repair.clicked.connect(
                        lambda u=unit, g=g, p=unit_type.price: self.repair_unit(g, u, p)
                    )
                    self.intelLayout.addWidget(repair, i, 1)
                i = i + 1
        stretch = QVBoxLayout()
        stretch.addStretch()
        self.intelLayout.addLayout(stretch, i, 0)

        self.buildingBox = QGroupBox("Buildings :")
        self.buildingsLayout = QGridLayout()

        j = 0
        total_income = 0
        received_income = 0
        for i, building in enumerate(self.buildings):
            if building.dcs_identifier not in FORTIFICATION_BUILDINGS:
                self.buildingsLayout.addWidget(
                    QBuildingInfo(building, self.ground_object), j / 3, j % 3
                )
                j = j + 1

            if building.category in REWARDS.keys():
                total_income = total_income + REWARDS[building.category]
                if not building.is_dead:
                    received_income = received_income + REWARDS[building.category]
            else:
                logging.warning(building.category + " not in REWARDS")

        self.financesBox = QGroupBox("Finances: ")
        self.financesBoxLayout = QGridLayout()
        self.financesBoxLayout.addWidget(
            QLabel("Available: " + str(total_income) + "M"), 2, 1
        )
        self.financesBoxLayout.addWidget(
            QLabel("Receiving: " + str(received_income) + "M"), 2, 2
        )

        self.financesBox.setLayout(self.financesBoxLayout)
        self.buildingBox.setLayout(self.buildingsLayout)
        self.intelBox.setLayout(self.intelLayout)

    def do_refresh_layout(self):
        try:
            for i in range(self.mainLayout.count()):
                item = self.mainLayout.itemAt(i)
                if item is not None and item.widget() is not None:
                    item.widget().setParent(None)
            self.sell_all_button.setParent(None)
            self.buy_replace.setParent(None)
            self.actionLayout.setParent(None)

            self.doLayout()
            if isinstance(self.ground_object, BuildingGroundObject):
                self.mainLayout.addWidget(self.buildingBox)
            else:
                self.mainLayout.addWidget(self.intelBox)

            self.actionLayout = QHBoxLayout()
            if self.total_value > 0:
                self.actionLayout.addWidget(self.sell_all_button)
            self.actionLayout.addWidget(self.buy_replace)

            if self.cp.captured and self.ground_object.purchasable:
                self.mainLayout.addLayout(self.actionLayout)
        except Exception as e:
            logging.exception(e)
        self.update_total_value()

    def update_total_value(self):
        total_value = 0
        if not self.ground_object.purchasable:
            return
        for u in self.ground_object.units:
            # Hack: Unknown variant.
            unit_type = next(GroundUnitType.for_dcs_type(vehicles.vehicle_map[u.type]))
            total_value += unit_type.price
        if self.sell_all_button is not None:
            self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)")
        self.total_value = total_value

    def repair_unit(self, group, unit, price):
        if self.game.budget > price:
            self.game.budget -= price
            group.units_losts = [u for u in group.units_losts if u.id != unit.id]
            group.units.append(unit)
            GameUpdateSignal.get_instance().updateGame(self.game)

            # Remove destroyed units in the vicinity
            destroyed_units = self.game.get_destroyed_units()
            for d in destroyed_units:
                p = Point(d["x"], d["z"])
                if p.distance_to_point(unit.position) < 15:
                    destroyed_units.remove(d)
                    logging.info("Removed destroyed units " + str(d))
            logging.info("Repaired unit : " + str(unit.id) + " " + str(unit.type))

        self.do_refresh_layout()

    def sell_all(self):
        self.update_total_value()
        self.game.budget = self.game.budget + self.total_value
        self.ground_object.groups = []

        # Replan if the tgo was a target of the redfor
        if any(
            package.target == self.ground_object
            for package in self.game.ato_for(player=False).packages
        ):
            self.game.initialize_turn(for_red=True, for_blue=False)

        self.do_refresh_layout()
        GameUpdateSignal.get_instance().updateGame(self.game)

    def buy_group(self):
        self.subwindow = QBuyGroupForGroundObjectDialog(
            self, self.ground_object, self.cp, self.game, self.total_value
        )
        self.subwindow.show()
Ejemplo n.º 8
0
class GroupOgen(QGroupBox):
    def __init__(self, parent):
        """Groupbox for Ogen module fields filling form."""
        super().__init__()
        self.setTitle('Ogen')
        self.group_type = 'Ogen'
        self.groups_list = []

        self.vbox1 = QVBoxLayout()
        self.vbox1.addStretch()

        self.grid = QGridLayout()
        self.grid.setColumnStretch(5, 1)
        self.grid.addLayout(self.vbox1, 2, 0, 1, 6)
        self.setLayout(self.grid)

    def add_group(self, group, metadata=None):
        """Adds group form."""
        if metadata is not None:
            group.write_fields(metadata=metadata)
        group.form_name.textChanged.connect(self.refresh_children)
        self.groups_list.append(group)
        nWidgetsVbox = self.vbox1.count()
        self.vbox1.insertWidget(nWidgetsVbox - 1,
                                group)  # insert before the stretch
        self.refresh_children(metadata=metadata)

    def is_referenced(self, grp_unique_name):
        """Tests if a group is being referenced any other groups. Returns boolean."""
        nWidgetsVbox = self.vbox1.count()
        for i in range(nWidgetsVbox):
            if self.vbox1.itemAt(i).widget() is not None:
                other_grp = self.vbox1.itemAt(i).widget()
                # check if this subgroup has any ComboBox referencing grp_unique_name
                for ch in other_grp.children():
                    if isinstance(ch, (CustomComboBox, QComboBox)):
                        if ch.currentText() == grp_unique_name:
                            return True
        return False

    def refresh_children(self, metadata=None):
        """Refreshes references with existing objects in child groups."""
        for child in self.groups_list:
            child.refresh_objects_references(metadata=metadata)

    def read_fields(self):
        """Reads fields and returns them structured in a dictionary."""
        error = None
        data = {}
        # group_type counts, if there are multiple groups of same type, they are saved in a list
        grp_types = [grp.group_type for grp in self.groups_list]
        grp_type_count = {
            value: len(list(freq))
            for value, freq in groupby(sorted(grp_types))
        }
        # initiate lists as values for groups keys with count > 1
        for k, v in grp_type_count.items():
            if v > 1 or k == 'Device' or k == 'OptogeneticStimulusSite' or k == 'OptogeneticSeries':
                data[k] = []
        # iterate over existing groups and copy their metadata
        for grp in self.groups_list:
            if grp_type_count[grp.group_type] > 1 or grp.group_type == 'Device' \
               or grp.group_type == 'OptogeneticStimulusSite' \
               or grp.group_type == 'OptogeneticSeries':
                data[grp.group_type].append(grp.read_fields())
            else:
                data[grp.group_type] = grp.read_fields()
        return data, error
Ejemplo n.º 9
0
class MainWindow(QMainWindow):
    def __init__(self, app):
        super(MainWindow, self).__init__()
        self.app = app
        self.central_widget_setup()
        self.default_vbox_setup()
        self.layout.addWidget(PrimaryMonitorSelect(self))
        self.add_application_button_setup()
        self.application_boxes_setup()
        self.application_grid_setup()
        self.application_scroll_setup()
        self.window_setup()

    def window_setup(self):
        self.adjustSize()
        self.setGeometry(
            QStyle.alignedRect(
                Qt.LeftToRight,
                Qt.AlignCenter,
                self.size(),
                QGuiApplication.primaryScreen().availableGeometry(),
            ))
        self.setWindowTitle("Saharah Paper")
        self.setWindowIcon(QIcon(f"{sys.argv[0]}/assets/app_icon.png"))

    def central_widget_setup(self):
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.layout = QVBoxLayout()
        self.layout.setAlignment(Qt.AlignCenter | Qt.AlignTop)
        self.centralWidget.setLayout(self.layout)

    def default_vbox_setup(self):
        self.defaultVBox = QHBoxLayout()
        self.defaultVBox.addWidget(WallpaperBox(self, 0, True))
        self.defaultVBox.addWidget(WallpaperBox(self, 1, True))
        self.layout.addLayout(self.defaultVBox)

    def add_application_button_setup(self):
        self.addApplicationButton = AddApplicationButton(self)
        self.layout.addWidget(self.addApplicationButton,
                              alignment=Qt.AlignCenter)

    def application_boxes_setup(self):
        self.applicationBoxes = list()
        for i in range(0, len(applicationSettings.list)):
            self.applicationBoxes.append(WallpaperBox(self, i))

    def application_grid_setup(self):
        self.applicationGrid = QVBoxLayout()
        self.applicationGridContainer = QWidget()
        self.applicationGridContainer.setLayout(self.applicationGrid)
        self.applicationGridContainer.setFixedWidth(1340)
        self.application_grid_arrange()

    def application_grid_arrange(self):
        for i in reversed(range(0, self.applicationGrid.count())):
            layout = self.applicationGrid.itemAt(i).layout()
            for j in reversed(range(0, layout.count())):
                widget = layout.itemAt(j).widget()
                widget.hide()
        for i in range(0, len(self.applicationBoxes)):
            r = math.floor((i) / 4)
            item = self.applicationGrid.itemAt(r)
            if item is None:
                layout = QHBoxLayout()
                layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
                self.applicationGrid.addLayout(layout)
            else:
                layout = item.layout()
            applicationBox = self.applicationBoxes[i]
            applicationBox.set_index(i)
            applicationBox.show()
            layout.addWidget(applicationBox)
        self.applicationGridContainer.setFixedHeight(
            self.applicationGrid.count() * 250)

    def application_scroll_setup(self):
        self.applicationScroll = QScrollArea()
        self.applicationScroll.setWidget(self.applicationGridContainer)
        self.applicationScroll.setFixedSize(1370, 510)
        self.layout.addWidget(self.applicationScroll)

    def closeEvent(self, event):
        self.app.mainWindow.hide()
        event.accept()
Ejemplo n.º 10
0
class DataDisplay(QWidget):
    def __init__(self, state, transactionList, monthCode, prevCode=None):
        QWidget.__init__(self)
        self.allTransactions = []
        self.state = state
        self.addListeners()
        self.loadNewMonth(transactionList, monthCode, prevCode)

    def addListeners(self):
        self.state.addSubscriber(Events.transaction_drop_event, self.dropEvent)
        self.state.addSubscriber(Events.remove_category, self.removeCategory)
        self.state.addSubscriber(Events.update_category_total,
                                 self.updateConfigCategoryTotal)
        self.state.addSubscriber(Events.update_category_title,
                                 self.updateConfigCategoryName)
        self.state.addSubscriber(Events.add_category, self.addCategory)
        self.state.addSubscriber(Events.remove_all_categories,
                                 self.removeAllCategories)

    def onDestroy(self):
        self.state.removeSubscriber(Events.transaction_drop_event,
                                    self.dropEvent)
        self.state.removeSubscriber(Events.remove_category,
                                    self.removeCategory)
        self.state.removeSubscriber(Events.update_category_total,
                                    self.updateConfigCategoryTotal)
        self.state.removeSubscriber(Events.update_category_title,
                                    self.updateConfigCategoryName)
        self.state.removeSubscriber(Events.add_category, self.addCategory)
        self.state.removeSubscriber(Events.remove_all_categories,
                                    self.removeAllCategories)

    def addCategory(self, data):
        cfg = self.state.getConfig()
        if not data['title'] in cfg['months'][self.month]:
            amt = int(data['amt'])
            cfg['months'][self.month][data['title']] = {
                'transactionList': [],
                'total': amt
            }
            self.state.setConfig(cfg)

            idx = -1
            if data['title'] == 'Income':
                idx = 0
            else:
                for i, section in enumerate(self.sectionState):
                    if int(section['total']) < amt:
                        idx = i
                        break

            self.sectionState.insert(idx, {
                'name': data['title'],
                'total': amt
            })
            self.contentWrapperLayout.insertWidget(
                idx, Category(data['title'], amt, [], self.state))
            self.updateTotalAmt()

    def loadNewMonth(self,
                     transactionList,
                     monthCode,
                     prevCode=None,
                     disableCredit=False):
        self.allTransactions = transactionList
        self.month = monthCode
        self.constructCategories(transactionList, monthCode, prevCode)
        self.updateTotalAmt()

    def constructCategories(self, transactionList, monthCode, prevCode):
        config = self.state.getConfig()
        if monthCode not in config['months']:
            if not config['months']:
                config['months'][monthCode] = {}
            else:
                prevCode = prevCode if prevCode else max(
                    list(config['months'].keys()))
                # config option?
                config['months'][monthCode] = deepcopy(
                    config['months'][prevCode])

        formattedSections = {
            'uncategorized': {
                'tList': [],
                'total': 0
            },
            'income': {
                'tList': [],
                'total': 0
            }
        }

        for transaction in transactionList:
            foundCategory = False
            if transaction.isCredit:
                formattedSections['income']['tList'].append(transaction)
                formattedSections['income']['total'] += transaction.amt
            else:
                for category in list(config['months'][monthCode].keys()):
                    transactionList = config['months'][monthCode][category][
                        'transactionList']
                    if not category in formattedSections.keys():
                        formattedSections[category] = {'tList': [], 'total': 0}
                    if transaction.name in transactionList:
                        foundCategory = True
                        formattedSections[category]['tList'].append(
                            transaction)
                        formattedSections[category]['total'] += transaction.amt
                if not foundCategory:
                    formattedSections['uncategorized']['tList'].append(
                        transaction)
                    formattedSections['uncategorized'][
                        'total'] += transaction.amt

        def sortSection(x, y):
            if x == 'Income':
                return 1
            if x == 'uncategorized':
                return -1
            if formattedSections[x]['total'] < formattedSections[y]['total']:
                return -1
            return 1

        categoryKeys = sorted(list(formattedSections.keys()),
                              key=cmp_to_key(sortSection),
                              reverse=True)
        sectionsUI = []
        self.sectionState = []
        incomeCategory = None
        for category in categoryKeys:
            if category == 'income':
                if formattedSections['income']['total'] > 0:
                    self.sectionState.append({
                        'name':
                        'Income',
                        'total':
                        formattedSections['income']['total']
                    })
                    incomeCategory = {
                        'title': 'Income',
                        'amt': formattedSections['income']['total']
                    }
            elif category == 'uncategorized':
                self.sectionState.append({
                    'name': 'Uncategorized',
                    'total': '0'
                })
                sectionsUI.append(
                    Category('Uncategorized', 0,
                             formattedSections['uncategorized']['tList'],
                             self.state))
            else:
                catTotal = config['months'][monthCode][category]['total']
                self.sectionState.append({'name': category, 'total': catTotal})
                sectionsUI.append(
                    Category(category, catTotal,
                             formattedSections[category]['tList'], self.state))

        self.contentWrapperLayout = QVBoxLayout()
        for sectionLayout in sectionsUI:
            self.contentWrapperLayout.addWidget(sectionLayout)
        self.setLayout(self.contentWrapperLayout)
        self.state.setConfig(config)
        if incomeCategory:
            self.addCategory(incomeCategory)
            incomeCategoryWidget = self.getCategoryWidget('Income')
            incomeCategoryWidget.addTransactions(
                formattedSections['income']['tList'])

    def updateConfigCategoryTotal(self, name, amt):
        cfg = self.state.getConfig()
        cfg['months'][self.month][name]['total'] = amt
        self.state.setConfig(cfg)
        self.updateTotalAmt()

    def updateConfigCategoryName(self, name, newTitle):
        cfg = self.state.getConfig()
        cfg['months'][self.month][newTitle] = cfg['months'][self.month].pop(
            name)
        self.state.setConfig(cfg)

    def dropEvent(self, transactionTitle, destCategoryTitle):
        cfg = self.state.getConfig()
        sourceCat = self.getCategoryFromTransaction(cfg, transactionTitle)
        if not len(sourceCat) or sourceCat[0] != destCategoryTitle:
            transactionsToAdd = []
            if len(sourceCat):
                oldList = cfg['months'][self.month][
                    sourceCat[0]]['transactionList']
                newList = list(filter((transactionTitle).__ne__, oldList))
                cfg['months'][self.month][
                    sourceCat[0]]['transactionList'] = newList
                transactionsToAdd += self.removeTransactionsFromCategory(
                    transactionTitle, sourceCat[0])
            else:
                transactionsToAdd += self.removeTransactionsFromCategory(
                    transactionTitle, 'Uncategorized')

            if destCategoryTitle != 'Uncategorized':
                cfg['months'][self.month][destCategoryTitle][
                    'transactionList'].append(transactionTitle)

            self.state.setConfig(cfg)
            categoryToAddTo = self.getCategoryWidget(destCategoryTitle)
            categoryToAddTo.addTransactions(transactionsToAdd)

    def removeTransactionsFromCategory(self, transactionTitle, title):
        i = 0
        transactionsRemoved = []
        curWidget = self.getCategoryWidget(title)
        j = 0
        while j < len(curWidget.transactions):
            widgetTransaction = curWidget.transactions[j]
            if widgetTransaction.name == transactionTitle:
                transactionsRemoved.append(widgetTransaction)
                curWidget.removeTransaction(widgetTransaction)
            else:
                j += 1

        return transactionsRemoved

    def getCategoryWidget(self, categoryName):
        i = 0
        curWidget = self.contentWrapperLayout.itemAt(i)
        while (curWidget):
            if curWidget.widget().name == categoryName:
                return curWidget.widget()
            i += 1
            curWidget = self.contentWrapperLayout.itemAt(i)

    def getCategoryFromTransaction(self, cfg, title):
        category = list(
            filter(
                lambda x: title in cfg['months'][self.month][x][
                    'transactionList'], cfg['months'][self.month].keys()))
        return category

    def removeCategory(self, title, transactionList):
        cfg = self.state.getConfig()

        amt = cfg['months'][self.month][title]['total']
        del cfg['months'][self.month][title]

        self.state.setConfig(cfg)
        i = 0
        curWidget = self.contentWrapperLayout.itemAt(i)
        while (curWidget):
            if curWidget.widget().name == title:
                curWidget.widget().deleteLater()
                self.contentWrapperLayout.removeWidget(curWidget.widget())
                self.sectionState.remove({'name': title, 'total': amt})
            elif curWidget.widget().name == 'Uncategorized':
                curWidget.widget().addTransactions(transactionList)
                i += 1
            else:
                i += 1
            curWidget = self.contentWrapperLayout.itemAt(i)
        self.updateTotalAmt()

    def removeAllCategories(self):
        cfg = self.state.getConfig()
        allCategories = cfg['months'][self.month].keys()
        for category in allCategories:
            categoryTransactions = self.getCategoryWidget(
                category).getTransactions()
            self.removeCategory(category, categoryTransactions)

    def getTotalAmt(self):
        totalAmt = 0
        for transaction in self.allTransactions:
            # totalAmt += (-1 * transaction.amt) if transaction.isCredit else transaction.amt
            totalAmt += 0 if transaction.isCredit else transaction.amt
        return round(totalAmt, 2)

    def sumCategories(self):
        cfg = self.state.getConfig()
        sum = 0
        categories = list(cfg['months'][self.month].keys())
        for category in categories:
            categoryTotal = cfg['months'][self.month][category]['total']
            # sum += categoryTotal if category != 'Income' else -categoryTotal
            sum += categoryTotal if category != 'Income' else 0
        return sum

    def updateTotalAmt(self):
        categorySum = self.sumCategories()
        transactionSum = self.getTotalAmt()
        self.state.next(Events.update_total, transactionSum, categorySum)
Ejemplo n.º 11
0
class GroupEcephys(QGroupBox):
    def __init__(self, parent):
        """Groupbox for Ecephys module fields filling form."""
        super().__init__()
        self.setTitle('Ecephys')
        self.group_type = 'Ecephys'
        self.groups_list = []

        self.combo1 = CustomComboBox()
        self.combo1.addItem('-- Add group --')
        self.combo1.addItem('Device')
        self.combo1.addItem('ElectrodeGroup')
        self.combo1.addItem('ElectricalSeries')
        self.combo1.addItem('SpikeEventSeries')
        self.combo1.addItem('EventDetection')
        self.combo1.addItem('EventWaveform')
        self.combo1.addItem('LFP')
        self.combo1.addItem('FilteredEphys')
        self.combo1.addItem('FeatureExtraction')
        self.combo1.addItem('DecompositionSeries')
        self.combo1.setCurrentIndex(0)
        self.combo1.activated.connect(lambda: self.add_group('combo'))
        self.combo2 = CustomComboBox()
        self.combo2.addItem('-- Del group --')
        self.combo2.setCurrentIndex(0)
        self.combo2.activated.connect(lambda: self.del_group('combo'))

        self.vbox1 = QVBoxLayout()
        self.vbox1.addStretch()

        self.grid = QGridLayout()
        self.grid.setColumnStretch(5, 1)
        if parent.show_add_del:
            self.grid.addWidget(self.combo1, 1, 0, 1, 2)
            self.grid.addWidget(self.combo2, 1, 2, 1, 2)
        self.grid.addLayout(self.vbox1, 2, 0, 1, 6)
        self.setLayout(self.grid)

    def add_group(self, group, metadata=None):
        """Adds group form."""
        if metadata is not None:
            group.write_fields(metadata=metadata)
        group.form_name.textChanged.connect(self.refresh_del_combo)
        self.groups_list.append(group)
        nWidgetsVbox = self.vbox1.count()
        self.vbox1.insertWidget(nWidgetsVbox - 1,
                                group)  # insert before the stretch
        self.combo1.setCurrentIndex(0)
        self.combo2.addItem(group.form_name.text())
        self.refresh_children(metadata=metadata)

    def del_group(self, group_name):
        """Deletes group form by name."""
        if group_name == 'combo':
            group_name = str(self.combo2.currentText())
        if group_name != '-- Del group --':
            # Tests if any other group references this one
            if self.is_referenced(grp_unique_name=group_name):
                QMessageBox.warning(
                    self, "Cannot delete subgroup", group_name +
                    " is being referenced by another subgroup(s).\n"
                    "You should remove any references of " + group_name +
                    " before "
                    "deleting it!")
                self.combo2.setCurrentIndex(0)
            else:
                nWidgetsVbox = self.vbox1.count()
                for i in range(nWidgetsVbox):
                    if self.vbox1.itemAt(i) is not None:
                        if hasattr(self.vbox1.itemAt(i).widget(), 'form_name'):
                            if self.vbox1.itemAt(
                                    i).widget().form_name.text() == group_name:
                                self.groups_list.remove(
                                    self.vbox1.itemAt(
                                        i).widget())  # deletes list item
                                self.vbox1.itemAt(i).widget().setParent(
                                    None)  # deletes widget
                                self.combo2.removeItem(
                                    self.combo2.findText(group_name))
                                self.combo2.setCurrentIndex(0)
                                self.refresh_children()

    def is_referenced(self, grp_unique_name):
        """Tests if a group is being referenced any other groups. Returns boolean."""
        nWidgetsVbox = self.vbox1.count()
        for i in range(nWidgetsVbox):
            if self.vbox1.itemAt(i).widget() is not None:
                other_grp = self.vbox1.itemAt(i).widget()
                # check if this subgroup has any ComboBox referencing grp_unique_name
                for ch in other_grp.children():
                    if isinstance(ch, (CustomComboBox, QComboBox)):
                        if ch.currentText() == grp_unique_name:
                            return True
        return False

    def refresh_children(self, metadata=None):
        """Refreshes references with existing objects in child groups."""
        for child in self.groups_list:
            child.refresh_objects_references(metadata=metadata)

    def refresh_del_combo(self):
        """Refreshes del combobox with existing objects names in child groups."""
        self.combo2.clear()
        self.combo2.addItem('-- Del group --')
        for child in self.groups_list:
            self.combo2.addItem(child.form_name.text())
        self.refresh_children()

    def read_fields(self):
        """Reads fields and returns them structured in a dictionary."""
        error = None
        data = {}
        # group_type counts, if there are multiple groups of same type, they are saved in a list
        grp_types = [grp.group_type for grp in self.groups_list]
        grp_type_count = {
            value: len(list(freq))
            for value, freq in groupby(sorted(grp_types))
        }
        # initiate lists as values for groups keys with count > 1
        for k, v in grp_type_count.items():
            if v > 1 or k == 'Device' or k == 'ElectrodeGroup' or k == 'ElectricalSeries':
                data[k] = []
        # iterate over existing groups and copy their metadata
        for grp in self.groups_list:
            if grp_type_count[grp.group_type] > 1 or grp.group_type == 'Device' \
               or grp.group_type == 'ElectrodeGroup' or grp.group_type == 'ElectricalSeries':
                data[grp.group_type].append(grp.read_fields())
            else:
                data[grp.group_type] = grp.read_fields()
        return data, error
class MainWidget(Widget):
    """
    Klasa główna wyświetlająca wszystkie niezbędne elementy w głównym oknie programu.
    """
    def __init__(self, user):
        super(MainWidget, self).__init__(user)
        self.que = queue.Queue()
        self.page_number = 1
        self.sorted_by = None
        self.sorted_direction = None
        self._txt_change_perm = 'Zmiana hasła'
        self._txt_permission = 'Utwórz konto z uprawnieniami'
        self._txt_bad_data = 'Błędne dane'
        self.page = 0
        self.lbl_page = QLabel()

        # Layouty
        self.layout = QVBoxLayout()
        self.menu_layout = QHBoxLayout()
        self.text_layout = QVBoxLayout()

        # Widgety
        self.btn_home = QPushButton('Strona główna')
        self.btn_my_book = QPushButton('Wypożyczone książki')
        self.btn_reserved = QPushButton('Zarezerwowane książki')
        self.btn_library = QPushButton('Biblioteka')
        self.btn_profile = QPushButton('Profil')
        self.btn_permission = QPushButton(self._txt_permission)
        self.btn_change_passwd = QPushButton(self._txt_change_perm)
        self.btn_set_permission = QPushButton('Uprawnienia')
        self.btn_logout = QPushButton('Wylogowanie')
        self.btn_add_book = QPushButton('Dodaj nową książkę')

        # Ustawienia widgetów
        self.menu_layout.setContentsMargins(-1, -1, -1, 25)
        self.btn_home.clicked.connect(self.on_home_clicked)
        if self.user.get('roleId') > 1:
            self.btn_my_book.clicked.connect(self.on_book_clicked_permission)
        else:
            self.btn_my_book.clicked.connect(self.on_book_clicked)
        self.btn_reserved.clicked.connect(self.on_reserved_clicked)
        self.btn_library.clicked.connect(self.on_library_clicked)
        self.btn_permission.clicked.connect(self.on_permission_clicked)
        if self.user.get('roleId') != 3:
            self.btn_change_passwd.clicked.connect(
                self.on_change_passwd_clicked)
            self.btn_profile.clicked.connect(self.on_profile_clicked)
        else:
            self.btn_profile.setText('Usuwanie profili')
            self.btn_change_passwd.clicked.connect(
                self.on_change_passwd_admin_clicked)
            self.btn_profile.clicked.connect(self.on_profile_admin_clicked)
        self.btn_set_permission.clicked.connect(self.on_set_permission_clicked)
        self.btn_logout.clicked.connect(self.on_logout_clicked)
        self.btn_next_page.clicked.connect(self.next_page)
        self.btn_back_page.clicked.connect(self.back_page)
        self.btn_add_book.clicked.connect(self.add_book)
        self.edit_search.returnPressed.connect(self.search)

        # Przypisanie widgetów do layoutów
        self.menu_layout.addWidget(self.btn_home)
        self.menu_layout.addWidget(self.btn_my_book)
        if self.user.get('roleId') == 1:
            self.menu_layout.addWidget(self.btn_reserved)
        self.menu_layout.addWidget(self.btn_library)
        self.menu_layout.addWidget(self.btn_profile)
        if self.user.get('roleId') == 3:
            self.menu_layout.addWidget(self.btn_permission)
            self.menu_layout.addWidget(self.btn_set_permission)
        self.menu_layout.addWidget(self.btn_change_passwd)
        self.menu_layout.addItem(self.h_spacer)
        self.menu_layout.addWidget(self.btn_logout)
        self.text_layout.addWidget(self.lbl_home)

        self.layout.addLayout(self.menu_layout)
        self.layout.addLayout(self.text_layout)

        self.setLayout(self.layout)

    def clear_layout(self):
        """
        Czyści layout głównego widżetu z pozostałych, niepotrzebnych  widgetów.
        """
        for i in reversed(range(self.text_layout.count())):
            self.text_layout.itemAt(i).widget().setParent(None)

    def on_home_clicked(self):
        """
        Wyświetla widgety strony głównej.
        """
        print("Home")
        self.clear_layout()
        self.text_layout.addWidget(self.lbl_home)

    def on_book_clicked(self, user_id=None):
        """
        Wyświetla wypożyczone książki danego użytkownika.
        :param user_id: int
        """
        print("My books")
        if user_id is None:
            self.clear_layout()
        self.tbl_result.clear()
        self.lbl_title.setText('Wypożyczone książki')
        self.text_layout.addWidget(self.lbl_title)
        if self.get_book_user_id(user_id):
            self.text_layout.addWidget(self.tbl_result)

    def on_reserved_clicked(self, user_id=None):
        """
        Wyświetla zarezerwowane książki danego użytkownika.
        """
        print("Reserved")
        self.clear_layout()
        self.tbl_result.clear()
        self.lbl_title.setText('Zarezerwowane książki')
        self.text_layout.addWidget(self.lbl_title)
        if self.get_reservation_user_id(user_id):
            self.text_layout.addWidget(self.tbl_result)

    def on_book_clicked_permission(self):
        """
        Dla konta z uprawnieniami, wyświetla tabelę użytkowników przed tabelą wypożyczonych książek.
        """
        print('Books permision')
        self.lbl_title.setText('Użytkownicy')
        self.tbl_result.clear()
        self.clear_layout()
        self.text_layout.addWidget(self.lbl_title)
        self.get_users()
        self.text_layout.addWidget(self.tbl_result)

    def next_page(self):
        """
        Wyświetla następną stronę książek w bibliotece.
        """
        self.page_number += 1
        self.lbl_page.setText('Strona {}/{}'.format(self.page_number,
                                                    self.page))
        if self.lbl_title.text() == 'Biblioteka':
            self.get_books(self.page_number,
                           search=self.edit_search.text(),
                           sort_by=self.sorted_by,
                           sort_direction=self.sorted_direction)

    def back_page(self):
        """
        Wyświetla poprzednią stronę książek w bibliotece.
        """
        self.page_number -= 1
        self.lbl_page.setText('Strona {}/{}'.format(self.page_number,
                                                    self.page))
        if self.lbl_title.text() == 'Biblioteka':
            self.get_books(self.page_number,
                           search=self.edit_search.text(),
                           sort_by=self.sorted_by,
                           sort_direction=self.sorted_direction)

    def search(self):
        """
        Wyszukuje odpowiednią frazę w kolumnach Tytuł, Wydawca, Autor, Opis.
        """
        self.page_number = 1
        if self.lbl_title.text() == 'Biblioteka':
            self.get_books(1,
                           search=self.edit_search.text(),
                           sort_direction=self.sorted_direction)

    def sort_direction(self, text):
        """
        Sortuje wyniki wyświetlania zawartości biblioteki rosnąco bądź malejąco.
        :param text: str
        """
        sort_dict = {'Rosnąco': 0, 'Malejąco': 1}
        self.page_number = 1
        self.sorted_direction = sort_dict.get(text)
        if self.lbl_title.text() == 'Biblioteka':
            self.get_books(1,
                           sort_by=self.sorted_by,
                           sort_direction=self.sorted_direction)

    def sort_by(self, text):
        """
        Sortuje wyniki wyświetlania zawartości biblioteki na podstawie Tytułu, Opisu, Kategorii, Wydawcy lub Daty
        wydania.
        :param text: str
        """
        sort_dict = {
            'Sortuj według': None,
            'Tytuł': 'BookName',
            'Opis': 'BookDescription',
            'Kategoria': 'Category',
            'Wydawca': 'PublisherName',
            'Data wydania': 'PublishDate'
        }
        self.page_number = 1
        self.sorted_by = sort_dict.get(text)
        if self.lbl_title.text() == 'Biblioteka':
            self.get_books(1,
                           sort_by=self.sorted_by,
                           sort_direction=self.sorted_direction)

    def on_library_clicked(self):
        """
        Wyświetla książki zawarte w bibliotece.
        """
        print("library")
        self.clear_layout()
        self.lbl_title.setText('Biblioteka')
        self.edit_search.setPlaceholderText('Wyszukaj i wciśnij ENTER')
        self.btn_delete_book.show()
        self.btn_borrow_book.show()
        self.text_layout.addWidget(self.lbl_title)
        self.text_layout.addWidget(self.library)
        self.text_layout.addWidget(self.tbl_result)
        self.text_layout.addWidget(self.btn_add_book)
        self.btn_add_book.setStyleSheet(
            "color: #17a2b8; border-color : #17a2b8")
        self.get_books(self.page_number)

        print(self.page_number, self.page)
        self.lbl_page.setText('Strona {}/{}'.format(self.page_number,
                                                    self.page))
        self.text_layout.addWidget(self.lbl_page)

    def on_profile_clicked(self):
        """
        Wyświetla dane profilu, dostępne do edycji.
        """
        print("profile")
        self.clear_layout()
        self.lbl_title.setText('Ustawienia profilu')
        self.text_layout.addWidget(self.lbl_title)
        self.text_layout.addWidget(self.dialog_profile)
        data = self.get_user_id()
        self.edit_name.setText(data.get('name'))
        self.edit_subname.setText(data.get('surname'))
        self.edit_email.setText(data.get('email'))

    def on_profile_admin_clicked(self):
        """
        Wyświetla dane wszystkich użytkowników.
        """
        self.clear_layout()
        self.lbl_title.setText('Usuwanie profilu')
        self.text_layout.addWidget(self.lbl_title)
        self.get_users()
        self.text_layout.addWidget(self.tbl_result)

    def on_permission_clicked(self):
        """
        Wyświetla możliwość utworzenia konta z uprawnieniami.
        """
        print("permission")
        self.clear_layout()
        self.lbl_title.setText('Utwórz nowe konto z uprawnieniami')
        self.text_layout.addWidget(self.lbl_title)
        self.text_layout.addWidget(self.dialog_permission)

    def on_set_permission_clicked(self):
        """
        Wyświetla możliwość zmiany uprawnień konta.
        """
        print("permission set")
        self.clear_layout()
        self.lbl_title.setText('Uprawnienia')
        self.text_layout.addWidget(self.lbl_title)
        self.get_users()
        self.text_layout.addWidget(self.tbl_result)

    def on_set_permission(self):
        print("permission set")
        self.clear_layout()
        self.lbl_title.setText('Uprawnienia')
        self.text_layout.addWidget(self.lbl_title)
        self.text_layout.addWidget(self.dialog_set_permission)

    def change_permission(self, user_id):
        permission_api = URL + self.get_users_api + '/role/' + str(user_id)
        jsons = {"roleId": int(self._role_id)}

        x = threading.Thread(target=patch_request,
                             args=(permission_api, jsons,
                                   self.user.get('token'), self.que))

        x.start()
        x.join()

        data = self.que.get()
        print(data)
        if data.status_code == 200:
            QMessageBox.information(self, "Zmiana uprawnień",
                                    "Uprawnienia zostały zmienione!")

    def on_change_passwd_clicked(self):
        """
        Wyświetla możliwość zmiany hasła.
        """
        print("Change password")
        self.clear_layout()
        self.lbl_title.setText(self._txt_change_perm)
        self.text_layout.addWidget(self.lbl_title)
        self.text_layout.addWidget(self.dialog_password)
        self.edit_passwd1.setFocus()

    def on_change_passwd_admin_clicked(self):
        """
        Wyświetla użytkowników, dla których hasło zostanie zmienione.
        """
        print("Change admin password")
        self.clear_layout()
        self.lbl_title.setText(self._txt_change_perm)
        self.text_layout.addWidget(self.lbl_title)
        self.get_users()
        self.text_layout.addWidget(self.tbl_result)

    def on_logout_clicked(self):
        """
        Wylogowuje użytkownika z programu.
        """
        print("Logout")
        self.clear_layout()
        self.logout()

    def logout(self):
        """
        Ukrywa główne okno, wyświetla monit o logowanie, po czym podmienia zawartość zmiennej user.
        """
        self.close()
        self.parent().hide()
        window = Login()
        dec = window.exec_()
        if dec == QDialog.Accepted:
            result = json.loads(window.response.text)
            self.user = result
            self.parent().setCentralWidget(MainWidget(result))
            self.parent().show()
            self.on_home_clicked()

    def set_data(self, data):
        """
        Wyświetla dane w tabeli i je formatuje.
        :param data: dict
        """
        self.tbl_result.clear()
        row_count = (len(data))
        column_count = (len(data[0]))
        role_id = {1: 'Użytkownik', 2: 'Pracownik', 3: 'Administrator'}

        self.tbl_result.setColumnCount(column_count)
        self.tbl_result.setRowCount(row_count)

        self.tbl_result.setHorizontalHeaderLabels((list(data[0].keys())))

        if list(data[0].keys())[0] == 'id':
            self.tbl_result.hideColumn(0)
        if 'roleId' in list(data[0].keys(
        )) and self.lbl_title.text() != self._txt_permission:
            self.tbl_result.hideColumn(4)
        else:
            self.tbl_result.showColumn(4)

        for row in range(row_count):
            for column in range(column_count):
                item = (list(data[row].values())[column])
                if column == 4 and self.lbl_title.text(
                ) == self._txt_permission:
                    item = role_id.get(item)
                self.tbl_result.setItem(row, column,
                                        QTableWidgetItem(str(item)))

        self.tbl_result.resizeColumnsToContents()
        self.tbl_result.resizeRowsToContents()

    def get_books(self,
                  page_number,
                  sort_by=None,
                  sort_direction=None,
                  search=None):
        """
        Pobiera dane o zawartości książek w bibliotece z API w wątku.
        :param page_number: int
        :param sort_by: str
        :param sort_direction: int
        :param search: str
        """
        self.page_number = page_number
        url = URL + self.get_books_api + f'?PageNumber={page_number}&PageSize=15'
        if search:
            url += f'&SearchPhrase={search}'
        if sort_by:
            url += f'&SortBy={sort_by}'
        if sort_direction:
            url += f'&SortDirection={sort_direction}'

        x = threading.Thread(target=get_request,
                             args=(url, self.user.get('token'), self.que))

        x.start()
        x.join()

        data = self.que.get()
        self.page = data.get('totalPages')

        if data.get('itemFrom') == 1:
            self.btn_back_page.setEnabled(False)
        else:
            self.btn_back_page.setEnabled(True)
        if page_number < data.get('totalPages'):
            self.btn_next_page.setEnabled(True)
        else:
            self.btn_next_page.setEnabled(False)

        self.set_data(data.get('items'))

    def get_users(self):
        """
        Pobiera dane o użytkownikach z API w wątku.
        """
        x = threading.Thread(target=get_request,
                             args=("".join([URL, self.get_users_api]),
                                   self.user.get('token'), self.que))

        x.start()
        x.join()

        data = self.que.get()
        self.set_data(data)

    def get_user_id(self):
        """
        Pobiera dane o konkretnym użytkowniku w wątku i je zwraca.
        :return: json
        """
        user_id_api = "".join(
            [URL, self.get_users_api, '/',
             str(self.user.get('id'))])
        x = threading.Thread(target=get_request,
                             args=(user_id_api, self.user.get('token'),
                                   self.que))

        x.start()
        x.join()

        data = self.que.get()
        return data

    def get_book_id(self, book_id):
        """
        Pobiera dane o konkretnej książce w wątku i je zwraca.
        :param book_id: int
        :return: json
        """
        self.btn_delete_book.clicked.connect(lambda: self.delete_book(book_id))
        self.btn_borrow_book.clicked.connect(lambda: self.borrow_book_user())
        self._book_id = book_id
        book_id_api = "".join([URL, self.get_books_api, '/', str(book_id)])
        x = threading.Thread(target=get_request,
                             args=(book_id_api, self.user.get('token'),
                                   self.que))

        x.start()
        x.join()

        data = self.que.get()
        return data

    def get_book_user_id(self, user_id=None):
        """
        Pobiera dane na temat wypożyczonych książek przez konkretnego użytkownika.
        :param user_id: int
        :return: boolean
        """
        if not user_id:
            user_id = self.user.get('id')
        book_user_id_api = URL + self.get_books_api + '/' + self.get_users_api[:-1] + '/' + str(
            user_id)
        x = threading.Thread(target=get_request,
                             args=(book_user_id_api, self.user.get('token'),
                                   self.que))

        x.start()
        x.join()

        data = self.que.get()
        if data:
            self.set_data(data)
            return True
        else:
            return False

    def get_reservation_user_id(self, user_id=None):
        """
        Pobiera dane na temat zarezerwowanych książek przez konkretnego użytkownika.
        :param user_id: int
        :return: boolean
        """
        if not user_id:
            user_id = self.user.get('id')
        reservation_user_id_api = URL + self.get_books_api + '/' + self.get_users_api[:-1] + self.reservation_api + str(
            user_id)
        x = threading.Thread(target=get_request,
                             args=(reservation_user_id_api,
                                   self.user.get('token'), self.que))

        x.start()
        x.join()

        data = self.que.get()
        if data:
            self.set_data(data)
            return True
        else:
            return False

    def borrow_book_user(self):
        """
        Wyświetla listę użytkowników, dla których dana książka zostanie wypożyczona.
        """
        self.clear_layout()
        self.lbl_title.setText('Wybierz użytkownika')
        self.text_layout.addWidget(self.lbl_title)
        self.get_users()
        self.text_layout.addWidget(self.tbl_result)

    def reservation_book(self, book_id):
        """
        Dokonuje rezerwacji danej książki w wątku.
        :param book_id: int
        """
        url = URL + self.get_books_api + self.reservation_api + str(book_id)
        x = threading.Thread(target=patch_request,
                             args=(url, {}, self.user.get('token'), self.que))

        x.start()
        x.join()

        data = self.que.get()
        print(data)
        return data

    def borrow_book(self, book_id, user_id):
        """
        Dokonuje wypożyczenia książki użytkownikowi, w wątku.
        :param book_id: int
        :param user_id: int
        """
        url = URL + self.get_books_api + '/borrow/' + str(book_id)
        jsons = {"userId": int(user_id)}

        self.reservation_book(book_id)

        x = threading.Thread(target=patch_request,
                             args=(url, jsons, self.user.get('token'),
                                   self.que))

        x.start()
        x.join()

        data = self.que.get()
        print(data)
        if data:
            QMessageBox.information(self, "Wypożyczono",
                                    "Podana pozycja została wypożyczona!")
            self.on_library_clicked()
        elif data.status_code == 400:
            QMessageBox.warning(self, "Błąd", data.text)
            self.on_library_clicked()

    def back_book(self, book_id):
        """
        Dokonuje zwrotu danej książki w wątku.
        :param book_id: int
        """
        url = URL + self.get_books_api + '/return/' + str(book_id)
        jsons = {}
        x = threading.Thread(target=patch_request,
                             args=(url, jsons, self.user.get('token'),
                                   self.que))

        x.start()
        x.join()

        data = self.que.get()
        print(data)
        print('Zwrócono')
        if data:
            QMessageBox.information(self, "Zwrócono",
                                    "Książka została zwrócona!")
            self.on_home_clicked()
        else:
            self.clear_layout()
            self.lbl_title.setText('Nie masz żadnych wypożyczonych książek')
            self.text_layout.addWidget(self.lbl_title)

    def post_user(self):
        """
        Dokonuje utworzenia nowego użytkownika.
        """
        user_api = "".join([URL, self.get_users_api])
        jsons = {
            "name": self.edit_new_name.text(),
            "surname": self.edit_new_surname.text(),
            "email": self.edit_new_email.text(),
            "password": self.edit_pass2.text(),
            "confirmPassword": self.edit_pass3.text(),
            "roleId": self._role_id
        }

        if self.edit_pass2.text() != self.edit_pass3.text():
            QMessageBox.warning(self, "Błędne hasła",
                                "Hasła nie są takie same.")
            self.edit_pass2.setText('')
            self.edit_pass3.setText('')
            self.edit_pass2.setFocus()
            return
        if self.edit_pass2.text() == '':
            QMessageBox.warning(self, "Błąd", "Hasła nie mogą być puste.")
            self.edit_pass2.setFocus()
            return
        if self.edit_new_email.text() == '':
            QMessageBox.warning(self, "Błąd", "Email nie może być pusty.")
            self.edit_new_email.setFocus()
            return

        x = threading.Thread(target=post_request,
                             args=(user_api, jsons, self.user.get('token'),
                                   self.que))

        x.start()
        x.join()

        data = self.que.get()
        if data.status_code == 201:
            QMessageBox.information(self, "Nowe konto",
                                    'Utworzono nowe konto.')
        print(data)

    def add_book(self):
        """
        Wyświetla widgety odpowiedzialne za dodanie nowej książki do biblioteki.
        """
        print("Add book")
        self.clear_layout()
        self.lbl_title.setText('Dodaj nową książkę')
        self.text_layout.addWidget(self.lbl_title)
        self.btn_delete_book.hide()
        self.btn_borrow_book.hide()
        self.text_layout.addWidget(self.dialog_book)

    def change_passwd(self, user_id=None):
        """
        Umożliwia zmianę hasła danego użytkownika w wątku.
        :param user_id: int
        """
        if user_id is None:
            user_id = self.user.get('id')
        print(user_id)
        token = self.user.get('token')
        url_password = "".join(['users/changePassword/', str(user_id)])

        if self.edit_passwd2.text() == '' or self.edit_passwd3.text() == '':
            QMessageBox.warning(self, "Brak nowego hasła",
                                "Nowe hasło nie może być puste")
            self.edit_passwd2.setText('')
            self.edit_passwd3.setText('')
            self.edit_passwd2.setFocus()
            return

        if self.edit_passwd2.text() != self.edit_passwd3.text():
            QMessageBox.warning(self, self._txt_bad_data,
                                "Podane hasła nie są identyczne!")
            self.edit_passwd2.setText('')
            self.edit_passwd3.setText('')
            self.edit_passwd2.setFocus()
            return

        jsons = {
            "oldPassword": self.edit_passwd1.text(),
            "newPassword": self.edit_passwd2.text(),
            "confirmNewPassword": self.edit_passwd3.text()
        }
        x = threading.Thread(target=patch_request,
                             args=("".join([URL, url_password]), jsons, token,
                                   self.que))

        x.start()
        x.join()

        response = self.que.get()
        if response.status_code == 400:
            print(response.text)
            if response.text == 'Invalid password':
                QMessageBox.warning(self, "Błędne hasło", response.text)
                self.edit_passwd1.setText('')
                self.edit_passwd1.setFocus()
                return
            error = json.loads(response.text)
            err = json.loads(
                json.dumps(error['errors'], indent=4, sort_keys=True))
            print(err)
            for key in err.keys():
                if key == 'NewPassword':
                    QMessageBox.warning(self, "Nowe hasło", err.get(key)[0])

        if response.status_code == 200:
            QMessageBox.information(self, "Hasło zmienione",
                                    "Hasło zostało zmienione!")
            self.edit_passwd1.setText('')
            self.edit_passwd2.setText('')
            self.edit_passwd3.setText('')
            self.on_home_clicked()

    def delete_profile(self, user_id=None):
        """
        Umożliwia usunięcie danego użytkownika z bazy, w wątku.
        :param user_id: int
        """
        token = self.user.get('token')
        _user_id = user_id
        if _user_id is None:
            user_id = self.user.get('id')
        url_profile = "".join([URL, self.get_users_api, '/', str(user_id)])
        button_reply = QMessageBox.question(
            self, 'Usuwanie',
            "Czy na pewnno chcesz usunąć te konto?\nOperacji tej nie można cofnąć.",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if button_reply == QMessageBox.No:
            return

        x = threading.Thread(target=delete_request,
                             args=(url_profile, token, self.que))

        x.start()
        x.join()

        response = self.que.get()
        if response.status_code == 204:
            QMessageBox.information(self, "Usunięto",
                                    "Konto zostało usunięte!")
            if _user_id != self.user.get('id'):
                return
            self.on_logout_clicked()

        if response.status_code == 500:
            QMessageBox.warning(
                self, "Błąd",
                "Nie można usunąć użytkownika, który wypożyczył książki!")

    def delete_book(self, book_id):
        """
        Umożliwia usunięcie danej książki z bazy, w wątku.
        :param book_id: int
        """
        token = self.user.get('token')
        book_id_api = "".join([URL, self.get_books_api, '/', str(book_id)])
        button_reply = QMessageBox.question(
            self, 'Usuwanie',
            "Czy na pewno chcesz usunąć tą książkę?\nOperacji tej nie można cofnąć.",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if button_reply == QMessageBox.No:
            return

        x = threading.Thread(target=delete_request,
                             args=(book_id_api, token, self.que))

        x.start()
        x.join()

        response = self.que.get()
        if response.status_code == 204:
            QMessageBox.information(self, "Usunięto",
                                    "Książka została usunięta!")
            self.on_library_clicked()

    def new_book(self, book_id=None):
        """
        Umożliwia utworzenie nowej książki w bazie, bądź zmianę już istniejącej, w wątku.
        :param book_id: int
        """
        token = self.user.get('token')
        flag_book = False

        if self.edit_isbn.text() == '' or \
                self.edit_book_name.text() == '' or \
                self.edit_author.text() == '' or \
                self.edit_publisher.text() == '' or \
                self.edit_publish_date.text() == '' or \
                self.edit_category.text() == '':
            QMessageBox.warning(self, "Uwaga, błąd danych",
                                "Należy wypełnić wymagane pola.")
            return
        if not self.edit_publish_date.text().isdigit():
            QMessageBox.warning(self, self._txt_bad_data,
                                'Pole "Data wydania" należy wypełnić liczbą.')
            self.edit_publish_date.setFocus()
            return

        jsons = {
            "isbn": self.edit_isbn.text(),
            "bookName": self.edit_book_name.text(),
            "authorName": self.edit_author.text(),
            "publisherName": self.edit_publisher.text(),
            "publishDate": int(self.edit_publish_date.text()),
            "category": self.edit_category.text(),
            "language": self.edit_language_book.text(),
            "bookDescription": self.edit_book_description.toPlainText()
        }

        if not book_id:
            book_id_api = "".join([URL, self.get_books_api])
            x = threading.Thread(target=post_request,
                                 args=(book_id_api, jsons, token, self.que))
            title = 'Dodano'
            descr = 'Dodano nową pozycję do biblioteki.'
            flag_book = True
        if book_id:
            book_id_api = "".join([URL, self.get_books_api, '/', str(book_id)])
            x = threading.Thread(target=put_request,
                                 args=(book_id_api, jsons, token, self.que))
            title = 'Zmieniono'
            descr = 'Dane o książce zostały zaktualizowane.'
            flag_book = True

        if flag_book:
            x.start()
            x.join()

            response = self.que.get()
            print(response)
            if response.status_code == 200 or response.status_code == 201:
                QMessageBox.information(self, title, descr)
                self.on_library_clicked()

    def change_profile(self):
        """
        Umożliwia zmianę danych użytkownika, w wątku.
        """
        token = self.user.get('token')
        url_profile = "".join(
            [URL, self.get_users_api, '/',
             str(self.user.get('id'))])

        name = self.edit_name.text()
        surname = self.edit_subname.text()
        email = self.edit_email.text()

        try:
            validate_email(email)
        except EmailNotValidError as e:
            print(e)
            QMessageBox.warning(self, "Błędny email",
                                "Proszę wpisać poprawny email")
            self.edit_email.setFocus()
            return

        if name == '' or surname == '' or email == '':
            QMessageBox.warning(self, self._txt_bad_data,
                                "Podane dane nie mogą być puste!")
            self.edit_name.setFocus()
            return

        jsons = {"name": name, "surname": surname, "email": email}

        x = threading.Thread(target=put_request,
                             args=(url_profile, jsons, token, self.que))

        x.start()
        x.join()

        response = self.que.get()
        print(response)
        if response.status_code == 200:
            QMessageBox.information(self, "Zmiana danych",
                                    "Dane zostały pomyślnie zapisane!")

    def change_book(self, book_id):
        """
        Uzupełnia dane widgetów o zawartość jsona danej książki.
        :param book_id: int
        """
        print("Change book id")
        self.clear_layout()
        self.lbl_title.setText('Edycja książki')
        self.text_layout.addWidget(self.lbl_title)
        jsons = self.get_book_id(book_id)
        self.edit_isbn.setText(jsons.get('isbn'))
        self.edit_book_name.setText(jsons.get('bookName'))
        self.edit_author.setText(jsons.get('authorName'))
        self.edit_publisher.setText(jsons.get('publisherName'))
        self.edit_publish_date.setText(str(jsons.get('publishDate')))
        self.edit_category.setText(jsons.get('category'))
        self.edit_language_book.setText(jsons.get('language'))
        self.edit_book_description.setPlainText(jsons.get('bookDescription'))
        self.text_layout.addWidget(self.dialog_book)

    def delete_reservation(self, book_id):
        """
        Zwraca zarezerwowaną książkę.
        :param book_id: int
        """
        print("Return book reserved")

        reservation_del_api = URL + self.get_books_api + self.reservation_api + str(
            book_id)
        x = threading.Thread(target=delete_request,
                             args=(reservation_del_api, self.user.get('token'),
                                   self.que))

        x.start()
        x.join()

        data = self.que.get()
        print(data)
        if data:
            QMessageBox.information(
                self, "Pozycja usunięta",
                "Podana pozycja została usunięta z listy zarezerwowanych!")

        self.on_reserved_clicked()
Ejemplo n.º 13
0
class WetSand(QWidget):
    def __init__(self):
        super().__init__()

        # Init list
        self.list = QListWidget()
        self.list.addItem(WetListItem("You don't form in the wet sand"))
        self.list.addItem(WetListItem("You don't form at all"))

        # Init buttons
        self.btns = [
            WetButton('johs'),
            WetButton('jon'),
            WetButton('feel'),
            WetButton('chat'),
            WetButton('tony')
        ]

        # Freeze and unfreeze buttons
        self.freeze_btn = QPushButton('Freeze me')
        self.unfreeze_btn = QPushButton('Unfreeze me')

        # Layout stuff
        self.btn_lyt = QVBoxLayout()
        for btn in self.btns:
            self.btn_lyt.addWidget(btn)

        self.top_lyt = QHBoxLayout()
        self.top_lyt.addWidget(self.list)
        self.top_lyt.addLayout(self.btn_lyt)

        self.bottom_lyt = QHBoxLayout()
        self.bottom_lyt.addWidget(self.freeze_btn)
        self.bottom_lyt.addWidget(self.unfreeze_btn)

        self.main_lyt = QVBoxLayout()
        self.main_lyt.addLayout(self.top_lyt)
        self.main_lyt.addLayout(self.bottom_lyt)

        # Signals and slots
        # Use functools.partial to send arguments
        self.freeze_btn.clicked.connect(partial(self.freeze, True))
        self.unfreeze_btn.clicked.connect(partial(self.freeze, False))

        # Don't forget me
        self.setLayout(self.main_lyt)
        self.show()

    @Slot()
    def freeze(self, yes=True):
        # Freeze buttons
        print(yes)
        btns = (self.btn_lyt.itemAt(i).widget()
                for i in range(self.btn_lyt.count()))
        for btn in btns:
            btn.set_clickable(not yes)

        # Freeze list items
        list_items = [self.list.item(i) for i in range(self.list.count())]
        for list_item in list_items:
            list_item.set_selectable(not yes)
Ejemplo n.º 14
0
class ConfigWindow(QWidget):
    def __init__(self, current_profile):
        """
        The ConfigWindow is a widget dedicated to reading and editing the OCI config file and provides functionality to create, edit, and switch profiles on the fly, updating
        the view of the main window.

        :param current_profile: The profile the ConfigWindow should be initialized with
        :type current_profile: string
        """
        super().__init__()

        #
        self.main_window = None
        self.setWindowTitle("Profile Settings")
        self.setMinimumSize(600, 200)

        #Looks for the config file in '~/.oci/config' and reads it into config
        self.DEFAULT_LOCATION = os.path.expanduser(
            os.path.join('~', '.oci', 'config'))
        self.config = configparser.ConfigParser(interpolation=None)
        self.config.read(self.DEFAULT_LOCATION)
        self.current_profile = current_profile

        #Set up necessary dropdown and LineEdit widgets
        self.dropdown = self.get_profiles_dropdown()
        self.tenancy = QLineEdit()
        self.tenancy.setPlaceholderText("Tenancy OCID")
        self.region = QLineEdit()
        self.region.setPlaceholderText("Region")
        self.user = QLineEdit()
        self.user.setPlaceholderText("User OCID")
        self.fingerprint = QLineEdit()
        self.fingerprint.setPlaceholderText("Fingerprint")
        self.key_file = QLineEdit()
        self.key_file.setPlaceholderText("Key File Path")
        self.passphrase = QLineEdit()
        self.passphrase.setEchoMode(QLineEdit.Password)
        self.passphrase.setPlaceholderText("Passphrase")
        self.save_button = QPushButton('Save')
        self.save_button.clicked.connect(self.save_signal)

        #Set the profile to the current_profile passed in upon init
        self.change_profile(current_profile)
        self.dropdown.setCurrentText(current_profile)

        #Add all widgets to a vertical layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.dropdown)
        self.layout.addWidget(self.tenancy)
        self.layout.addWidget(self.region)
        self.layout.addWidget(self.user)
        self.layout.addWidget(self.key_file)
        self.layout.addWidget(self.fingerprint)
        self.layout.addWidget(self.passphrase)
        self.layout.addWidget(self.save_button)

        self.setLayout(self.layout)

    def get_profiles_dropdown(self):
        """
        :return:
            A dropdown menu widget that lists all profiles including the default profile from the OCI config file
            When index changes, it will call the change_profile signal function
        :rtype: :class: 'Qt.QtWidgets.QComboBox'
        """
        dropdown = QComboBox()
        dropdown.addItems(['DEFAULT'] + self.config.sections())
        dropdown.addItem("Add New Profile...")
        dropdown.currentIndexChanged.connect(self.change_profile_signal)
        return dropdown

    def change_profile_signal(self, item):
        """
        Slot to change profile. If the item index is at 0, then it is the default profile.
        If it is the last index, then that means create a new profile

        :param item: The index of the item from the dropdown widget
        :type item: int
        """
        if item > len(self.config.sections()):
            self.create_new_profile()
        elif item == 0:
            self.change_profile('DEFAULT')
        else:
            self.change_profile(self.config.sections()[item - 1])

    def change_profile(self, profile_name):
        """
        Changes the profile that the ConfigWindow is set for and also changes it for the MainWindow

        :param profile_name: the name of the profile to switch to
        :type profile_name: string

        TODO: Adhere to signal/slot convention
        """
        self.current_profile = profile_name
        profile = self.config[profile_name]

        for line, key in zip([self.tenancy, self.region, self.user, self.fingerprint, self.key_file, self.passphrase],\
        ['tenancy', 'region', 'user', 'fingerprint', 'key_file', 'pass_phrase']):
            if key in profile:
                line.setText(profile[key])
            else:
                line.setText("")

        if self.main_window:
            self.main_window.change_profile(self.current_profile)

    def create_new_profile(self):
        """
        Layout to create a new profile. Removes the dropdown widget and changes the buttons
        """
        self.layout.removeItem(self.layout.itemAt(0))
        self.dropdown.setParent(None)

        self.new_profile_name = QLineEdit()
        self.new_profile_name.setPlaceholderText("Profile Name")
        self.layout.insertWidget(0, self.new_profile_name)

        self.tenancy.setText("")
        self.region.setText("")
        self.user.setText("")
        self.fingerprint.setText("")
        self.key_file.setText("")
        self.passphrase.setText("")
        self.create_button = QPushButton('Create')
        self.create_button.clicked.connect(self.create_signal)
        self.cancel_button = QPushButton('Cancel')
        self.cancel_button.clicked.connect(self.cancel_signal)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.addButton(self.create_button,
                                 QDialogButtonBox.ActionRole)
        self.buttonBox.addButton(self.cancel_button,
                                 QDialogButtonBox.ActionRole)

        self.layout.removeItem(self.layout.itemAt(7))
        self.save_button.setParent(None)

        self.layout.addWidget(self.buttonBox)

    def create_signal(self):
        """
        Create a new profile with the given information in the LineEdit widgets. Saves to the OCI config file
        """
        profile_name = self.new_profile_name.text()
        self.config[profile_name] = {}
        self.config[profile_name]['tenancy'] = self.tenancy.text()
        self.config[profile_name]['region'] = self.region.text()
        self.config[profile_name]['user'] = self.user.text()
        self.config[profile_name]['fingerprint'] = self.fingerprint.text()
        self.config[profile_name]['key_file'] = self.key_file.text()
        self.config[profile_name]['pass_phrase'] = self.passphrase.text()

        with open(self.DEFAULT_LOCATION, 'w') as configfile:
            self.config.write(configfile)

        self.current_profile = profile_name
        self.cancel_signal()

    def save_signal(self):
        """
        Saves edits on a currently existing profile. Saves to the OCI config file
        """
        self.config[self.current_profile]['tenancy'] = self.tenancy.text()
        self.config[self.current_profile]['region'] = self.region.text()
        self.config[self.current_profile]['user'] = self.user.text()
        self.config[
            self.current_profile]['fingerprint'] = self.fingerprint.text()
        self.config[self.current_profile]['key_file'] = self.key_file.text()
        self.config[
            self.current_profile]['pass_phrase'] = self.passphrase.text()

        with open(self.DEFAULT_LOCATION, 'w') as configfile:
            self.config.write(configfile)

    def cancel_signal(self):
        """
        Cancels the creation a new profile and reverts layout to default layout
        """
        self.layout.removeItem(self.layout.itemAt(0))
        self.new_profile_name.setParent(None)

        self.dropdown = self.get_profiles_dropdown()
        self.layout.insertWidget(0, self.dropdown)

        self.layout.removeItem(self.layout.itemAt(7))
        self.buttonBox.setParent(None)

        self.change_profile(self.current_profile)
        self.dropdown.setCurrentText(self.current_profile)

        self.layout.addWidget(self.save_button)
Ejemplo n.º 15
0
class Form(QDialog):
    def __init__(self, parent=None):
        super(Form, self).__init__(parent)
        self.setWindowTitle('RetroUFO')

        # Create widgets
        self.chkboxPlatformDetect = QCheckBox('Platform Auto-Detect')
        self.chkboxPlatformDetect.setChecked(True)
        self.chkboxPlatformDetect.stateChanged.connect(self.auto_detect)

        self.cmbboxPlatform = QComboBox()
        self.cmbboxPlatform.setEnabled(False)
        self.cmbboxPlatform.setEditable(False)
        self.cmbboxPlatform.addItem('Linux')
        self.cmbboxPlatform.addItem('macOS')
        self.cmbboxPlatform.addItem('Windows')

        self.cmbboxArchitecture = QComboBox()
        self.cmbboxArchitecture.setEnabled(False)
        self.cmbboxArchitecture.setEditable(False)
        self.cmbboxArchitecture.addItem('x86')
        self.cmbboxArchitecture.addItem('x86_64')

        self.chkboxLocationDetect = QCheckBox('Core Location Auto-Detect')
        self.chkboxLocationDetect.setChecked(True)
        self.chkboxLocationDetect.stateChanged.connect(self.auto_location)

        self.leditCoreLocation = QLineEdit('')
        self.leditCoreLocation.setEnabled(False)

        self.btnCoreLocation = QPushButton('...')
        self.btnCoreLocation.setEnabled(False)
        self.btnCoreLocation.clicked.connect(self.choose_location)

        self.teditLog = QTextEdit()
        self.teditLog.setReadOnly(True)

        self.tcsrLog = QTextCursor(self.teditLog.document())

        self.chkboxKeepDownload = QCheckBox('Keep Downloaded Cores')
        self.chkboxKeepDownload.setChecked(False)

        self.btnGrabCores = QPushButton('Grab Cores')
        self.btnGrabCores.clicked.connect(self.grab_cores)

        # Create layout and add widgets
        self.formLayout = QVBoxLayout()
        self.formLayout.addWidget(self.chkboxPlatformDetect)
        self.formLayout.addWidget(self.cmbboxPlatform)
        self.formLayout.addWidget(self.cmbboxArchitecture)
        self.formLayout.addWidget(self.chkboxLocationDetect)
        self.formLayout.addWidget(self.leditCoreLocation)
        self.formLayout.addWidget(self.btnCoreLocation)
        self.formLayout.addWidget(self.teditLog)
        self.formLayout.addWidget(self.chkboxKeepDownload)
        self.formLayout.addWidget(self.btnGrabCores)

        # Set dialog layout
        self.setLayout(self.formLayout)

    def auto_detect(self):
        if self.chkboxPlatformDetect.isChecked():
            self.cmbboxPlatform.setEnabled(False)
            self.cmbboxArchitecture.setEnabled(False)
        else:
            self.cmbboxPlatform.setEnabled(True)
            self.cmbboxArchitecture.setEnabled(True)

    def auto_location(self):
        if self.chkboxLocationDetect.isChecked():
            self.leditCoreLocation.setEnabled(False)
            self.btnCoreLocation.setEnabled(False)
        else:
            self.leditCoreLocation.setEnabled(True)
            self.btnCoreLocation.setEnabled(True)

    def choose_location(self):
        directory = QFileDialog.getExistingDirectory(self,
                                                     'Choose Target Location',
                                                     os.path.expanduser('~'))

        self.leditCoreLocation.insert(directory)

    def update_log(self, _info):
        self.teditLog.insertPlainText('{}\n'.format(_info))

        # Auto scrolling on log UI
        self.teditLog.moveCursor(QTextCursor.End)

    def lock_ui(self, _lock):
        # Cycle through each widget and disable it except for log UI
        widgets = (self.formLayout.itemAt(i).widget()
                   for i in range(self.formLayout.count()))
        for widget in widgets:
            if isinstance(widget, QTextEdit):
                pass
            else:
                widget.setDisabled(_lock)
                # Have to run these to make sure only the correct things unlock after grab thread
                self.auto_detect()
                self.auto_location()

    def grab_cores(self):
        """ Where the magic happens """
        if not self.chkboxKeepDownload.isChecked():
            self.clean_up()

        target_platform = self.get_platform()
        architecture = self.get_architecture()
        location = self.get_location()

        self.grab = GrabThread(target_platform, architecture, location)
        self.grab.add_to_log.connect(self.update_log)
        self.grab.lock.connect(self.lock_ui)
        self.grab.start()

    def get_platform(self):
        """ Gets the Platform and Architecture if not supplied """

        if not self.chkboxPlatformDetect.isChecked():
            if self.cmbboxPlatform.currentText() == 'macOS':
                return 'apple/osx'  # macOS
            else:
                return self.cmbboxPlatform.currentText().lower()
        else:
            if platform.system() == 'Linux':
                return 'linux'
            elif platform.system() == 'Darwin':  # macOS
                return 'apple/osx'
            elif platform.system(
            ) == 'Windows' or 'MSYS_NT' in platform.system(
            ):  # Checks for MSYS environment as well
                return 'windows'
            else:
                msgBox = QMessageBox.warning(
                    self, 'Error', 'Platform not found or supported!',
                    QMessageBox.Ok)
                msgBox.exec_()

    def get_architecture(self):
        """ Gets the Platform and Architecture if not supplied """

        if '64' in platform.architecture()[0]:
            return 'x86_64'

        elif '32' in platform.architecture()[0]:
            return 'x86'
        else:
            msgBox = QMessageBox.warning(
                self, 'Error', 'Architecture not found or supported',
                QMessageBox.Ok)
            msgBox.exec_()

    def get_location(self):
        if not self.chkboxLocationDetect.isChecked():
            return self.leditCoreLocation.text()
        else:
            return CORE_LOCATION[self.get_platform()]

    def clean_up(self):
        """ Removes all the downloaded files """
        if os.path.isdir('cores'):
            rmtree('cores/')
Ejemplo n.º 16
0
class Application(QMainWindow):
    def __init__(self,
                 metafile=None,
                 conversion_module=None,
                 source_paths=None,
                 kwargs_fields=None,
                 extension_modules=None,
                 extension_forms=None,
                 nwbfile_loc=None,
                 conversion_class=None,
                 nwbwidgets=True):
        super().__init__()
        # Dictionary storing source files paths
        self.source_paths = source_paths
        # Path to conversion module .py file
        self.conversion_module_path = conversion_module
        # Dictionary storing custom boolean options (to form checkboxes)
        self.kwargs_fields = kwargs_fields
        # Extension modules
        self.extension_modules = extension_modules
        # Updates name_to_gui_class with extension classes
        self.name_to_gui_class = name_to_gui_class
        if extension_forms:
            self.name_to_gui_class.update(extension_forms)
        # Temporary folder path
        self.temp_dir = tempfile.mkdtemp()
        # default nwbfile save location:
        self.nwbfile_loc = nwbfile_loc
        # conversion_class:
        self.conversion_class = conversion_class

        self.resize(1200, 900)
        self.setWindowTitle('NWB:N conversion tools')

        # Initialize GUI elements
        self.init_gui()
        self.init_meta_tab()
        self.load_meta_file(filename=metafile)
        if nwbwidgets:
            self.init_nwb_explorer()
        self.show()

    def init_gui(self):
        """Initiates GUI elements."""
        mainMenu = self.menuBar()

        fileMenu = mainMenu.addMenu('File')
        action_choose_conversion = QAction('Choose conversion module', self)
        fileMenu.addAction(action_choose_conversion)
        action_choose_conversion.triggered.connect(self.load_conversion_module)

        helpMenu = mainMenu.addMenu('Help')
        action_about = QAction('About', self)
        helpMenu.addAction(action_about)
        action_about.triggered.connect(self.about)

        self.tabs = QTabWidget()
        self.setCentralWidget(self.tabs)

    def init_meta_tab(self):
        # Center panels -------------------------------------------------------
        self.groups_list = []

        # Left-side panel: forms
        self.btn_load_meta = QPushButton('Load metafile')
        self.btn_load_meta.setIcon(self.style().standardIcon(
            QStyle.SP_ArrowDown))
        self.btn_load_meta.clicked.connect(
            lambda: self.load_meta_file(filename=None))
        self.btn_load_meta.setToolTip(
            "The YAML file with metadata for this conversion.\n"
            "You can customize the metadata in the forms below.")
        self.btn_save_meta = QPushButton('Save metafile')
        self.btn_save_meta.setIcon(self.style().standardIcon(
            QStyle.SP_DriveFDIcon))
        self.btn_save_meta.clicked.connect(self.save_meta_file)
        self.btn_run_conversion = QPushButton('Run conversion')
        self.btn_run_conversion.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPlay))
        self.btn_run_conversion.clicked.connect(self.run_conversion)
        self.btn_form_editor = QPushButton('Form -> Editor')
        self.btn_form_editor.clicked.connect(self.form_to_editor)

        self.lbl_nwb_file = QLabel('Output nwb file:')
        self.lbl_nwb_file.setToolTip(
            "Path to the NWB file that will be created.")
        self.lin_nwb_file = QLineEdit('')
        if self.nwbfile_loc is not None:
            self.lin_nwb_file = QLineEdit(self.nwbfile_loc)
        self.btn_nwb_file = QPushButton()
        self.btn_nwb_file.setIcon(self.style().standardIcon(
            QStyle.SP_DialogOpenButton))
        self.btn_nwb_file.clicked.connect(self.load_nwb_file)

        l_grid1 = QGridLayout()
        l_grid1.setColumnStretch(3, 1)
        l_grid1.addWidget(self.btn_load_meta, 0, 0, 1, 1)
        l_grid1.addWidget(self.btn_save_meta, 0, 1, 1, 1)
        l_grid1.addWidget(self.btn_run_conversion, 0, 2, 1, 1)
        l_grid1.addWidget(QLabel(), 0, 3, 1, 1)
        l_grid1.addWidget(self.btn_form_editor, 0, 4, 1, 2)
        l_grid1.addWidget(self.lbl_nwb_file, 1, 0, 1, 1)
        l_grid1.addWidget(self.lin_nwb_file, 1, 1, 1, 3)
        l_grid1.addWidget(self.btn_nwb_file, 1, 4, 1, 1)

        # Adds custom files/dir paths fields
        if self.source_paths is None:
            self.lbl_source_file = QLabel('source files:')
            self.lin_source_file = QLineEdit('')
            self.btn_source_file = QPushButton()
            self.btn_source_file.setIcon(self.style().standardIcon(
                QStyle.SP_DialogOpenButton))
            self.btn_source_file.clicked.connect(self.load_source_files)
            l_grid1.addWidget(self.lbl_source_file, 3, 0, 1, 1)
            l_grid1.addWidget(self.lin_source_file, 3, 1, 1, 3)
            l_grid1.addWidget(self.btn_source_file, 3, 4, 1, 1)
        else:
            self.group_source_paths = QGroupBox('Source paths')
            self.grid_source = QGridLayout()
            self.grid_source.setColumnStretch(3, 1)
            ii = -1
            for k, v in self.source_paths.items():
                ii += 1
                lbl_src = QLabel(k + ':')
                setattr(self, 'lbl_src_' + str(ii), lbl_src)
                lin_src = QLineEdit(v['path'])
                setattr(self, 'lin_src_' + str(ii), lin_src)
                btn_src = QPushButton()
                btn_src.setIcon(self.style().standardIcon(
                    QStyle.SP_DialogOpenButton))
                setattr(self, 'btn_src_' + str(ii), btn_src)
                if v['type'] == 'file':
                    btn_src.clicked.connect(
                        (lambda x: lambda: self.load_source_files(x[0], x[1]))(
                            [ii, k]))
                else:
                    btn_src.clicked.connect(
                        (lambda x: lambda: self.load_source_dir(x[0], x[1]))(
                            [ii, k]))
                self.grid_source.addWidget(lbl_src, ii, 0, 1, 1)
                self.grid_source.addWidget(lin_src, ii, 1, 1, 3)
                self.grid_source.addWidget(btn_src, ii, 4, 1, 1)
            self.group_source_paths.setLayout(self.grid_source)
            l_grid1.addWidget(self.group_source_paths, 3, 0, 1, 6)

        # Adds custom kwargs checkboxes
        if self.kwargs_fields:
            self.group_kwargs = QGroupBox('KWARGS')
            self.grid_kwargs = QGridLayout()
            self.grid_kwargs.setColumnStretch(4, 1)
            ii = -1
            for k, v in self.kwargs_fields.items():
                ii += 1
                chk_kwargs = QCheckBox(k)
                chk_kwargs.setChecked(v)
                chk_kwargs.clicked.connect(
                    (lambda x: lambda: self.update_kwargs(x[0], x[1]))([ii,
                                                                        k]))
                setattr(self, 'chk_kwargs_' + str(ii), chk_kwargs)
                self.grid_kwargs.addWidget(chk_kwargs, ii // 4, ii % 4, 1, 1)
            self.group_kwargs.setLayout(self.grid_kwargs)
            l_grid1.addWidget(self.group_kwargs, 4, 0, 1, 6)

        self.l_vbox1 = QVBoxLayout()
        self.l_vbox1.addStretch()
        scroll_aux = QWidget()
        scroll_aux.setLayout(self.l_vbox1)
        l_scroll = QScrollArea()
        l_scroll.setWidget(scroll_aux)
        l_scroll.setWidgetResizable(True)

        self.l_vbox2 = QVBoxLayout()
        self.l_vbox2.addLayout(l_grid1)
        self.l_vbox2.addWidget(l_scroll)

        # Right-side panel
        # Metadata text
        editor_label = QLabel('Metafile preview:')
        r_grid1 = QGridLayout()
        r_grid1.setColumnStretch(1, 1)
        r_grid1.addWidget(editor_label, 0, 0, 1, 1)
        r_grid1.addWidget(QLabel(), 0, 1, 1, 1)
        self.editor = QTextEdit()
        r_vbox1 = QVBoxLayout()
        r_vbox1.addLayout(r_grid1)
        r_vbox1.addWidget(self.editor)

        # Logger
        log_label = QLabel('Log:')
        r_grid2 = QGridLayout()
        r_grid2.setColumnStretch(1, 1)
        r_grid2.addWidget(log_label, 0, 0, 1, 1)
        r_grid2.addWidget(QLabel(), 0, 1, 1, 1)
        self.logger = QTextEdit()
        self.logger.setReadOnly(True)
        r_vbox2 = QVBoxLayout()
        r_vbox2.addLayout(r_grid2)
        r_vbox2.addWidget(self.logger)

        r_vsplitter = QSplitter(QtCore.Qt.Vertical)
        ru_w = QWidget()
        ru_w.setLayout(r_vbox1)
        rb_w = QWidget()
        rb_w.setLayout(r_vbox2)
        r_vsplitter.addWidget(ru_w)
        r_vsplitter.addWidget(rb_w)

        # Metadata/conversion tab Layout
        self.left_w = QWidget()
        self.left_w.setLayout(self.l_vbox2)
        self.splitter = QSplitter(QtCore.Qt.Horizontal)
        self.splitter.addWidget(self.left_w)
        self.splitter.addWidget(r_vsplitter)

        self.metadata_layout = QVBoxLayout()
        self.metadata_layout.addWidget(self.splitter)
        self.tab_metadata = QWidget()
        self.tab_metadata.setLayout(self.metadata_layout)
        self.tabs.addTab(self.tab_metadata, 'Metadata/Conversion')

        # Background color
        p = self.palette()
        p.setColor(self.backgroundRole(), QtCore.Qt.white)
        self.setPalette(p)

    def init_nwb_explorer(self):
        """Initializes NWB file explorer tab"""
        # Layout Widgets
        self.btn_load_nwbexp = QPushButton('Load NWB')
        self.btn_load_nwbexp.setIcon(self.style().standardIcon(
            QStyle.SP_ArrowDown))
        self.btn_load_nwbexp.clicked.connect(self.load_nwb_explorer)
        self.btn_load_nwbexp.setToolTip("Choose NWB file to explore!")
        self.btn_close_nwbexp = QPushButton('Close')
        self.btn_close_nwbexp.setIcon(self.style().standardIcon(
            QStyle.SP_DialogCloseButton))
        self.btn_close_nwbexp.clicked.connect(self.close_nwb_explorer)
        self.btn_close_nwbexp.setToolTip("Close current file view.")
        self.html = QWebEngineView()

        self.grid_widgets = QGridLayout()
        self.grid_widgets.setColumnStretch(2, 1)
        self.grid_widgets.addWidget(self.btn_load_nwbexp, 0, 0, 1, 1)
        self.grid_widgets.addWidget(self.btn_close_nwbexp, 0, 1, 1, 1)
        self.grid_widgets.addWidget(QLabel(), 0, 2, 1, 1)
        self.vbox_widgets = QVBoxLayout()
        self.vbox_widgets.addLayout(self.grid_widgets)
        self.vbox_widgets.addWidget(self.html)

        # Layout Console
        console_label = QLabel('Ipython console:')
        self.explorer_console = ConsoleWidget(par=self)
        self.explorer_console.setToolTip("nwbfile --> NWB file data")

        self.grid_console = QGridLayout()
        self.grid_console.addWidget(console_label, 0, 0, 1, 1)
        self.grid_console.addWidget(self.explorer_console, 1, 0, 1, 1)

        hsplitter = QSplitter(QtCore.Qt.Horizontal)
        left_w = QWidget()
        left_w.setLayout(self.vbox_widgets)
        right_w = QWidget()
        right_w.setLayout(self.grid_console)
        hsplitter.addWidget(left_w)
        hsplitter.addWidget(right_w)

        # Add tab to GUI
        self.tabs.addTab(hsplitter, 'NWB widgets')

    def write_to_logger(self, txt):
        time = datetime.datetime.now().time().strftime("%H:%M:%S")
        full_txt = "[" + time + "]    " + txt
        self.logger.append(full_txt)

    def run_conversion(self):
        """Runs conversion function."""
        self.write_to_logger('Converting data to NWB... please wait.')
        self.toggle_enable_gui(enable=False)
        self.thread = ConversionFunctionThread(self)
        self.thread.finished.connect(
            lambda: self.finish_conversion(error=self.thread.error))
        self.thread.start()

    def finish_conversion(self, error):
        if error:
            self.write_to_logger('ERROR:')
            self.write_to_logger(str(error))
        else:
            self.write_to_logger('Data successfully converted to NWB.')
        self.toggle_enable_gui(enable=True)

    def toggle_enable_gui(self, enable):
        self.editor.setEnabled(enable)
        self.left_w.setEnabled(enable)

    def save_meta_file(self):
        """Saves metadata to .yml file."""
        filename, _ = QFileDialog.getSaveFileName(self, 'Save file', '',
                                                  "(*.yml);;(*.yaml)")
        if filename:
            data = {}
            for grp in self.groups_list:
                info, error = grp.read_fields()
                if error is None:
                    data[grp.group_type] = info
                else:
                    return
            with open(filename, 'w') as f:
                yaml.dump(data, f, default_flow_style=False)

    def read_metadata_from_form(self):
        """Loads metadata from form."""
        metadata = {}
        for grp in self.groups_list:
            info, error = grp.read_fields()
            if error is None:
                metadata[grp.group_type] = info
            else:
                return
        return metadata

    def form_to_editor(self):
        """Loads data from form to editor."""
        metadata = self.read_metadata_from_form()
        txt = yaml.dump(metadata, default_flow_style=False)
        self.editor.setText(txt)

    def update_kwargs(self, ind, key):
        """Updates the boolean values for keyword arguments."""
        chk_kw = getattr(self, 'chk_kwargs_' + str(ind))
        self.kwargs_fields[key] = chk_kw.isChecked()

    def load_source_files(self, ind, key):
        """Browser to source file location."""
        filenames, ftype = QFileDialog.getOpenFileNames(parent=self,
                                                        caption='Open file',
                                                        directory='',
                                                        filter="(*)")
        if len(filenames):
            all_names = ''
            for fname in filenames:
                all_names += fname + ', '
            lin_src = getattr(self, 'lin_src_' + str(ind))
            lin_src.setText(all_names[:-2])
            self.source_paths[key]['path'] = all_names[:-2]

    def load_source_dir(self, ind, key):
        """Browser to source directory location."""
        dirname = QFileDialog.getExistingDirectory(parent=self,
                                                   caption='Source directory',
                                                   directory='')
        if len(dirname):
            lin_src = getattr(self, 'lin_src_' + str(ind))
            lin_src.setText(dirname)
            self.source_paths[key]['path'] = dirname

    def load_meta_file(self, filename=None):
        """
        Opens (or browsers to) a .yml file containing metadata for NWB. Then:
        1. loads the internal variable self.metadata with the content
        2. writes content to editor
        3. updates forms
        """
        if filename is None:
            filename, ftype = QFileDialog.getOpenFileName(
                parent=self,
                caption='Open file',
                directory='',
                filter="(*.yml);;(*.yaml)")
            if ftype != '(*.yml)' or ftype != '(*.yaml)':
                return
        with open(filename) as f:
            self.metadata = yaml.safe_load(f)
        txt = yaml.dump(self.metadata, default_flow_style=False)
        self.editor.setText(txt)
        self.update_forms()

    def load_conversion_module(self):
        """Browser to conversion script file location."""
        filename, ftype = QFileDialog.getOpenFileName(parent=self,
                                                      caption='Open file',
                                                      directory='',
                                                      filter="(*py)")
        if filename != '':
            self.conversion_module_path = filename

    def load_nwb_file(self):
        """Browser to nwb file location."""
        filename, ftype = QFileDialog.getSaveFileName(parent=self,
                                                      caption='Save file',
                                                      directory='',
                                                      filter="(*nwb)")
        if filename is not None:
            self.lin_nwb_file.setText(filename)

    def load_nwb_explorer(self):
        """Browser to nwb file location."""
        filename, ftype = QFileDialog.getOpenFileName(parent=self,
                                                      caption='Load file',
                                                      directory='',
                                                      filter="(*nwb)")
        if filename != '':
            # Opens file on Ipython console
            self.run_console(fname=filename)
            # Opens file on NWBWidgets
            self.run_voila(fname=filename)

    def close_nwb_explorer(self):
        """Close current NWB file view on explorer"""
        if hasattr(self, 'voilathread'):
            # Stop Voila thread
            self.voilathread.stop()
            # Closes nwb file on console
            self.explorer_console._execute('io.close()', True)
            self.explorer_console.clear()

    def run_console(self, fname):
        """Loads NWB file on Ipython console"""
        # Imports extension modules
        imports_text = ""
        if self.extension_modules:
            for k, v in self.extension_modules.items():
                imports_text += "\nfrom " + k + " import " + ", ".join(v)
        code = """
            import pynwb
            import os
            """ + imports_text + """
            fpath = os.path.join(r'""" + str(fname) + """')
            io = pynwb.NWBHDF5IO(fpath, 'r', load_namespaces=True)
            nwbfile = io.read()
            """
        self.explorer_console._execute(code, True)
        self.explorer_console.clear()
        self.explorer_console.print_text('nwbfile --> Loaded NWB file\n')

    def run_voila(self, fname):
        """Set up notebook and run it with a dedicated Voila thread."""
        # Stop any current Voila thread
        self.close_nwb_explorer()
        # Write Figure + ipywidgets to a .ipynb file
        nb = nbf.v4.new_notebook()
        # Imports extension modules
        imports_text = ""
        if self.extension_modules:
            for k, v in self.extension_modules.items():
                imports_text += "\nfrom " + k + " import " + ", ".join(v)
        code = """
            from nwbwidgets import nwb2widget
            import pynwb
            import os
            """ + imports_text + """
            fpath = os.path.join(r'""" + str(fname) + """')
            io = pynwb.NWBHDF5IO(fpath, 'r', load_namespaces=True)
            nwb = io.read()
            nwb2widget(nwb)
            """
        nb['cells'] = [nbf.v4.new_code_cell(code)]
        nbpath = os.path.join(self.temp_dir, Path(fname).stem + '.ipynb')
        nbf.write(nb, nbpath)
        # Run instance of Voila with the just saved .ipynb file
        port = get_free_port()
        self.voilathread = voilaThread(parent=self, port=port, nbpath=nbpath)
        self.voilathread.start()
        # Load Voila instance on GUI
        self.update_html(url='http://localhost:' + str(port))
        # self.parent.write_to_logger(txt=self.name + " ready!")

    def update_html(self, url):
        """Loads temporary HTML file and render it."""
        self.html.load(QtCore.QUrl(url))
        self.html.show()

    def clean_groups(self):
        """Removes all groups widgets."""
        for grp in self.groups_list:
            nWidgetsVbox = self.l_vbox1.count()
            for i in range(nWidgetsVbox):
                if self.l_vbox1.itemAt(i) is not None:
                    if grp == self.l_vbox1.itemAt(i).widget():
                        self.l_vbox1.itemAt(i).widget().setParent(
                            None)  # deletes widget
        self.groups_list = []  # deletes all list items

    def update_forms(self):
        """Updates forms fields with values in metadata."""
        self.clean_groups()
        for grp in self.metadata:
            if grp == 'NWBFile':
                item = GroupNwbfile(parent=self,
                                    metadata=self.metadata['NWBFile'])
                item.write_fields(data=self.metadata['NWBFile'])
                self.groups_list.append(item)
                self.l_vbox1.addWidget(item)
            if grp == 'Subject':
                item = GroupSubject(parent=self)
                item.write_fields(data=self.metadata['Subject'])
                self.groups_list.append(item)
                self.l_vbox1.addWidget(item)
            if grp == 'Ophys':
                item = GroupOphys(self)
                for subgroup in self.metadata[grp]:
                    # if many items of same class, in list
                    if isinstance(self.metadata[grp][subgroup], list):
                        for subsub in self.metadata[grp][subgroup]:
                            item.add_group(
                                group=self.name_to_gui_class[subgroup](
                                    parent=item),
                                metadata=subsub)
                    else:  # if it's just one item of this class
                        item.add_group(group=self.name_to_gui_class[subgroup](
                            parent=item),
                                       metadata=self.metadata[grp][subgroup])
                self.groups_list.append(item)
                self.l_vbox1.addWidget(item)
            if grp == 'Ecephys':
                item = GroupEcephys(self)
                for subgroup in self.metadata[grp]:
                    # if many items of same class, in list
                    if isinstance(self.metadata[grp][subgroup], list):
                        for subsub in self.metadata[grp][subgroup]:
                            item.add_group(
                                group=self.name_to_gui_class[subgroup](
                                    parent=item),
                                metadata=subsub)
                    else:  # if it's just one item of this class
                        item.add_group(group=self.name_to_gui_class[subgroup](
                            parent=item),
                                       metadata=self.metadata[grp][subgroup])
                self.groups_list.append(item)
                self.l_vbox1.addWidget(item)
            if grp == 'Behavior':
                item = GroupBehavior(self)
                for subgroup in self.metadata[grp]:
                    # if many items of same class, in list
                    if isinstance(self.metadata[grp][subgroup], list):
                        for subsub in self.metadata[grp][subgroup]:
                            item.add_group(
                                group=self.name_to_gui_class[subgroup](
                                    parent=item),
                                metadata=subsub)
                    else:  # if it's just one item of this class
                        item.add_group(group=self.name_to_gui_class[subgroup](
                            parent=item),
                                       metadata=self.metadata[grp][subgroup])
                self.groups_list.append(item)
                self.l_vbox1.addWidget(item)
            if grp == 'Ogen':
                item = GroupOgen(self)
                for subgroup in self.metadata[grp]:
                    # if many items of same class, in list
                    if isinstance(self.metadata[grp][subgroup], list):
                        for subsub in self.metadata[grp][subgroup]:
                            item.add_group(
                                group=self.name_to_gui_class[subgroup](
                                    parent=item),
                                metadata=subsub)
                    else:  # if it's just one item of this class
                        item.add_group(group=self.name_to_gui_class[subgroup](
                            parent=item),
                                       metadata=self.metadata[grp][subgroup])
                self.groups_list.append(item)
                self.l_vbox1.addWidget(item)
        nItems = self.l_vbox1.count()
        self.l_vbox1.addStretch(nItems)

    def about(self):
        """About dialog."""
        msg = QMessageBox()
        msg.setWindowTitle("About NWB conversion")
        msg.setIcon(QMessageBox.Information)
        msg.setText("Version: 0.1.0 \n"
                    "GUI for NWB conversion and exploration.\n ")
        msg.setInformativeText(
            "<a href='https://github.com/catalystneuro/nwb-qt-gui'>NWBQtGUI Github page</a>"
        )
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()

    def closeEvent(self, event):
        """Before exiting, executes these actions."""
        # Stop any current Voila thread
        self.close_nwb_explorer()
        # Remove any remaining temporary directory/files
        shutil.rmtree(self.temp_dir, ignore_errors=False, onerror=None)
        event.accept()
Ejemplo n.º 17
0
class NodeChoiceWidget(QWidget):
    def __init__(self, flow, nodes):
        super(NodeChoiceWidget, self).__init__()

        self.flow = flow

        self.all_nodes = sort_nodes(nodes)  # copy, no ref

        self.nodes = []

        # 'current_nodes' are the currently selectable ones, they get recreated every time update_view() is called
        self.current_nodes = []
        self.all_current_node_widgets = []
        self.active_node_widget_index = -1
        self.active_node_widget = None

        self.reset_list()

        self.node_widget_index_counter = 0

        self.setMinimumWidth(260)
        self.setFixedHeight(450)

        self.main_layout = QVBoxLayout(self)
        self.main_layout.setAlignment(Qt.AlignTop)
        self.setLayout(self.main_layout)

        # adding all stuff to the layout
        self.search_line_edit = QLineEdit(self)
        self.search_line_edit.setPlaceholderText('search for node...')
        self.search_line_edit.textChanged.connect(self.update_view)
        self.layout().addWidget(self.search_line_edit)

        self.list_scroll_area = QScrollArea(self)
        self.list_scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.list_scroll_area.setHorizontalScrollBarPolicy(
            Qt.ScrollBarAsNeeded)
        self.list_scroll_area.setWidgetResizable(True)

        self.list_scroll_area_widget = QWidget()
        self.list_scroll_area.setWidget(self.list_scroll_area_widget)

        self.list_layout = QVBoxLayout()
        self.list_layout.setAlignment(Qt.AlignTop)
        self.list_scroll_area_widget.setLayout(self.list_layout)

        self.layout().addWidget(self.list_scroll_area)

        self.setFixedHeight(400)

        self.update_view('')

        try:
            f = open('resources/stylesheets/dark_node_choice_widget.txt')
            self.setStyleSheet(f.read())
            f.close()
        except FileNotFoundError:
            pass

        self.search_line_edit.setFocus()

    def mousePressEvent(self, event):
        QWidget.mousePressEvent(self, event)
        event.accept()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.flow.hide_node_choice_widget()

        if event.key() == Qt.Key_Down:
            index = self.active_node_widget_index+1 if \
                self.active_node_widget_index+1 < len(self.all_current_node_widgets) else 0
            self.set_active_node_widget_index(index)
        if event.key() == Qt.Key_Up:
            index = self.active_node_widget_index-1 if \
                self.active_node_widget_index-1 > -1 else len(self.all_current_node_widgets)-1
            self.set_active_node_widget_index(index)

        if event.key() == Qt.Key_Return:
            if len(self.all_current_node_widgets) > 0:
                self.place_node(self.active_node_widget_index)

    def wheelEvent(self, event):
        QWidget.wheelEvent(self, event)
        event.accept()

    def refocus(self):
        self.search_line_edit.setFocus()
        self.search_line_edit.selectAll()

    def update_list(self, nodes):
        self.nodes = sort_nodes(nodes)

    def reset_list(self):
        self.nodes = self.all_nodes

    def update_view(self, text=''):
        text = text.lower()
        for i in reversed(range(self.list_layout.count())):
            self.list_layout.itemAt(i).widget().setParent(None)

        self.current_nodes.clear()
        self.all_current_node_widgets.clear()

        self.node_widget_index_counter = 0

        # select valid elements from text
        # nodes
        nodes_names_dict = {}
        for n in self.nodes:
            nodes_names_dict[n] = n.title.lower()
        sorted_indices = self.get_sorted_dict_matching_search(
            nodes_names_dict, text)
        for n, index in sorted_indices.items():
            self.current_nodes.append(n)

        # nodes
        if len(self.current_nodes) > 0:
            nodes_label = QLabel('Hover for description')
            nodes_label_font = QFont('Poppins')
            nodes_label_font.setPixelSize(15)
            nodes_label.setStyleSheet('color: #9bbf9dff; border: none;')
            nodes_label.setFont(nodes_label_font)
            self.list_layout.addWidget(nodes_label)

            for n in self.current_nodes:
                node_widget = self.create_list_item_widget(n)
                self.list_layout.addWidget(node_widget)
                self.all_current_node_widgets.append(node_widget)

        if len(self.all_current_node_widgets) > 0:
            self.set_active_node_widget_index(0)

        # self.setFixedWidth(self.minimumWidth())
        # print(self.list_scroll_area_widget.width())

    def get_sorted_dict_matching_search(self, items_dict, text):
        indices_dict = {}
        for item, name in items_dict.items(
        ):  # the strings are already lowered here
            Debugger.debug(item, name, text)
            if name.__contains__(text):
                index = name.index(text)
                indices_dict[item] = index
        return {
            k: v
            for k, v in sorted(indices_dict.items(), key=lambda i: i[1])
        }

    def create_list_item_widget(self, node):
        node_widget = NodeWidget(self, node)  # , self.node_images[node])
        node_widget.custom_focused_from_inside.connect(
            self.node_widget_focused_from_inside)
        node_widget.setObjectName('node_widget_' +
                                  str(self.node_widget_index_counter))
        self.node_widget_index_counter += 1
        node_widget.chosen.connect(self.node_widget_chosen)

        return node_widget

    def node_widget_focused_from_inside(self):
        self.set_active_node_widget_index(
            self.all_current_node_widgets.index(self.sender()))

    def set_active_node_widget_index(self, index):
        self.active_node_widget_index = index
        node_widget = self.all_current_node_widgets[index]

        if self.active_node_widget:
            self.active_node_widget.set_custom_focus(False)

        node_widget.set_custom_focus(True)
        self.active_node_widget = node_widget
        self.list_scroll_area.ensureWidgetVisible(self.active_node_widget)

    def node_widget_chosen(
        self
    ):  # gets called when the user clicks on a node widget with the mouse
        self.flow.ignore_mouse_event = True  # otherwise, it will receive a mouse press event

        index = int(
            self.sender().objectName()[self.sender().objectName().rindex('_') +
                                       1:])
        self.place_node(index)

    def place_node(self, index):
        node_index = index
        node = self.current_nodes[node_index]
        self.flow.place_node__cmd(node)

        self.flow.hide_node_choice_widget()
Ejemplo n.º 18
0
class QGroundObjectMenu(QDialog):

    changed = QtCore.Signal()

    def __init__(self, parent, ground_object: TheaterGroundObject,
                 buildings: [], cp: ControlPoint, game: Game):
        super(QGroundObjectMenu, self).__init__(parent)
        self.setMinimumWidth(350)
        self.ground_object = ground_object
        self.buildings = buildings
        self.cp = cp
        self.game = game
        self.setWindowTitle("Location " + self.ground_object.obj_name)
        self.setWindowIcon(EVENT_ICONS["capture"])
        self.intelBox = QGroupBox("Units :")
        self.buildingBox = QGroupBox("Buildings :")
        self.intelLayout = QGridLayout()
        self.buildingsLayout = QGridLayout()
        self.sell_all_button = None
        self.total_value = 0
        self.init_ui()

    def init_ui(self):

        self.mainLayout = QVBoxLayout()
        self.budget = QBudgetBox(self.game)
        self.budget.setGame(self.game)

        self.doLayout()

        if self.ground_object.dcs_identifier == "AA":
            self.mainLayout.addWidget(self.intelBox)
        else:
            self.mainLayout.addWidget(self.buildingBox)

        self.actionLayout = QHBoxLayout()

        self.sell_all_button = QPushButton("Disband (+" +
                                           str(self.total_value) + "M)")
        self.sell_all_button.clicked.connect(self.sell_all)
        self.sell_all_button.setProperty("style", "btn-danger")

        self.buy_replace = QPushButton("Buy/Replace")
        self.buy_replace.clicked.connect(self.buy_group)
        self.buy_replace.setProperty("style", "btn-success")

        if self.total_value > 0:
            self.actionLayout.addWidget(self.sell_all_button)
        self.actionLayout.addWidget(self.buy_replace)

        if self.cp.captured and self.ground_object.dcs_identifier == "AA":
            self.mainLayout.addLayout(self.actionLayout)
        self.setLayout(self.mainLayout)

    def doLayout(self):

        self.update_total_value()
        self.intelBox = QGroupBox("Units :")
        self.intelLayout = QGridLayout()
        i = 0
        for g in self.ground_object.groups:
            if not hasattr(g, "units_losts"):
                g.units_losts = []
            for u in g.units:
                self.intelLayout.addWidget(
                    QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) +
                           "</b>"), i, 0)
                i = i + 1

            for u in g.units_losts:

                utype = unit_type_of(u)
                if utype in PRICES:
                    price = PRICES[utype]
                else:
                    price = 6

                self.intelLayout.addWidget(
                    QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) +
                           "</b> [DEAD]"), i, 0)
                if self.cp.captured:
                    repair = QPushButton("Repair [" + str(price) + "M]")
                    repair.setProperty("style", "btn-success")
                    repair.clicked.connect(
                        lambda u=u, g=g, p=price: self.repair_unit(g, u, p))
                    self.intelLayout.addWidget(repair, i, 1)
                i = i + 1
        stretch = QVBoxLayout()
        stretch.addStretch()
        self.intelLayout.addLayout(stretch, i, 0)

        self.buildingBox = QGroupBox("Buildings :")
        self.buildingsLayout = QGridLayout()
        j = 0
        for i, building in enumerate(self.buildings):
            if building.dcs_identifier not in FORTIFICATION_BUILDINGS:
                self.buildingsLayout.addWidget(
                    QBuildingInfo(building, self.ground_object), j / 3, j % 3)
                j = j + 1

        self.buildingBox.setLayout(self.buildingsLayout)
        self.intelBox.setLayout(self.intelLayout)

    def do_refresh_layout(self):
        try:
            for i in range(self.mainLayout.count()):
                item = self.mainLayout.itemAt(i)
                if item is not None and item.widget() is not None:
                    item.widget().setParent(None)
            self.sell_all_button.setParent(None)
            self.buy_replace.setParent(None)
            self.actionLayout.setParent(None)

            self.doLayout()
            if self.ground_object.dcs_identifier == "AA":
                self.mainLayout.addWidget(self.intelBox)
            else:
                self.mainLayout.addWidget(self.buildingBox)

            self.actionLayout = QHBoxLayout()
            if self.total_value > 0:
                self.actionLayout.addWidget(self.sell_all_button)
            self.actionLayout.addWidget(self.buy_replace)

            if self.cp.captured and self.ground_object.dcs_identifier == "AA":
                self.mainLayout.addLayout(self.actionLayout)

        except Exception as e:
            print(e)
        self.update_total_value()
        self.changed.emit()

    def update_total_value(self):
        total_value = 0
        for group in self.ground_object.groups:
            for u in group.units:
                utype = unit_type_of(u)
                if utype in PRICES:
                    total_value = total_value + PRICES[utype]
                else:
                    total_value = total_value + 1
        if self.sell_all_button is not None:
            self.sell_all_button.setText("Disband (+$" +
                                         str(self.total_value) + "M)")
        self.total_value = total_value

    def repair_unit(self, group, unit, price):
        if self.game.budget > price:
            self.game.budget -= price
            group.units_losts = [
                u for u in group.units_losts if u.id != unit.id
            ]
            group.units.append(unit)
            GameUpdateSignal.get_instance().updateGame(self.game)

            # Remove destroyed units in the vicinity
            destroyed_units = self.game.get_destroyed_units()
            for d in destroyed_units:
                p = Point(d["x"], d["z"])
                if p.distance_to_point(unit.position) < 15:
                    destroyed_units.remove(d)
                    logging.info("Removed destroyed units " + str(d))
            logging.info("Repaired unit : " + str(unit.id) + " " +
                         str(unit.type))

        self.do_refresh_layout()
        self.changed.emit()

    def sell_all(self):
        self.update_total_value()
        self.game.budget = self.game.budget + self.total_value
        self.ground_object.groups = []
        self.do_refresh_layout()
        GameUpdateSignal.get_instance().updateBudget(self.game)

    def buy_group(self):
        self.subwindow = QBuyGroupForGroundObjectDialog(
            self, self.ground_object, self.cp, self.game, self.total_value)
        self.subwindow.changed.connect(self.do_refresh_layout)
        self.subwindow.show()
Ejemplo n.º 19
0
class QGroundObjectMenu(QDialog):
    def __init__(
        self,
        parent,
        ground_object: TheaterGroundObject,
        cp: ControlPoint,
        game: Game,
    ):
        super().__init__(parent)
        self.setMinimumWidth(350)
        self.ground_object = ground_object
        self.cp = cp
        self.game = game
        self.setWindowTitle(
            f"Location - {self.ground_object.obj_name} ({self.cp.name})"
        )
        self.setWindowIcon(EVENT_ICONS["capture"])
        self.intelBox = QGroupBox("Units :")
        self.buildingBox = QGroupBox("Buildings :")
        self.orientationBox = QGroupBox("Orientation :")
        self.intelLayout = QGridLayout()
        self.buildingsLayout = QGridLayout()
        self.sell_all_button = None
        self.total_value = 0
        self.init_ui()

    def init_ui(self):

        self.mainLayout = QVBoxLayout()
        self.budget = QBudgetBox(self.game)
        self.budget.setGame(self.game)

        self.doLayout()

        if isinstance(self.ground_object, BuildingGroundObject):
            self.mainLayout.addWidget(self.buildingBox)
            if self.cp.captured:
                self.mainLayout.addWidget(self.financesBox)
        else:
            self.mainLayout.addWidget(self.intelBox)
            self.mainLayout.addWidget(self.orientationBox)

        self.actionLayout = QHBoxLayout()

        self.sell_all_button = QPushButton("Disband (+" + str(self.total_value) + "M)")
        self.sell_all_button.clicked.connect(self.sell_all)
        self.sell_all_button.setProperty("style", "btn-danger")

        self.buy_replace = QPushButton("Buy/Replace")
        self.buy_replace.clicked.connect(self.buy_group)
        self.buy_replace.setProperty("style", "btn-success")

        if self.ground_object.purchasable:
            if self.total_value > 0:
                self.actionLayout.addWidget(self.sell_all_button)
            self.actionLayout.addWidget(self.buy_replace)

        if self.cp.captured and self.ground_object.purchasable:
            self.mainLayout.addLayout(self.actionLayout)
        self.setLayout(self.mainLayout)

    def doLayout(self):

        self.update_total_value()
        self.intelBox = QGroupBox("Units :")
        self.intelLayout = QGridLayout()
        i = 0
        for g in self.ground_object.groups:
            for unit in g.units:
                self.intelLayout.addWidget(
                    QLabel(f"<b>Unit {str(unit.display_name)}</b>"), i, 0
                )

                if not unit.alive and unit.repairable and self.cp.captured:
                    price = unit.unit_type.price if unit.unit_type else 0
                    repair = QPushButton(f"Repair [{price}M]")
                    repair.setProperty("style", "btn-success")
                    repair.clicked.connect(
                        lambda u=unit, p=price: self.repair_unit(u, p)
                    )
                    self.intelLayout.addWidget(repair, i, 1)
                i += 1

        stretch = QVBoxLayout()
        stretch.addStretch()
        self.intelLayout.addLayout(stretch, i, 0)

        self.buildingBox = QGroupBox("Buildings :")
        self.buildingsLayout = QGridLayout()

        j = 0
        total_income = 0
        received_income = 0
        for static in self.ground_object.statics:
            if static not in FORTIFICATION_BUILDINGS:
                self.buildingsLayout.addWidget(
                    QBuildingInfo(static, self.ground_object), j / 3, j % 3
                )
                j = j + 1

            if self.ground_object.category in REWARDS.keys():
                total_income += REWARDS[self.ground_object.category]
                if static.alive:
                    received_income += REWARDS[self.ground_object.category]
            else:
                logging.warning(self.ground_object.category + " not in REWARDS")

        self.financesBox = QGroupBox("Finances: ")
        self.financesBoxLayout = QGridLayout()
        self.financesBoxLayout.addWidget(
            QLabel("Available: " + str(total_income) + "M"), 2, 1
        )
        self.financesBoxLayout.addWidget(
            QLabel("Receiving: " + str(received_income) + "M"), 2, 2
        )

        # Orientation Box
        self.orientationBox = QGroupBox("Orientation :")
        self.orientationBoxLayout = QHBoxLayout()

        heading_image = QLabel()
        heading_image.setPixmap(
            ICONS["heading"].transformed(
                QTransform().rotate(self.ground_object.heading.degrees)
            )
        )
        self.orientationBoxLayout.addWidget(heading_image)
        self.headingLabel = QLabel("Heading:")
        self.orientationBoxLayout.addWidget(self.headingLabel)
        self.headingSelector = QSpinBox()
        self.headingSelector.setEnabled(False)  # Disable for now
        self.headingSelector.setMinimum(0)
        self.headingSelector.setMaximum(360)
        self.headingSelector.setValue(self.ground_object.heading.degrees)
        self.orientationBoxLayout.addWidget(self.headingSelector)
        if self.cp.captured:
            # TODO Let the user choose the heading with the SpinBox
            self.headingSelector.setEnabled(False)
            self.head_to_conflict_button = QPushButton("Head to conflict")
            heading = (
                self.game.theater.heading_to_conflict_from(self.ground_object.position)
                or self.ground_object.heading
            )
            self.head_to_conflict_button.clicked.connect(
                lambda: self.rotate_tgo(heading)
            )
            self.orientationBoxLayout.addWidget(self.head_to_conflict_button)
        else:
            self.headingSelector.setEnabled(False)

        # Set the layouts
        self.financesBox.setLayout(self.financesBoxLayout)
        self.buildingBox.setLayout(self.buildingsLayout)
        self.intelBox.setLayout(self.intelLayout)
        self.orientationBox.setLayout(self.orientationBoxLayout)

    def do_refresh_layout(self):
        try:
            for i in reversed(range(self.mainLayout.count())):
                item = self.mainLayout.itemAt(i)
                if item is not None and item.widget() is not None:
                    item.widget().setParent(None)
            self.sell_all_button.setParent(None)
            self.buy_replace.setParent(None)
            self.actionLayout.setParent(None)

            self.doLayout()
            if isinstance(self.ground_object, BuildingGroundObject):
                self.mainLayout.addWidget(self.buildingBox)
            else:
                self.mainLayout.addWidget(self.intelBox)
                self.mainLayout.addWidget(self.orientationBox)

            self.actionLayout = QHBoxLayout()
            if self.total_value > 0:
                self.actionLayout.addWidget(self.sell_all_button)
            self.actionLayout.addWidget(self.buy_replace)

            if self.cp.captured and self.ground_object.purchasable:
                self.mainLayout.addLayout(self.actionLayout)
        except Exception as e:
            logging.exception(e)
        self.update_total_value()

    def update_total_value(self):
        if not self.ground_object.purchasable:
            return
        self.total_value = self.ground_object.value
        if self.sell_all_button is not None:
            self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)")

    def repair_unit(self, unit, price):
        if self.game.blue.budget > price:
            self.game.blue.budget -= price
            unit.alive = True
            GameUpdateSignal.get_instance().updateGame(self.game)

            # Remove destroyed units in the vicinity
            destroyed_units = self.game.get_destroyed_units()
            for d in destroyed_units:
                p = Point(d["x"], d["z"], self.game.theater.terrain)
                if p.distance_to_point(unit.position) < 15:
                    destroyed_units.remove(d)
                    logging.info("Removed destroyed units " + str(d))
            logging.info(f"Repaired unit: {unit.unit_name}")

        self.update_game()

    def rotate_tgo(self, heading: Heading) -> None:
        self.ground_object.rotate(heading)
        self.do_refresh_layout()

    def sell_all(self):
        self.update_total_value()
        self.game.blue.budget += self.total_value
        self.ground_object.groups = []
        self.update_game()

    def buy_group(self) -> None:
        self.subwindow = QGroundObjectBuyMenu(
            self, self.ground_object, self.game, self.total_value
        )
        if self.subwindow.exec_():
            self.update_game()

    def update_game(self) -> None:
        events = GameUpdateEvents()
        events.update_tgo(self.ground_object)
        if any(
            package.target == self.ground_object
            for package in self.game.ato_for(player=False).packages
        ):
            # Replan if the tgo was a target of the redfor
            self.game.initialize_turn(events, for_red=True, for_blue=False)
        EventStream.put_nowait(events)
        GameUpdateSignal.get_instance().updateGame(self.game)
        # Refresh the dialog
        self.do_refresh_layout()