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)
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)
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)
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)
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()
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)
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()
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
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()
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)
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()
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)
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)
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/')
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()
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()
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()
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()