class TabLayout(QWidget): def __init__(self, toplabel): super().__init__() ## Get label from ctor args self.label = QLabel(toplabel) ## Create and bind spinbox to update function self.spinbox = QSpinBox() self.spinbox.setMinimum(1) self.spinbox.valueChanged.connect(self.update) ## Keep track of how many elements we have in sublayout 2 self.counter = 1 ## Layouts self.toplayout = QVBoxLayout() self.sublayout1 = QHBoxLayout() self.sublayout2 = QHBoxLayout() ## Build the first line with label and spinbox self.sublayout1.addWidget(self.label) self.sublayout1.addWidget(self.spinbox) ## Build the entity container self.sublayout2.addWidget( EntityData() ) ## Build top level layout self.toplayout.addLayout(self.sublayout1) self.toplayout.addLayout(self.sublayout2) ## Display self.setLayout(self.toplayout) def update(self): if self.spinbox.value() > self.counter: for i in range( 0, self.spinbox.value() - self.counter ): self.sublayout2.addWidget( EntityData() ) elif self.spinbox.value() < self.counter: # Delete all widgets while self.sublayout2.count() > self.spinbox.value(): item = self.sublayout2.takeAt( self.sublayout2.count() - 1 ) if not item: continue w = item.widget() if w: w.setParent(None) # Recreate all widgets #for i in range( 0, self.spinbox.value() ): # self.sublayout2.addWidget( EntityData() ) self.sublayout2.update() self.toplayout.update() self.counter = self.spinbox.value()
def _init_option_ui(self): label_start_time = QLabel("Start time: ") self._line_edit_start_time = QLineEdit() label_end_time = QLabel("End time: ") self._line_edit_end_time = QLineEdit() label_duration = QLabel("Duration: ") self._line_edit_duration = QLineEdit() label_num_clip = QLabel("No. of clips: ") self._line_edit_num_clip = QLineEdit() button_preview = QPushButton("Preview") button_preview.clicked.connect(self.button_preview_clicked) hbox = QHBoxLayout() hbox.addWidget(label_start_time) hbox.addWidget(self._line_edit_start_time) hbox.addWidget(label_end_time) hbox.addWidget(self._line_edit_end_time) hbox.addWidget(label_duration) hbox.addWidget(self._line_edit_duration) hbox.addWidget(label_num_clip) hbox.addWidget(self._line_edit_num_clip) hbox.addWidget(button_preview) for i in range(hbox.count() - 1): hbox.itemAt(i).widget().setObjectName("option") self.vbox.addLayout(hbox)
def _init_browse_ui(self): label_file_name = QLabel("File name: ") self._list_widget_file_names = QListWidget() self._list_widget_file_names.setFixedHeight(100) button_browse = QPushButton("Browse") button_browse.clicked.connect(self.button_browse_clicked) button_add = QPushButton("Add") button_add.clicked.connect(self.button_add_clicked) button_remove = QPushButton("Remove") button_remove.clicked.connect(self.button_remove_clicked) vbox = QVBoxLayout() vbox.addWidget(button_browse) vbox.addWidget(button_add) vbox.addWidget(button_remove) hbox = QHBoxLayout() hbox.addWidget(label_file_name) hbox.addWidget(self._list_widget_file_names) hbox.addLayout(vbox) for i in range(hbox.count()): hbox.setAlignment(hbox.itemAt(i).widget(), Qt.AlignTop) self.vbox.addLayout(hbox)
class Page(QWidget): Hiding = QtCore.pyqtSignal(int) Showing = QtCore.pyqtSignal(int) def __init__(self, index: int, page_name: str, parent: QWidget = None): super(Page, self).__init__(parent) self.myIndex: int = index self.setObjectName(page_name) self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding) self.setContentsMargins(0, 0, 0, 0) self.hboxlayout = QHBoxLayout(self) self.hboxlayout.setContentsMargins(0, 0, 0, 0) self.hboxlayout.setSpacing(0) self.setLayout(self.hboxlayout) scroll_area = QScrollArea(self) scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding) scroll_area.setFrameShape(QFrame.NoFrame) scroll_area.setFrameShadow(QFrame.Plain) scroll_area.setLineWidth(0) scroll_area.setWidgetResizable(True) scroll_area.installEventFilter(TTScroller(scroll_area)) self.inner_area = QFrame() self.inner_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored) self.inner_area.setProperty("TTPage", QtCore.QVariant(True)) self.inner_area.setContentsMargins(0, 0, 0, 0) self.inner_area.setFrameShape(QFrame.Box) self.inner_area.setLineWidth(0) self.innerLayout = QHBoxLayout(self.inner_area) self.innerLayout.setContentsMargins(0, 0, 0, 0) self.innerLayout.setSpacing(2) self.inner_area.setLayout(self.innerLayout) spacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum) self.innerLayout.addItem(spacer) scroll_area.setWidget(self.inner_area) self.hboxlayout.addWidget(scroll_area) def add_group(self, name: str): grp = group.Group(name, self.inner_area) self.innerLayout.insertWidget(self.innerLayout.count() - 1, grp) parent_toolbar = tabtoolbar.find_tabtoolbar(self) # type: ignore[attr-defined] if not parent_toolbar: raise Exception("Could not find Parent Tabtoolbar") parent_toolbar.adjust_verticalsize(grp.height()) return grp def hide(self): self.Hiding.emit(self.myIndex) def show(self): self.Showing.emit(self.myIndex)
def __init__(self, options, parent=None): super().__init__(parent) self.options = options self.setWindowFlags(Qt.Dialog | Qt.WindowTitleHint | Qt.WindowCloseButtonHint) topLayout = QVBoxLayout(self) self.setLayout(topLayout) columnLayout = QHBoxLayout() topLayout.addLayout(columnLayout) rowLayout = QVBoxLayout() columnLayout.addLayout(rowLayout) groupBox = None for option in self.options.values(): if option.columnNum > columnLayout.count() - 1: rowLayout = QVBoxLayout() columnLayout.addLayout(rowLayout) if not groupBox or groupBox.title() != option.category: groupBox = QGroupBox(option.category) rowLayout.addWidget(groupBox) QGridLayout(groupBox) option.addDialogControl(groupBox) ctrlLayout = QHBoxLayout() topLayout.addLayout(ctrlLayout) ctrlLayout.addStretch(0) okButton = QPushButton(_('&OK')) ctrlLayout.addWidget(okButton) okButton.clicked.connect(self.accept) cancelButton = QPushButton(_('&Cancel')) ctrlLayout.addWidget(cancelButton) cancelButton.clicked.connect(self.reject)
class ShipSlot(QWidget): sloticon_table = None type_table = {1: 'MainCanonLight', 2: 'MainCanonMedium', 3: 'MainCanonHeavy', 4: 'SecondaryCanon', 5: 'Torpedo', 6: 'Fighter', 7: 'DiveBomber', 8: 'TorpedoBomber', 9: 'ReconPlane', 10:'ReconSeaplane', 11:'Rader', 12:'AAShell', 13:'APShell', 14:'DamageControl', 15:'AAGun', 16:'HighAngleGun', 17:'ASW', 18:'Soner', 19:'EngineImprovement', 20:'LandingCraft', 21:'Autogyro', 22:'ArtillerySpotter', 23:'AntiTorpedoBulge', 24:'SearchLight', 25:'DrumCanister', 26:'Facility', 27:'Flare', 28:'FleetCommandFacility', 29:'MaintenancePersonnel', 30:'AntiAircraftFireDirector', 31:'RocketLauncher', 32:'SurfaceShipPersonnel', 33:'FlyingBoat' } def __init__(self,parent): super(ShipSlot, self).__init__(parent) if self.sloticon_table is None: self.sloticon_table = slotitem.create_sloticontable() self.box = QHBoxLayout() self.box.setSpacing(3) self.box.setContentsMargins(0,5,0,5) self.setLayout(self.box) def set_slot(self, types): # remove all icon for i in reversed(list(range(self.box.count()))): self.box.itemAt(i).widget().setParent(None) for t in types: type_name = self.type_table.get(t, 'unknown') if not type_name in self.sloticon_table: type_name = 'unknown' self.box.addWidget(slotitem.IconBox(self.sloticon_table[type_name]))
class MenuBar(QTabWidget): def __init__(self, parent=None): super(MenuBar, self).__init__(parent) tabbar = TabBar(parent) self.setTabBar(tabbar) self.setMinimumHeight(135) self.setMouseTracking(True) self._drop = False self.currentChanged.connect(self.currentChangedFunc) def currentChangedFunc(self, index): tab_text = self.tabText(index) menu = self.findChild(MenuWidget, tab_text) self.anim = QPropertyAnimation(menu, b'_height') self.anim.setDuration(100) self.anim.setStartValue(0) self.anim.setEndValue(100) self.anim.start() def addMenu(self, p_str): p_str = " {p_str} ".format(p_str=p_str) menu = MenuWidget() menu.setObjectName(p_str) self.addTab(menu, p_str) self.hlayout = QHBoxLayout(menu) self.hlayout.setObjectName(p_str) self.hlayout.setContentsMargins(0, 0, 0, 0) self.hlayout.setSpacing(0) hs = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.hlayout.addItem(hs) return (menu) def addGroup(self, p_str, menu): group = GroupWidget(p_str, menu) group.setObjectName('group') insert_index = len(menu.findChildren(GroupWidget, 'group')) - 1 self.hlayout.insertWidget(insert_index, group) return (group) def listGroups(self, menu): self.group_list = [] for i in range(self.hlayout.count()): try: w = self.hlayout.itemAt(i).widget() self.group_list.append(w._title) except: AttributeError return (self.group_list) def addSliderChoiceWidget(self, menu): slider_choice = SliderChoiceWidget() insert_index = len(menu.findChildren(GroupWidget, 'group')) self.hlayout.insertWidget(insert_index, slider_choice) return (slider_choice)
class ThumbnailRow(QWidget): def __init__(self): super().__init__() self.labels = [] self.layout = QHBoxLayout() self.layout.setSpacing(3) self.layout.setContentsMargins(3, 0, 0, 3) self.setLayout(self.layout) self.items = [] def add_items(self, items): self.items = items self.draw_items() def draw_items(self): self.clear() for item in self.items: thumbnail = Thumbnail(item) self.layout.addWidget(thumbnail, 0) self.layout.addWidget(QWidget(), 1) def clear(self): for i in reversed(range(self.layout.count())): widget = self.layout.itemAt(i).widget() # remove it from the layout list self.layout.removeWidget(widget) # remove it from the gui widget.setParent(None) def zoom(self, scale): for i in reversed(range(self.layout.count())): widget = self.layout.itemAt(i).widget() if isinstance(widget, Thumbnail): widget.zoom(scale) def mousePressEvent(self, a0: QtGui.QMouseEvent): print("Mouse Press") def mouseDoubleClickEvent(self, a0: QtGui.QMouseEvent): print("Mouse Double Click")
class HorizontalImageScrollArea(QScrollArea): def __init__(self, parent=None): super(HorizontalImageScrollArea, self).__init__(parent) self.setWidgetResizable(True) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setFrameShape(QFrame.NoFrame) self.layout = QHBoxLayout() scroll = QWidget() scroll.setLayout(self.layout) self.setWidget(scroll) def eventFilter(self, obj, event): if obj == self.widget() and event.type() == QEvent.Resize: self.widget().resize(self.calcNewSize()) return True return super(HorizontalImageScrollArea, self).eventFilter(obj, event) def calcNewSize(self): height = self.viewport().height() layoutMargins = self.layout.contentsMargins() heightForCalc = height - layoutMargins.top() - layoutMargins.bottom() width = self.calcWidthForHeight(heightForCalc) return QSize(width, height) def calcWidthForHeight(self, height): width = 0 for wgt in range(self.layout.count()): width += self.layout.itemAt(wgt).widget().widthForHeight(height) if self.layout.count() > 1: width += self.layout.spacing() * (self.layout.count() - 1) return width
class MyRow(QWidget): def __init__(self, parent): super(MyRow,self).__init__(parent) self.__initUI() def __initUI(self): self.setStyleSheet('margin:1px;') self.layout = QHBoxLayout(self) self.layout.addStretch(1) pass def addWidget(self,item): self.layout.insertWidget(self.layout.count()-1,item) pass
def _init_info_ui(self): self._text_edit = QTextEdit() self._text_edit.setFixedHeight(80) self._text_edit.setReadOnly(True) button_create = QPushButton("Create") button_create.clicked.connect(self.button_create_clicked) button_upload = QPushButton("Upload") button_upload.clicked.connect(self.button_upload_clicked) vbox = QVBoxLayout() vbox.addWidget(button_create) vbox.addWidget(button_upload) hbox = QHBoxLayout() hbox.addWidget(self._text_edit) hbox.addLayout(vbox) for i in range(hbox.count()): hbox.setAlignment(hbox.itemAt(i).widget(), Qt.AlignTop) self.vbox.addLayout(hbox)
class FileWidget(QWidget): """ Represents a file. """ def __init__(self, source_db_object, submission_db_object, controller, file_ready_signal, align="left"): """ Given some text, an indication of alignment ('left' or 'right') and a reference to the controller, make something to display a file. Align is set to left by default because currently SecureDrop can only accept files from sources to journalists. """ super().__init__() self.controller = controller self.source = source_db_object self.submission = submission_db_object self.file_uuid = self.submission.uuid self.align = align self.layout = QHBoxLayout() self.update() self.setLayout(self.layout) file_ready_signal.connect(self._on_file_download) def update(self): icon = QLabel() icon.setPixmap(load_image('file.png')) if self.submission.is_downloaded: description = QLabel("Open") else: human_filesize = humanize_filesize(self.submission.size) description = QLabel("Download ({})".format(human_filesize)) if self.align != "left": # Float right... self.layout.addStretch(5) self.layout.addWidget(icon) self.layout.addWidget(description, 5) if self.align == "left": # Add space on right hand side... self.layout.addStretch(5) def clear(self): while self.layout.count(): child = self.layout.takeAt(0) if child.widget(): child.widget().deleteLater() @pyqtSlot(str) def _on_file_download(self, file_uuid: str) -> None: if file_uuid == self.file_uuid: self.clear() # delete existing icon and label self.update() # draw modified widget def mouseReleaseEvent(self, e): """ Handle a completed click via the program logic. The download state of the file distinguishes which function in the logic layer to call. """ if self.submission.is_downloaded: # Open the already downloaded file. self.controller.on_file_open(self.submission) else: # Download the file. self.controller.on_file_download(self.source, self.submission)
class ConfigUI(QWidget): configFilePath = "" parseConfigSignal = pyqtSignal(etree._Element) selectResGroupSignal = pyqtSignal(list) def __init__(self, parent=None): super(ConfigUI, self).__init__(parent) grid = QGridLayout() self.setLayout(grid) ## grid.addWidget(QLabel("配置文件:"), 0, 0) grid.addWidget(QLabel("资源分组:"), 1, 0) grid.addWidget(QLabel("数据编码:"), 2, 0) ## self.configFileLE = QLineEdit() # self.configFileLE.setEnabled(False) self.configFileLE.setFocusPolicy(Qt.NoFocus) grid.addWidget(self.configFileLE, 0, 1) browsePB = QPushButton("浏览") browsePB.clicked.connect(self.browse_config_path) grid.addWidget(browsePB, 0, 2) ## self.resGroupWidget = QWidget() self.resGroupLayout = QHBoxLayout() self.resGroupWidget.setLayout(self.resGroupLayout) grid.addWidget(self.resGroupWidget, 1, 1) selectPB = QPushButton("选择") selectPB.clicked.connect(self.select_res_group) grid.addWidget(selectPB, 1, 2) # def create_config def browse_config_path(self): open = QFileDialog() # self.configFilePath = open.getOpenFileUrl(None, "选择转换列表文件") self.configFilePath = open.getOpenFileName(None, "选择转换列表文件", "./") self.configFileLE.setText(self.configFilePath[0]) if self.configFilePath[0] != "": list = etree.parse(self.configFilePath[0]) root = list.getroot() for item in root: if item.tag == "ConvTree": # 转换树 self.parseConfigSignal.emit(item) elif item.tag == "ResStyleList": # 资源分组 self.parse_res_group(item) pass def select_res_group(self): groups = self.resGroupWidget.children() if len(groups) > 0: resGroupArr = [] for item in groups: if isinstance(item, QCheckBox): if item.isChecked(): resGroupArr.append(int(item.text().split(" ")[1])) self.selectResGroupSignal.emit(resGroupArr) def parse_res_group(self, item): while self.resGroupLayout.count(): self.resGroupLayout.takeAt(0) for node in item: if node.tag != "ResStyle": continue print(node.attrib["Name"]) checkBox = QCheckBox(node.attrib["Name"] + " " + str(node.attrib["ID"])) self.resGroupLayout.addWidget(checkBox) self.resGroupLayout.addStretch()
class TaskGeneratorDialog(QDialog): def __init__(self, nbprocessors): QDialog.__init__(self) self.layout = QVBoxLayout(self) self.taskset = None # Utilizations: vbox_utilizations = QVBoxLayout() group = QGroupBox("Task Utilizations:") hbox = QHBoxLayout() hbox.addWidget(QLabel("Generator:", self)) self.comboGenerator = QComboBox() self.comboGenerator.addItem("RandFixedSum") self.comboGenerator.addItem("UUniFast-Discard") self.comboGenerator.addItem("Kato's method") self.comboGenerator.currentIndexChanged.connect(self.generator_changed) hbox.addWidget(self.comboGenerator) vbox_utilizations.addLayout(hbox) # Load slider + spinner: hbox_load = QHBoxLayout() sld = _DoubleSlider(QtCore.Qt.Horizontal, self) sld.setMinimum(0) sld.setMaximum(32) self.spin_load = QDoubleSpinBox(self) self.spin_load.setMinimum(0) self.spin_load.setMaximum(32) self.spin_load.setSingleStep(0.1) hbox_load.addWidget(QLabel("Total utilization: ", self)) hbox_load.addWidget(sld) hbox_load.addWidget(self.spin_load) sld.doubleValueChanged.connect(self.spin_load.setValue) self.spin_load.valueChanged.connect(sld.setValue) self.spin_load.setValue(nbprocessors / 2.) vbox_utilizations.addLayout(hbox_load) # Number of periodic tasks: self.hbox_tasks = QHBoxLayout() self.spin_tasks = QSpinBox(self) self.spin_tasks.setMinimum(0) self.spin_tasks.setMaximum(999) # That's arbitrary. self.hbox_tasks.addWidget(QLabel("Number of periodic tasks: ", self)) self.hbox_tasks.addStretch(1) self.hbox_tasks.addWidget(self.spin_tasks) vbox_utilizations.addLayout(self.hbox_tasks) # Number of sporadic tasks: self.hbox_sporadic_tasks = QHBoxLayout() self.spin_sporadic_tasks = QSpinBox(self) self.spin_sporadic_tasks.setMinimum(0) self.spin_sporadic_tasks.setMaximum(999) # That's arbitrary. self.hbox_sporadic_tasks.addWidget( QLabel("Number of sporadic tasks: ", self)) self.hbox_sporadic_tasks.addStretch(1) self.hbox_sporadic_tasks.addWidget(self.spin_sporadic_tasks) vbox_utilizations.addLayout(self.hbox_sporadic_tasks) # Min / Max utilizations self.hbox_utilizations = QHBoxLayout() self.hbox_utilizations.addWidget(QLabel("Min/Max utilizations: ", self)) self.interval_utilization = IntervalSpinner( self, min_=0, max_=1, step=.01, round_option=False) self.hbox_utilizations.addWidget(self.interval_utilization) vbox_utilizations.addLayout(self.hbox_utilizations) group.setLayout(vbox_utilizations) self.layout.addWidget(group) # Periods: vbox_periods = QVBoxLayout() group = QGroupBox("Task Periods:") # Log uniform self.lunif = QRadioButton("log-uniform distribution between:") vbox_periods.addWidget(self.lunif) self.lunif.setChecked(True) self.lunif_interval = IntervalSpinner(self) self.lunif_interval.setEnabled(self.lunif.isChecked()) self.lunif.toggled.connect(self.lunif_interval.setEnabled) vbox_periods.addWidget(self.lunif_interval) # Uniform self.unif = QRadioButton("uniform distribution between:") vbox_periods.addWidget(self.unif) self.unif_interval = IntervalSpinner(self) self.unif_interval.setEnabled(self.unif.isChecked()) self.unif.toggled.connect(self.unif_interval.setEnabled) vbox_periods.addWidget(self.unif_interval) # Discrete discrete = QRadioButton("chosen among these (space separated) values:") vbox_periods.addWidget(discrete) self.periods = QLineEdit(self) self.periods.setValidator(QRegExpValidator( QRegExp("^\\d*(\.\\d*)?( \\d*(\.\\d*)?)*$"))) vbox_periods.addWidget(self.periods) self.periods.setEnabled(discrete.isChecked()) discrete.toggled.connect(self.periods.setEnabled) vbox_periods.addStretch(1) group.setLayout(vbox_periods) self.layout.addWidget(group) buttonBox = QDialogButtonBox() cancel = buttonBox.addButton(QDialogButtonBox.Cancel) generate = buttonBox.addButton("Generate", QDialogButtonBox.AcceptRole) cancel.clicked.connect(self.reject) generate.clicked.connect(self.generate) self.layout.addWidget(buttonBox) self.show_randfixedsum_options() def generator_changed(self, value): if value == 2: self.show_kato_options() else: self.show_randfixedsum_options() def show_randfixedsum_options(self): for i in range(self.hbox_utilizations.count()): self.hbox_utilizations.itemAt(i).widget().hide() for i in range(self.hbox_tasks.count()): if self.hbox_tasks.itemAt(i).widget(): self.hbox_tasks.itemAt(i).widget().show() for i in range(self.hbox_sporadic_tasks.count()): if self.hbox_sporadic_tasks.itemAt(i).widget(): self.hbox_sporadic_tasks.itemAt(i).widget().show() def show_kato_options(self): for i in range(self.hbox_utilizations.count()): if self.hbox_utilizations.itemAt(i).widget(): self.hbox_utilizations.itemAt(i).widget().show() for i in range(self.hbox_tasks.count()): if self.hbox_tasks.itemAt(i).widget(): self.hbox_tasks.itemAt(i).widget().hide() for i in range(self.hbox_sporadic_tasks.count()): if self.hbox_sporadic_tasks.itemAt(i).widget(): self.hbox_sporadic_tasks.itemAt(i).widget().hide() def get_min_utilization(self): return self.interval_utilization.getMin() def get_max_utilization(self): return self.interval_utilization.getMax() def generate(self): n = self.get_nb_tasks() if (n == 0): QMessageBox.warning( self, "Generation failed", "Please check the utilization and the number of tasks.") return if self.comboGenerator.currentIndex() == 0: u = StaffordRandFixedSum(n, self.get_utilization(), 1) elif self.comboGenerator.currentIndex() == 1: u = UUniFastDiscard(n, self.get_utilization(), 1) else: u = gen_kato_utilizations(1, self.get_min_utilization(), self.get_max_utilization(), self.get_utilization()) n = len(u[0]) p_types = self.get_periods() if p_types[0] == "unif": p = gen_periods_uniform(n, 1, p_types[1], p_types[2], p_types[3]) elif p_types[0] == "lunif": p = gen_periods_loguniform(n, 1, p_types[1], p_types[2], p_types[3]) else: p = gen_periods_discrete(n, 1, p_types[1]) if u and p: self.taskset = gen_tasksets(u, p)[0] self.accept() elif not u: QMessageBox.warning( self, "Generation failed", "Please check the utilization and the number of tasks.") else: QMessageBox.warning( self, "Generation failed", "Pleache check the periods.") def get_nb_tasks(self): return self.spin_tasks.value() + self.spin_sporadic_tasks.value() def get_nb_periodic_tasks(self): return self.spin_tasks.value() def get_nb_sporadic_tasks(self): return self.spin_sporadic_tasks.value() def get_utilization(self): return self.spin_load.value() def get_periods(self): if self.unif.isChecked(): return ("unif", self.unif_interval.getMin(), self.unif_interval.getMax(), self.unif_interval.getRound()) elif self.lunif.isChecked(): return ("lunif", self.lunif_interval.getMin(), self.lunif_interval.getMax(), self.lunif_interval.getRound()) else: return ("discrete", map(float, str(self.periods.text()).split()))
class ChurchUI: def __init__(self, window, church, target_layout): self.church = church self.target_layout = target_layout self.window = window self.layout = None self.church_button = None self.del_button = None self.edit_button = None self.church_text = None self.place_text = None self.msg = None self.student_ui = None self.create() def create(self): self.layout = QHBoxLayout() self.layout.setSpacing(2) # Create Church Button self.church_button = Button(self.church.get_name(), width=None, height=40) self.church_button.align_text() # Create church Text self.church_text = TextBox(self.church.name, height=40) # Create Place Text self.place_text = TextBox(self.church.place, height=40) # Create Edit Button self.edit_button = Button('EDIT', width=40, height=40) # Create Del button self.del_button = Button('DEL', width=40, height=40) self.layout.addWidget(self.church_text) self.layout.addWidget(self.place_text) self.layout.addWidget(self.church_button) self.layout.addWidget(self.edit_button) self.layout.addWidget(self.del_button) self.target_layout.insertLayout(self.target_layout.count() - 1, self.layout) self.church_button.clicked.connect(self.select) self.church_text.returnPressed.connect(self.finish_edit) self.place_text.returnPressed.connect(self.finish_edit) self.edit_button.clicked.connect(self.edit) self.del_button.clicked.connect(self.passive_delete) def delete(self): if self.layout is not None: while self.layout.count(): item = self.layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: pass self.church.delete() del self def hide(self): pass def select(self): try: if not self.student_ui: self.student_ui = student.Students(self.window, self.church) data.selection.select(self.student_ui) except Exception as e: print(e) def deselect(self): self.window.setCentralWidget(None) def passive_delete(self): self.msg = display.QuestionMessageBox( self.window, 'Delete Church', 'Do You want to delete church ' + self.church.name + ' and all the students?', self.delete) def edit(self): if self.church_button.isVisible(): data.editing.start_edit(self) else: data.editing.finish_edit() def start_edit(self): self.church_button.hide() # self.box_layout self.church_text.show() self.place_text.show() def finish_edit(self): if self.church_text.text() and self.place_text.text(): self.church_text.hide() self.place_text.hide() self.church.name = self.church_text.text() self.church.place = self.place_text.text() self.church_button.setText(self.church.get_name()) self.church_button.show() def revert_edit(self): self.church_text.hide() self.place_text.hide() self.church_button.setText(self.church_text.text() + ', ' + self.place_text.text()) self.church_button.show()
class LibraryWidget(QWidget): def __init__(self, parent, item, link, list_widget, show_new=False): super(LibraryWidget, self).__init__(None) self.parent = parent self.item = item self.link = link self.list_widget = list_widget self.show_new = show_new self.observer = None self.build_info = None self.destroyed.connect(lambda: self._destroyed()) self.setEnabled(False) self.layout = QHBoxLayout() self.layout.setContentsMargins(2, 2, 2, 2) self.setLayout(self.layout) self.infoLabel = QLabel("Loading build information...") self.launchButton = QPushButton("Launch") self.launchButton.setMinimumWidth(75) self.launchButton.setProperty("CancelButton", True) self.layout.addWidget(self.launchButton) self.layout.addWidget(self.infoLabel, stretch=1) self.thread = BuildInfoReader(link) self.thread.finished.connect(self.draw) self.thread.start() self.item.setSizeHint(self.sizeHint()) def draw(self, build_info): if build_info is None: self.infoLabel.setText( ("Build *{0}* is damaged!").format(Path(self.link).name)) self.launchButton.setText("Delete") self.launchButton.clicked.connect(self.ask_remove_from_drive) self.setEnabled(True) return for i in reversed(range(self.layout.count())): self.layout.itemAt(i).widget().setParent(None) self.build_info = build_info self.branch = self.build_info.branch self.item.date = build_info.commit_time self.icon_favorite = QIcon(":resources/icons/favorite.svg") self.icon_fake = QIcon(":resources/icons/fake.svg") self.icon_delete = QIcon(":resources/icons/delete.svg") self.launchButton = QPushButton("Launch") self.launchButton.setMinimumWidth(75) self.launchButton.setProperty("LaunchButton", True) self.subversionLabel = QLabel() self.branchLabel = QLabel() self.commitTimeLabel = QLabel() self.buildHashLabel = QLabel() self.countButton = QPushButton("0") self.countButton.setEnabled(False) self.countButton.setProperty("Count", True) self.countButton.hide() self.countButton.setFixedSize(24, 24) self.widgetFavorite = QPushButton() self.widgetFavorite.setEnabled(False) self.widgetFavorite.setFixedSize(24, 24) self.widgetFavorite.setIcon(self.icon_fake) self.widgetFavorite.setProperty("Icon", True) self.layout.addWidget(self.launchButton) self.layout.addWidget(self.subversionLabel) self.layout.addWidget(self.branchLabel) self.layout.addWidget(self.commitTimeLabel) self.layout.addWidget(self.buildHashLabel) self.layout.addStretch() self.layout.addWidget(self.countButton) self.layout.addWidget(self.widgetFavorite) self.launchButton.clicked.connect(self.launch) self.subversionLabel.setText(self.build_info.subversion) if self.branch == 'lts': branch_name = "LTS" else: branch_name = self.branch.replace('-', ' ').title() self.branchLabel.setText(branch_name) self.commitTimeLabel.setText(self.build_info.commit_time) self.buildHashLabel.setText(self.build_info.build_hash) # Context menu self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) self.menu = QMenu() self.menu.setFont(self.parent.font) self.menu_extended = QMenu() self.menu_extended.setFont(self.parent.font) self.deleteAction = QAction("Delete From Drive", self) self.deleteAction.setIcon(self.icon_delete) self.deleteAction.triggered.connect(self.ask_remove_from_drive) self.setAsFavoriteAction = QAction("Mark As Favorite", self) self.setAsFavoriteAction.setIcon(self.icon_favorite) self.setAsFavoriteAction.triggered.connect(self.set_favorite) self.registerExtentionAction = QAction("Register Extension") self.registerExtentionAction.triggered.connect(self.register_extension) self.createShortcutAction = QAction("Create Shortcut") self.createShortcutAction.triggered.connect(self.create_shortcut) self.showFolderAction = QAction("Show Folder") self.showFolderAction.triggered.connect(self.show_folder) self.createSymlinkAction = QAction("Create Symlink") self.createSymlinkAction.triggered.connect(self.create_symlink) self.menu.addAction(self.setAsFavoriteAction) if get_platform() == 'Windows': self.menu.addAction(self.registerExtentionAction) self.menu.addAction(self.createShortcutAction) self.menu.addAction(self.createSymlinkAction) self.menu.addAction(self.showFolderAction) self.menu.addAction(self.deleteAction) self.menu_extended.addAction(self.deleteAction) if self.show_new: self.NewItemLabel = QLabel("New") self.NewItemLabel.setAlignment(Qt.AlignRight | Qt.AlignCenter) self.NewItemLabel.setIndent(6) self.layout.addWidget(self.NewItemLabel) if get_mark_as_favorite() == 0: pass elif (get_mark_as_favorite() == 1 and self.branch == "stable"): self.set_favorite() elif (get_mark_as_favorite() == 2 and self.branch == "daily"): self.set_favorite() elif get_mark_as_favorite() == 3: self.set_favorite() elif get_favorite_path() == self.link: self.set_favorite() self.setEnabled(True) self.list_widget.sortItems() self.list_widget.resize_labels( ('subversionLabel', 'branchLabel', 'commitTimeLabel', 'buildHashLabel')) def context_menu(self): if len(self.list_widget.selectedItems()) > 1: self.menu_extended.exec_(QCursor.pos()) return link_path = Path(get_library_folder()) / "bl_symlink" link = link_path.as_posix() if os.path.exists(link): if (os.path.isdir(link) or os.path.islink(link)): if link_path.resolve() == self.link: self.createSymlinkAction.setEnabled(False) self.menu.exec_(QCursor.pos()) return self.createSymlinkAction.setEnabled(True) self.menu.exec_(QCursor.pos()) def mouseDoubleClickEvent(self, event): if self.build_info is not None: self.launch() def mouseReleaseEvent(self, event): if event.button == Qt.LeftButton: if hasattr(self, "NewItemLabel"): self.NewItemLabel.hide() mod = QApplication.keyboardModifiers() if not (mod == Qt.ShiftModifier or mod == Qt.ControlModifier): self.list_widget.clearSelection() self.item.setSelected(True) event.accept() event.ignore() @QtCore.pyqtSlot() def launch(self): self.item.setSelected(True) if hasattr(self, "NewItemLabel"): self.NewItemLabel.hide() platform = get_platform() library_folder = Path(get_library_folder()) if platform == 'Windows': b3d_exe = library_folder / self.link / "blender.exe" proc = _popen(b3d_exe.as_posix()) elif platform == 'Linux': b3d_exe = library_folder / self.link / "blender" proc = _popen('nohup "' + b3d_exe.as_posix() + '"') if self.observer is None: self.observer = Observer(self) self.observer.count_changed.connect(self.proc_count_changed) self.observer.started.connect(self.observer_started) self.observer.finished.connect(self.observer_finished) self.observer.start() self.observer.append_proc(proc) def proc_count_changed(self, count): self.countButton.setText(str(count)) def observer_started(self): self.countButton.show() self.deleteAction.setEnabled(False) def observer_finished(self): self.observer = None self.countButton.hide() self.deleteAction.setEnabled(True) @QtCore.pyqtSlot() def ask_remove_from_drive(self): self.item.setSelected(True) self.dlg = DialogWindow( self.parent, title="Warning", text="Are you sure you want to<br>delete selected builds?", accept_text="Yes", cancel_text="No", icon=DialogIcon.WARNING) if len(self.list_widget.selectedItems()) > 1: self.dlg.accepted.connect(self.remove_from_drive_extended) else: self.dlg.accepted.connect(self.remove_from_drive) @QtCore.pyqtSlot() def remove_from_drive_extended(self): for item in self.list_widget.selectedItems(): self.list_widget.itemWidget(item).remove_from_drive() @QtCore.pyqtSlot() def remove_from_drive(self): self.launchButton.setText("Deleting") self.setEnabled(False) self.item.setFlags(self.item.flags() & ~Qt.ItemIsSelectable) path = Path(get_library_folder()) / self.link self.remover = Remover(path) self.remover.finished.connect(self.remover_finished) self.remover.start() def remover_finished(self, code): if code == 0: self.parent.draw_from_cashed(self.build_info) self.list_widget.remove_item(self.item) return else: self.launchButton.setText("Launch") self.setEnabled(True) return @QtCore.pyqtSlot() def set_favorite(self): set_favorite_path(self.link) if self.parent.favorite is not None: self.parent.favorite.widgetFavorite.setIcon(self.icon_fake) self.parent.favorite.setAsFavoriteAction.setVisible(True) self.parent.favorite = self self.widgetFavorite.setIcon(self.icon_favorite) self.setAsFavoriteAction.setVisible(False) @QtCore.pyqtSlot() def register_extension(self): path = Path(get_library_folder()) / self.link self.register = Register(path) self.register.start() @QtCore.pyqtSlot() def create_shortcut(self): name = "Blender {0} {1}".format( self.build_info.subversion.replace('(', '').replace(')', ''), self.build_info.branch.replace('-', ' ').title()) create_shortcut(self.link, name) @QtCore.pyqtSlot() def create_symlink(self): target = self.link.as_posix() link = (Path(get_library_folder()) / "bl_symlink").as_posix() platform = get_platform() if platform == 'Windows': if os.path.exists(link): if os.path.isdir(link): os.rmdir(link) _check_call('mklink /J "{0}" "{1}"'.format(link, target)) elif platform == 'Linux': if os.path.exists(link): if os.path.islink(link): os.unlink(link) os.symlink(target, link) @QtCore.pyqtSlot() def show_folder(self): platform = get_platform() library_folder = Path(get_library_folder()) folder = library_folder / self.link if platform == 'Windows': os.startfile(folder.as_posix()) elif platform == 'Linux': subprocess.call(["xdg-open", folder.as_posix()]) def _destroyed(self): if self.parent.favorite == self: self.parent.favorite = None
class Window(QWidget): def __init__(self, login_id, login_password): super().__init__() self.id = login_id self.password = login_password self.inbox = "" self.init_ui() def inbox_mails(self, mail): self.incoming_mail = QGroupBox() self.incoming_mail_group = QVBoxLayout(self.incoming_mail) self.inbox_subject = QLabel("Subject: " + mail[0]) self.inbox_from = QLabel("From: " + mail[1]) self.incoming_mail_group.addWidget(self.inbox_subject) self.incoming_mail_group.addWidget(self.inbox_from) self.box_palette = QPalette() self.box_palette.setColor(QPalette.Background, QColor(100, 100, 100)) self.setPalette(self.box_palette) return self.incoming_mail def inbox_container(self): self.scrollArea = QScrollArea(self) self.scrollArea.setMaximumWidth(250) self.scrollArea.setMinimumWidth(100) self.scrollArea.setWidgetResizable(True) self.widget = QWidget() self.scrollArea.setWidget(self.widget) self.layoutScrollArea = QVBoxLayout() self.inbox = MailSettings.inbox_getter(self.id, self.password) for index, mail_in_inbox in enumerate(self.inbox): self.layoutScrollArea.addWidget(self.inbox_mails(mail_in_inbox)) self.widget.setLayout(self.layoutScrollArea) def init_ui(self): self.mail_address = QLineEdit() self.send_button = QPushButton("Send!") self.mail_text = QTextEdit() self.mail_subject = QLineEdit() self.label_to = QLabel("To:") self.label_subject = QLabel("Subject:") self.label_text = QLabel("Text:") self.label_text.setFixedWidth(50) self.attached_file_paths = [] self.warning_label = QLabel("") self.warning_label_head = QLabel("") self.hidden_inbox_button = QPushButton("<", self) self.hidden_inbox_button.setFixedSize(30, 70) self.file_open_button = QPushButton("Attach") self.file_open_button.setIcon(QIcon('attach-paperclip-symbol.png')) self.delete_all_files = QPushButton("Delete All") self.delete_all_files.hide() self.delete_all_files.clicked.connect(self.del_all_files) self.hidden_inbox_button.clicked.connect(self.hidden_inbox) self.file_open_button.clicked.connect(self.file_open) v_box1 = QVBoxLayout() v_box1.addWidget(self.label_to) v_box1.addWidget(self.label_subject) v_box2 = QVBoxLayout() v_box2.addWidget(self.mail_address) v_box2.addWidget(self.mail_subject) h_box1 = QHBoxLayout() h_box1.addLayout(v_box1) h_box1.addLayout(v_box2) h_box2 = QHBoxLayout() h_box2.addWidget(self.label_text) h_box2.addWidget(self.mail_text) v_box_attach = QVBoxLayout() v_box_attach.addWidget(self.file_open_button) self.h_box_files = QHBoxLayout() h_box3 = QHBoxLayout() h_box3.addLayout(self.h_box_files) h_box3.addStretch() h_box3.addWidget(self.delete_all_files) h_box3.addWidget(self.file_open_button) self.v_box = QVBoxLayout() self.v_box.addLayout(h_box1) self.v_box.addLayout(h_box2) self.v_box.addLayout(h_box3) self.v_box.addWidget(self.warning_label_head) self.v_box.addWidget(self.warning_label) self.v_box.addWidget(self.send_button, 10) self.inbox_container() h_box4 = QHBoxLayout() h_box4.addWidget(self.scrollArea) h_box4.addWidget(self.hidden_inbox_button) h_box5 = QHBoxLayout() h_box5.addLayout(h_box4) h_box5.addLayout(self.v_box) self.setLayout(h_box5) self.send_button.clicked.connect(self.send) def send(self): to = self.mail_address.text() subject = self.mail_subject.text() text = self.mail_text.toPlainText() file = self.attached_file_paths mail = MailSettings(to, subject, text, file, self.id, self.password) warnings = mail.mail_sender() if warnings: warning = "\n".join(warnings) self.warning_label_head.setText("Invalid e-mail addresses:\n") self.warning_label_head.setStyleSheet( "color: red; border: 2px solid red; border-radius: 5px") self.warning_label.setText(warning) def hidden_inbox(self): if self.scrollArea.isHidden(): self.scrollArea.show() self.hidden_inbox_button.setText("<") self.setGeometry(self.x() + 1, self.y() + 1, self.width() + 1, self.height() + 1) self.update() else: self.scrollArea.hide() self.hidden_inbox_button.setText(">") self.setGeometry(self.x() + 1, self.y() + 1, self.width() + 1, self.height() + 1) self.update() def file_open(self): file_name = QFileDialog.getOpenFileName(self, "Select File", os.getenv("HOME")) if file_name[0] not in self.attached_file_paths and file_name[0] != "": self.attached_file_paths.append(str(file_name[0])) self.h_box_files.addWidget( QLabel("{}-)".format(len(self.attached_file_paths)) + file_name[0].split("/")[-1] + " ")) # print(self.attached_file_paths) self.delete_all_files.show() else: pass def del_all_files(self): self.attached_file_paths = [] self.delete_all_files.hide() for i in reversed(range(self.h_box_files.count())): self.h_box_files.itemAt(i).widget().setParent(None) self.setGeometry(self.x() + 1, self.y() + 1, self.width() + 1, self.height() + 1) self.update()
class Controller(QWidget): def __init__(self): super().__init__() # game details self._player = 0 self._players = None self._round = None self._cards_played = 0 # child processes self._server = None self._client = None self._parent_conn = None # connection to child # set graphics properties self.setWindowTitle('500') self.setGeometry(50, 50, WIDTH, HEIGHT) self.setFixedSize(self.size()) # check for background image if os.path.exists("bkg.jpg"): bg = QtGui.QPixmap("bkg.jpg") palette = QtGui.QPalette() palette.setBrush(10, QtGui.QBrush(bg)) self.setPalette(palette) else: print( "No background image found. Copy bkg.jpg to the directory to add one." ) # default font self.setFont(QtGui.QFont("Book Antiqua", 11, QtGui.QFont.Bold)) # create main menu self._main_menu = QWidget() self.create_main_menu_view() # create game options view self._options_type = None # which options screen to display self._port = None self._username = None self._ip = None self._password = None self._player_types = [None] * 3 self._game_options_view = QWidget() self.create_game_options_view() # create game view self._game_view = QWidget() self._card_layout = [None] * NUMBER_PLAYERS # array of players cards self._played_cards = None # four cards in center of screen self._player_info = [None ] * NUMBER_PLAYERS # info label for each player self._bet_controls = [] self._card_winning = None self.create_game_view() # create stacked layout self._stacked_layout = QStackedWidget(self) self._stacked_layout.addWidget(self._main_menu) self._stacked_layout.addWidget(self._game_options_view) self._stacked_layout.addWidget(self._game_view) self.show() # create main menu and add it to _main_menu layout def create_main_menu_view(self): menu = QVBoxLayout() menu.setAlignment(QtCore.Qt.AlignHCenter) menu.addStretch(1) # create a pretty title layout title_layout = QHBoxLayout() title_layout.addStretch(1) # add some bowers for nice looks svgWidget = QtSvg.QSvgWidget('img/JH.svg') svgWidget.setMinimumHeight(CARD_HEIGHT) svgWidget.setMaximumHeight(CARD_HEIGHT) svgWidget.setMinimumWidth(CARD_WIDTH) svgWidget.setMaximumWidth(CARD_WIDTH) title_layout.addWidget(svgWidget) title_layout.addStretch(1) # create title title = QLabel("500 Online!") font = QtGui.QFont("Book Antiqua", 50, QtGui.QFont.Bold) title.setFont(font) title_layout.addWidget(title) title_layout.addStretch(1) svgWidget = QtSvg.QSvgWidget('img/JD.svg') svgWidget.setMinimumHeight(CARD_HEIGHT) svgWidget.setMaximumHeight(CARD_HEIGHT) svgWidget.setMinimumWidth(CARD_WIDTH) svgWidget.setMaximumWidth(CARD_WIDTH) title_layout.addWidget(svgWidget) title_layout.addStretch(1) menu.addLayout(title_layout) menu.addStretch(1) # create layout layout = QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignCenter) # bots button button = QPushButton('Play against bots', self) button.clicked.connect(lambda: self.goto_game_options(0)) layout.addWidget(button) # join server button = QPushButton('Join a server', self) button.clicked.connect(lambda: self.goto_game_options(1)) layout.addWidget(button) # host and play button = QPushButton('Host and play', self) button.clicked.connect(lambda: self.goto_game_options(2)) layout.addWidget(button) # options TODO # button = QPushButton('Options', self) # layout.addWidget(button) # exit button = QPushButton('Exit', self) button.clicked.connect(self.close) layout.addWidget(button) menu.addLayout(layout) menu.addStretch(2) # title = QLabel("By Gareth Booth") TODO # font = QtGui.QFont("Book Antiqua", 8, QtGui.QFont.Bold) # title.setFont(font) # menu.addWidget(title) # add to layout self._main_menu.setLayout(menu) # go to the game options screen # type is which screen button you came from: # 0 for bots # 1 for join server # 2 for host and play def goto_game_options(self, options_type): self._options_type = options_type self._stacked_layout.setCurrentIndex(1) self.reset_game_options() # hide layouts depending on which options type we are if options_type == 0: self._ip.hide() self._password.hide() for index in range(0, 3): self._player_types[index].hide() elif options_type == 1: for index in range(0, 3): self._player_types[index].hide() elif options_type == 2: self._ip.hide() # unhide all game options (note port and username are never hidden) def reset_game_options(self): self._ip.show() self._password.show() for index in range(0, 3): self._player_types[index].show() # create the options for joining game menu def create_game_options_view(self): # create layout layout = QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignCenter) # username edit = QLineEdit() edit.setText("Jimmy") edit.setMaximumWidth(200) self._username = QWidget() self._username.setLayout(create_menu_entry("Username: "******"Port: ", port)) layout.addWidget(self._port) # ip ip = QLineEdit() ip.setMaximumWidth(200) self._ip = QWidget() self._ip.setLayout(create_menu_entry("Ip Address: ", ip)) layout.addWidget(self._ip) # password password = QLineEdit() password.setMaximumWidth(200) self._password = QWidget() self._password.setLayout(create_menu_entry("Password: "******"Human", "lvl 1 bot", "lvl 2 bot"]) player_type.setMaximumWidth(200) self._player_types[index] = QWidget() self._player_types[index].setLayout( create_menu_entry("Player " + str(index + 2) + " is a :", player_type)) layout.addWidget(self._player_types[index]) # join button button = QPushButton('Go', self) button.clicked.connect(self.create_game) layout.addWidget(button) # back button button = QPushButton('Back to Menu', self) button.clicked.connect(lambda: self._stacked_layout.setCurrentIndex(0)) layout.addWidget(button) # create layout with stretch to force center the controls hcentered_layout = QHBoxLayout() hcentered_layout.addStretch(1) hcentered_layout.addLayout(layout) hcentered_layout.addStretch(1) self._game_options_view.setLayout(hcentered_layout) # returns username from the game options screen def get_username(self): return self._username.layout().itemAt(1).widget().text() # returns port from the game options screen def get_port(self): return self._port.layout().itemAt(1).widget().value() # returns password from the game options screen def get_password(self): return self._password.layout().itemAt(1).widget().text() # returns password from the game options screen def get_ip(self): return self._ip.layout().itemAt(1).widget().text() # returns player type from the game options screen for given player def get_player_type(self, index): return self._player_types[index].layout().itemAt( 1).widget().currentIndex() # creates game view and adds it to _game_view layout def create_game_view(self): # layout game_layout = QVBoxLayout() self._card_layout[2] = QHBoxLayout() # create card iamges for teammate for _ in range(0, 10): svgWidget = QtSvg.QSvgWidget('img/BACK.svg') svgWidget.setFixedSize(CARD_WIDTH, CARD_HEIGHT) self._card_layout[2].addWidget(svgWidget) # add this card layout game_layout.addLayout(self._card_layout[2]) # Hbox for center of the screen middle_layout = QHBoxLayout() # player on other team self._card_layout[1] = QVBoxLayout() self._card_layout[1].setAlignment(QtCore.Qt.AlignLeft) for _ in range(0, 10): svgWidget = QtSvg.QSvgWidget('img/BACK.svg') svgWidget.setFixedSize(CARD_WIDTH / 2, CARD_HEIGHT / 2) self._card_layout[1].addWidget(svgWidget) # add this card layout middle_layout.addLayout(self._card_layout[1]) # add other team info label self._player_info[1] = QLabel(self) self._player_info[1].setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) middle_layout.addWidget(self._player_info[1]) # layout in middle center (for teammate and your info) middle_center_layout = QVBoxLayout() # add teammate info label self._player_info[2] = QLabel(self) self._player_info[2].setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignHCenter) middle_center_layout.addWidget(self._player_info[2]) middle_center_layout.addStretch(1) # add where the 4 cards will go during play self._played_cards = QHBoxLayout() for _ in range(0, NUMBER_PLAYERS): # layout containing this tricks details sublayout = QVBoxLayout() sublayout.setAlignment(QtCore.Qt.AlignVCenter) # text top_text = QLabel() sublayout.addWidget(top_text) # card image svgWidget = QtSvg.QSvgWidget('img/BACK.svg') svgWidget.setFixedSize(CARD_WIDTH, CARD_HEIGHT) sublayout.addWidget(svgWidget) # text bottom_text = QLabel() sublayout.addWidget(bottom_text) self._played_cards.addLayout(sublayout) # add this card layout middle_center_layout.addLayout(self._played_cards) middle_center_layout.addStretch(1) # add player info label self._player_info[0] = QLabel(self) self._player_info[0].setAlignment(QtCore.Qt.AlignBottom | QtCore.Qt.AlignCenter) middle_center_layout.addWidget(self._player_info[0]) # add middle center layout to middle layout middle_layout.addLayout(middle_center_layout) # add other team info label self._player_info[3] = QLabel(self) self._player_info[3].setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) middle_layout.addWidget(self._player_info[3]) # set player info as waiting for index in range(0, 4): self._player_info[index].setText("Waiting for player") self._card_layout[3] = QVBoxLayout() self._card_layout[3].setAlignment(QtCore.Qt.AlignRight) # create card iamges for other team for _ in range(0, 10): svgWidget = QtSvg.QSvgWidget('img/BACK.svg') svgWidget.setFixedSize(CARD_WIDTH / 2, CARD_HEIGHT / 2) self._card_layout[3].addWidget(svgWidget) # add this card layout middle_layout.addLayout(self._card_layout[3]) # add the middle layout game_layout.addLayout(middle_layout) # create card iamges for player self._card_layout[0] = QHBoxLayout() self._card_layout[0].setAlignment(QtCore.Qt.AlignBottom) for index in range(0, 13): svgWidget = QtSvg.QSvgWidget('img/BACK.svg') svgWidget.setFixedSize(CARD_WIDTH, CARD_HEIGHT) if index >= 10: svgWidget.hide() self._card_layout[0].addWidget(svgWidget) # add this card layout game_layout.addLayout(self._card_layout[0]) # add to layout game_layout.addLayout(self.create_bet_controls()) self._game_view.setLayout(game_layout) # returns layout containing all bet controls def create_bet_controls(self): # create layout for betting actions layout = QHBoxLayout() # number bet choice button number_bet = QComboBox() number_bet.addItems(["6", "7", "8", "9", "10"]) layout.addWidget(number_bet) self._bet_controls.append(number_bet) # suit bet choice button suit_bet = QComboBox() suit_bet.addItems( ["Spades", "Clubs", "Diamonds", "Hearts", "No Trumps"]) layout.addWidget(suit_bet) self._bet_controls.append(suit_bet) # bet button, sends bet based on what we entered in the choice boxes above button = QPushButton('Bet', self) button.clicked.connect(lambda: self.send_to_client( number_bet.currentText() + suit_to_letter(suit_bet.currentText()))) layout.addWidget(button) self._bet_controls.append(button) # pass button button = QPushButton('Pass', self) button.clicked.connect(lambda: self.send_to_client("PASS")) layout.addWidget(button) self._bet_controls.append(button) # misere button button = QPushButton('Bet Misere', self) button.clicked.connect(lambda: self.send_to_client("MISERE")) layout.addWidget(button) self._bet_controls.append(button) # open misere button (TODO functionality, such as seeing persons hand) button = QPushButton('Bet Open Misere', self) button.clicked.connect(lambda: self.send_to_client("OPENMISERE")) layout.addWidget(button) self._bet_controls.append(button) layout.addStretch(1) # main menu button button = QPushButton('Exit to menu', self) layout.addWidget(button) button.clicked.connect(lambda: self.exit_to_menu()) self._bet_controls.append(button) # disable bet buttons by default self.activate_bet_controls(set=False) return layout # toggles button activation # leave bool as None to toggle bet def activate_bet_controls(self, set=None): for elem in self._bet_controls[:len(self._bet_controls) - 1]: elem.setEnabled(not elem.isEnabled() if set == None else set) # reset bet controls def reset_bet_controls(self): self.activate_bet_controls(set=False) self._bet_controls[0].setCurrentIndex(0) self._bet_controls[1].setCurrentIndex(0) # given deck, updates the players hand def update_player_hand(self, deck, setEvent=False): # update each card for i, card in enumerate(deck): widget = self._card_layout[self._player].itemAt(i).widget() widget.load('img/' + card + '.svg') # show kitty cards if we are in kitty round if i >= 10: widget.show() # we only need this set after it is possible to choose a card if setEvent: # note the extra card=card argument to stop each # lambda using local variable card # if we are in the kitty round, you can't # choose an invalid card, so remove it instantly widget.mouseReleaseEvent = ( lambda event, card=card: self.send_card( card, remove=bool(len(deck) > 10))) # resets all players hands def reset_players_hands(self): # show each players cards again for i in range(0, NUMBER_PLAYERS): for j in range(0, 10): widget = self._card_layout[i].itemAt(j).widget() widget.load('img/BACK.svg') widget.show() # hide kitty cards again for i in range(10, 13): self._card_layout[self._player].itemAt(i).widget().hide() # reset card layout to what it was before self.rearrange_card_layout( -int(self._player)) # int to remove pylint errors # show the players first 10 cards and hide the last 3 def reset_player_hand_after_kitty(self): for i in range(0, 10): self._card_layout[self._player].itemAt(i).widget().show() for i in range(10, 13): self._card_layout[self._player].itemAt(i).widget().hide() # sends card to client, optionally remove them def send_card(self, card, remove=False): self.send_to_client(card) if remove == True: self.remove_card_from_hand(self._player, card) # toggles card controls # supply set to give them a value def activate_card_controls(self, set=None): for i in range(0, self._card_layout[self._player].count()): widget = self._card_layout[self._player].itemAt(i).widget() widget.setEnabled(not widget.isEnabled() if set == None else set) # removes given card from our hand # note closing the widget does not decrease the count of cards in the layout def remove_card_from_hand(self, player, card): # find location of card in our deck and close that widget if player == self._player: index = self._players[self._player]["deck"].index(card) else: index = self._round self._card_layout[player].itemAt(index).widget().hide() # resets the cards played interface def reset_cards_played(self): self._cards_played = 0 # reset image and text for i in range(0, self._played_cards.count()): self._played_cards.itemAt(i).layout().itemAt(0).widget().setText( "") self._played_cards.itemAt(i).layout().itemAt(1).widget().load( 'img/BACK.svg') self.reset_card_winning() # reset the winning text def reset_card_winning(self): for i in range(0, self._played_cards.count()): self._played_cards.itemAt(i).layout().itemAt(2).widget().setText( "") # adds the card to the center def add_card_played(self, card, player, winning): self._played_cards.itemAt(self._cards_played).layout().\ itemAt(0).widget().setText("Player " + str(player + 1)) self._played_cards.itemAt(self._cards_played).layout().\ itemAt(1).widget().load('img/' + card + '.svg') # update winning text if winning: self.reset_card_winning() self._played_cards.itemAt(self._cards_played).layout().\ itemAt(2).widget().setText("WINNING") self._cards_played += 1 # reset player info def reset_player_info(self): # set player info as waiting for index in range(0, 4): self._player_info[index].setText("Waiting for player") # change the card layout so that number is the player at the base of the # interface. player 0 is there by default. def rearrange_card_layout(self, number): b = self._card_layout[-number:] b.extend(self._card_layout[:-number]) self._card_layout = b # same for self._player_info c = self._player_info[-number:] c.extend(self._player_info[:-number]) self._player_info = c # check for new input from our Client regularly # this means that our GUI is not being blocked for waiting # also note that multiprocess poll is not the same as subprocess poll, # hence the need for Client on seperate process # see MsgTypes enum for details on message contents def handle_client_input(self): # slow down interaction for when you are playing with bots after = POLL_DELAY # check if game is still going if self._parent_conn == None: return # attempt to poll try: self._parent_conn.poll() except: # if we cannot poll it is because we could not connect to the server QMessageBox.information(self, '500', "Failed to connect") self.exit_to_menu() return # repeatedly poll if we have input from Client while self._parent_conn.poll(): data = self.recieve_from_client() print(data) # recieve game details if data["type"] is MsgType.PLAYER: self._player = data["player"] self._players = data["players"] # reset details in case we are restarting self.reset_players_hands() self.reset_player_info() self.reset_cards_played() self.reset_bet_controls() # we want to arrange the self._card_layout indexes so that # index 0 becomes the new index self._player. self.rearrange_card_layout(self._player) # update deck on screen # we only will add the click event either during choosing the kitty # or when the first round begins self.update_player_hand(data["players"][self._player]["deck"]) self.activate_card_controls(set=False) # update all info on screen for index in range(0, NUMBER_PLAYERS): self._player_info[index].setText( str(index + 1) + ": " + data["players"][index]["name"] + os.linesep) # enable bet controls on our bet elif data["type"] is MsgType.BETOURS: # activate bet controls self.activate_bet_controls(set=True) elif data["type"] is MsgType.BETINFO: # reset error text and disable bet controls if this was us if data["player"] == self._player: self.activate_bet_controls(set=False) # add betting text self._player_info[data["player"]].setText( self._player_info[data["player"]].text() + os.linesep + string_to_bet(data["bet"])) if MIN_TIME_BETWEEN_MOVES: after = MIN_TIME_BETWEEN_MOVES break # display why the users bet failed elif data["type"] is MsgType.BETFAILED: QMessageBox.information(self, '500', data["message"]) # disable controls after betting is done elif data["type"] is MsgType.BETWON: self.activate_bet_controls(set=False) if MIN_TIME_BETWEEN_MOVES: after = MIN_TIME_BETWEEN_MOVES break # update the deck with the kitty cards elif data["type"] is MsgType.KITTYDECK: self._players[self._player]["deck"] = data["deck"] self.update_player_hand(data["deck"], setEvent=True) # activate card controls on kitty elif data["type"] is MsgType.KITTYCHOOSE: self.activate_card_controls(set=True) # we are required to choose a joker suit elif data["type"] is MsgType.JOKERCHOOSE: # create message box with suit choices msg = QMessageBox() msg.setWindowTitle('500') msg.setText('Choose a joker suit.') msg.addButton(QPushButton('Spades'), QMessageBox.YesRole) msg.addButton(QPushButton('Clubs'), QMessageBox.YesRole) msg.addButton(QPushButton('Diamonds'), QMessageBox.YesRole) msg.addButton(QPushButton('Hearts'), QMessageBox.YesRole) ret = msg.exec_() # send our chosen suit to the client if ret == 0: ret = "S" elif ret == 1: ret = "C" elif ret == 2: ret = "D" elif ret == 3: ret = "H" self.send_to_client(ret) # waiting on another player to choose joker suit elif data["type"] is MsgType.JOKERSTART: QMessageBox.information(self, '500', "A player is choosing joker suit.") # joker suit chosen elif data["type"] is MsgType.JOKERDONE: QMessageBox.information( self, '500', "The joker is a " + letter_to_suit(data["suit"]) + ".") # update bet (cards are now sorted by suit) elif data["type"] is MsgType.GAMESTART: self.reset_player_hand_after_kitty() self._players[self._player]["deck"] = data["deck"] self.update_player_hand(data["deck"], setEvent=True) # new round elif data["type"] is MsgType.ROUNDNEW: # reset number of cards played self.reset_cards_played() self._round = data["round"] # we can play a card, activate card controls elif data["type"] is MsgType.ROUNDCHOOSE: self.activate_card_controls(set=True) # card has been played elif data["type"] is MsgType.ROUNDCARDPLAYED: # remove card from the hand self.activate_card_controls(set=False) self.remove_card_from_hand(data["player"], data["card"]) # play card to self._played_cards, updating winning text # if the winning card is different self.add_card_played( data["card"], data["player"], bool(self._card_winning != data["winningcard"])) self._card_winning = data["winningcard"] # only have a break if we are not next if MIN_TIME_BETWEEN_MOVES and \ not (data["player"] + 1) % NUMBER_PLAYERS == self._player: after = MIN_TIME_BETWEEN_MOVES break # player played bad card, show them a message elif data["type"] is MsgType.ROUNDBAD: QMessageBox.information(self, '500', data["message"]) # also case that we are in the last round of play elif data["type"] is MsgType.ROUNDWON: # remove card from the hand self.activate_card_controls(set=False) self.remove_card_from_hand(data["player"], data["card"]) # play card to self._played_cards self.add_card_played( data["card"], data["player"], bool(self._card_winning != data["winningcard"])) self._card_winning = None if MIN_TIME_BETWEEN_MOVES: after = MIN_TIME_BETWEEN_MOVES break # game over elif data["type"] is MsgType.GAMEOVER: QMessageBox.information(self, '500', "Game over!") self.exit_to_menu() return # repeat QtCore.QTimer.singleShot(after, self.handle_client_input) # when user presses join def join(self, port, password, ip, username): # communication self._parent_conn, child_conn = Pipe() # this call is blocking if the game loop method is in our controller class! self._client = Process(target=Client, args=( [ip, port, password, username], child_conn, )) self._client.start() # switch to game view self._stacked_layout.setCurrentIndex(2) # check for new data sent by Client in regular intervals QtCore.QTimer.singleShot(POLL_DELAY, self.handle_client_input) # sends data to the client, must be a string (non blocking) def send_to_client(self, data): if self._parent_conn != None: self._parent_conn.send(data) # recieves data from client (blocking) def recieve_from_client(self): if self._parent_conn != None: return self._parent_conn.recv() return None # when user presses the go button def create_game(self): # these details are always required details port = str(self.get_port()) if port == "" or port == "0": QMessageBox.information(self, '500', "Please enter a valid port") return username = self.get_username() if username == "": QMessageBox.information(self, '500', "Please enter a username") return # rest of the details are different for each options type if self._options_type == 0: password = "******" ptypes = "0222" ip = get_localhost_ip() self.create_server(port, password, ptypes) else: # get password for next 2 cases password = self.get_password() if password == "": QMessageBox.information(self, '500', "Please enter a password") return if self._options_type == 1: ip = self.get_ip() # check if ip is valid, ensure it is seperated by 4 dots and # all are numbers between 0 and 255 check = ip.split('.') error = 0 if len(check) == 4: for index in range(0, 4): if (check[index].isnumeric() == True and int(check[index]) >= 0 and int(check[index]) < 256): error += 1 if error != 4: QMessageBox.information(self, '500', "Please enter a valid IP address") return elif self._options_type == 2: ip = get_localhost_ip() ptypes = ["0"] for index in range(0, 3): ptypes.append(str(self.get_player_type(index))) self.create_server(port, password, ''.join(ptypes)) self.join(port, password, ip, username) # ensure we exit only after cleaning up def closeEvent(self, event): self.close_subprocesses() return QWidget.closeEvent(self, event) # closes server and client subprocesses def close_subprocesses(self): if self._server != None: self._server.kill() self._server = None self._parent_conn = None print("Server Terminated") if self._client != None: self._client.terminate() self._client = None print("Client Terminated") # change layout and close server and client subprocesses def exit_to_menu(self): self._stacked_layout.setCurrentIndex(0) self.close_subprocesses() self.reset() # reset game variables def reset(self): # reset all parts of gui self.reset_player_info() self.reset_bet_controls() self.reset_cards_played() self.reset_player_hand_after_kitty() self.activate_card_controls(set=False) # the below resets the hand locations, so ensure all card # related resetting done before this self.reset_players_hands() # reset all player details self._player = 0 self._players = None self._round = None self._cards_played = 0 # creates the server def create_server(self, port, password, playertypes): # create server args srvargs = ["./server", port, password, playertypes] # create server self._server = subprocess.Popen(srvargs, stdout=subprocess.DEVNULL) print("Server Created.")
class QRuleEditor(QDialog): def __init__(self, project, editor, rule): # Rule is some fontFeatures object self.project = project self.editor = editor self.inputslots = [] self.precontextslots = [] self.postcontextslots = [] self.outputslots = [] self.buffer_direction = "RTL" self.buffer_script = "Latin" self.all_valuerecord_editors = [] self.index = None if rule: self.backup_rule = Rule.fromXML(rule.toXML()) # Deep copy else: self.backup_rule = None super(QRuleEditor, self).__init__() splitter = QSplitter() self.slotview = QHBoxLayout() scroll = QScrollArea() scroll.setLayout(self.slotview) self.outputview_before = QBufferRenderer( project, VariationAwareBuffer(self.project.font)) self.outputview_after = QBufferRenderer( project, VariationAwareBuffer(self.project.font)) self.before_after = QWidget() self.before_after_layout_v = QVBoxLayout() self.asFea = QLabel() featureButtons = QWidget() self.featureButtonLayout = QHBoxLayout() featureButtons.setLayout(self.featureButtonLayout) self.selectedFeatures = [] layoutarea = QWidget() self.before_after_layout_h = QHBoxLayout() self.before_after_layout_h.addWidget(self.outputview_before) self.before_after_layout_h.addWidget(self.outputview_after) layoutarea.setLayout(self.before_after_layout_h) if self.project.variations: self.master_selection = QComboBox() for mastername in self.project.variations.masters: self.master_selection.addItem(mastername) self.master_selection.currentTextChanged.connect( self.masterChanged) self.before_after_layout_v.addWidget(self.master_selection) else: self.master_selection = None self.before_after_layout_v.addWidget(featureButtons) self.before_after_layout_v.addWidget(self.asFea) self.before_after_layout_v.addWidget(layoutarea) self.before_after.setLayout(self.before_after_layout_v) splitter.setOrientation(Qt.Vertical) splitter.addWidget(scroll) splitter.addWidget(self.before_after) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) for button in buttons.buttons(): button.setDefault(False) button.setAutoDefault(False) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) v_box_1 = QVBoxLayout() self.setLayout(v_box_1) v_box_1.addWidget(splitter) v_box_1.addWidget(buttons) self.setRule(rule) @property def currentMaster(self): if not self.master_selection: return None return self.master_selection.currentText() def masterChanged(self): for qvre in self.all_valuerecord_editors: qvre.change_master(self.currentMaster) self.resetBuffer() def keyPressEvent(self, evt): return def accept(self): self.editor.fontfeaturespanel.lookuplist.update(self.index) self.editor.setWindowModified(True) self.editor.showDebugger() def reject(self): for k in dir(self.backup_rule): self.rule = getattr(self.backup_rule, k) self.editor.fontfeaturespanel.lookuplist.update() self.editor.showDebugger() def setRule(self, rule, index=None): self.rule = rule self.index = index self.arrangeSlots() self.representative_string = self.makeRepresentativeString() self.resetBuffer() @property def location(self): sourceIndex = list(self.project.variations.masters.keys()).index( self.currentMaster) return self.project.variations.designspace.sources[ sourceIndex].location def resetBuffer(self): if self.rule: try: self.asFea.setText(self.rule.asFea()) except Exception as e: print("Can't serialize", e) self.outputview_before.set_buf(self.makeBuffer("before")) self.outputview_after.set_buf(self.makeBuffer("after")) if self.currentMaster: self.outputview_before.set_location(self.location) self.outputview_after.set_location(self.location) @pyqtSlot() def changeRepresentativeString(self): l = self.sender() if l.text().startswith("@"): self.representative_string[ l.slotnumber] = self.project.fontfeatures.namedClasses[ l.text()[1:]][0] else: self.representative_string[l.slotnumber] = l.text() self.resetBuffer() @pyqtSlot() def replacementChanged(self): l = self.sender() replacements = l.text().split() self.rule.replacement = [[x] for x in replacements] self.resetBuffer() @pyqtSlot() def addGlyphToSlot(self): l = self.sender() glyphname = l.text() # Check for class names if (glyphname.startswith("@") and glyphname[1:] in self.project.fontfeatures.namedClasses.keys()): # It's OK pass elif glyphname not in self.project.font.keys(): print(f"{glyphname} not found") l.setText("") return print("Adding ", glyphname) l.owner.contents[l.owner.slotindex].append(glyphname) self.arrangeSlots() self.representative_string = self.makeRepresentativeString() self.resetBuffer() @pyqtSlot() def removeGlyphFromSlot(self): l = self.sender() del l.contents[l.slotindex][l.indexWithinSlot] self.arrangeSlots() self.representative_string = self.makeRepresentativeString() self.resetBuffer() @pyqtSlot() def addRemoveSlot(self): sender = self.sender() action = sender.text() if action == "<+": sender.contents.insert(0, []) # If these are input glyphs, add another replacement etc. if sender.contents == self.rule.shaper_inputs(): if isinstance(self.rule, Positioning): self.rule.valuerecords.insert(0, ValueRecord()) elif (isinstance(self.rule, Substitution) and len(self.rule.shaper_inputs()) == 1): self.rule.replacement.insert(0, []) elif isinstance(self.rule, Chaining): self.rule.lookups.insert(0, []) elif action == "+>": sender.contents.append([]) # If these are input glyphs, add another replacement etc. if sender.contents == self.rule.shaper_inputs(): if isinstance(self.rule, Positioning): self.rule.valuerecords.append(ValueRecord()) elif (isinstance(self.rule, Substitution) and len(self.rule.shaper_inputs()) == 1): self.rule.replacement.append([]) elif isinstance(self.rule, Chaining): self.rule.lookups.append([]) elif action == "-": del sender.contents[self.sender().ix] self.arrangeSlots() self.representative_string = self.makeRepresentativeString() self.resetBuffer() def makeASlot(self, slotnumber, contents, style=None, editingWidgets=None): for ix, glyphslot in enumerate(contents): slot = QWidget() slotLayout = QVBoxLayout() if style: slot.setStyleSheet(style) scroll = QScrollArea() scroll.setWidgetResizable(True) scrollWidget = QWidget() scrollLayout = QVBoxLayout() scrollWidget.setLayout(scrollLayout) scroll.setWidget(scrollWidget) for ixWithinSlot, glyph in enumerate(glyphslot): glyphHolder = QWidget() glyphHolderLayout = QHBoxLayout() glyphHolder.setLayout(glyphHolderLayout) l = QPushButton(glyph) l.setDefault(False) l.setAutoDefault(False) l.slotnumber = slotnumber l.clicked.connect(self.changeRepresentativeString) glyphHolderLayout.addWidget(l) remove = QPushButton("x") remove.slotindex = ix remove.indexWithinSlot = ixWithinSlot remove.contents = contents remove.clicked.connect(self.removeGlyphFromSlot) glyphHolderLayout.addWidget(remove) scrollLayout.addWidget(glyphHolder) slotLayout.addWidget(scroll) # This is the part that adds a new glyph to a slot newglyph = QGlyphName(self.project, allow_classes=True) newglyph.slotindex = ix newglyph.contents = contents newglyph.glyphline.returnPressed.connect(self.addGlyphToSlot) slotLayout.addWidget(newglyph) slotLayout.addStretch() if editingWidgets and ix < len(editingWidgets): slotLayout.addWidget(editingWidgets[ix]) pushbuttonsArea = QWidget() pushbuttonsLayout = QHBoxLayout() pushbuttonsArea.setLayout(pushbuttonsLayout) if ix == 0: addASlotLeft = QPushButton("<+") addASlotLeft.contents = contents addASlotLeft.clicked.connect(self.addRemoveSlot) pushbuttonsLayout.addWidget(addASlotLeft) pushbuttonsLayout.addStretch() if not (editingWidgets and len(contents) == 1): removeASlot = QPushButton("-") removeASlot.contents = contents removeASlot.ix = ix removeASlot.clicked.connect(self.addRemoveSlot) pushbuttonsLayout.addWidget(removeASlot) pushbuttonsLayout.addStretch() if ix == len(contents) - 1: addASlotRight = QPushButton("+>") addASlotRight.contents = contents addASlotRight.clicked.connect(self.addRemoveSlot) pushbuttonsLayout.addWidget(addASlotRight) slotLayout.addWidget(pushbuttonsArea) slotnumber = slotnumber + 1 slot.setLayout(slotLayout) self.slotview.addWidget(slot) return slotnumber def lookupCombobox(self, current, warning): c = QComboBox() c.warning = warning names = [ x.name for x in self.project.fontfeatures.routines if not hasattr(x, "comment") ] names = ["--- No lookup ---"] + names for name in names: c.addItem(name) if current in names: c.setCurrentIndex(names.index(current)) self.setComboboxWarningIfNeeded(c) return c @pyqtSlot() def chainingLookupChanged(self): l = self.sender() if l.currentIndex() == 0: self.rule.lookups[l.ix] = [] else: self.rule.lookups[l.ix] = [RoutineReference(name=l.currentText())] self.setComboboxWarningIfNeeded(l) self.resetBuffer() def changesGlyphstringLength(self, routine, depth=1): if depth > 10: return False for r in routine.rules: if isinstance(r, Substitution) and len(r.input) != len(r.replacement): return True elif isinstance(r, Chaining): for lus in r.lookups: for l in (lus or []): if self.changesGlyphstringLength(l.routine, depth + 1): return True return False def setComboboxWarningIfNeeded(self, combobox): # Find routine rname = combobox.currentText() warningNeeded = False if rname: routine = None for r in self.project.fontfeatures.routines: if r.name == rname: routine = r if routine and self.changesGlyphstringLength(routine): stdicon = self.style().standardIcon(QStyle.SP_MessageBoxWarning) combobox.warning.setPixmap( stdicon.pixmap(stdicon.actualSize(QSize(16, 16)))) combobox.warning.setToolTip( "<qt>This lookup may change the length of the glyph stream. Subsequent lookups may not fire at the glyph slots you expect.</qt>" ) else: combobox.warning.clear() combobox.warning.setToolTip("") def addPrecontext(self): self.rule.precontext = [[]] self.arrangeSlots() def addPostcontext(self): self.rule.postcontext = [[]] self.arrangeSlots() def makeEditingWidgets(self): editingWidgets = [] if isinstance(self.rule, Substitution): replacements = [x[0] for x in self.rule.replacement if x] widget = QGlyphName(self.project, multiple=len(self.rule.shaper_inputs()) < 2) widget.setText(" ".join(replacements) or "") widget.position = 0 widget.returnPressed.connect(self.replacementChanged) editingWidgets.append(widget) else: for ix, i in enumerate(self.rule.shaper_inputs()): if isinstance(self.rule, Positioning): widget = QValueRecordEditor(self.rule.valuerecords[ix], vf=self.project.variations, master=self.currentMaster) widget.changed.connect(self.resetBuffer) editingWidgets.append(widget) self.all_valuerecord_editors.append(widget) elif isinstance(self.rule, Chaining): lookup = self.rule.lookups[ix] and self.rule.lookups[ix][ 0].name w = QWidget() wl = QHBoxLayout(w) w.setLayout(wl) warning = QLabel() widget = self.lookupCombobox(lookup, warning) widget.ix = ix widget.currentTextChanged.connect( self.chainingLookupChanged) wl.addWidget(widget) wl.addWidget(warning) editingWidgets.append(w) return editingWidgets def clearLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: self.clearLayout(item.layout()) def arrangeSlots(self): self.all_valuerecord_editors = [] self.clearLayout(self.slotview) if not self.rule: return slotnumber = 0 if not hasattr(self.rule, "precontext") or not self.rule.precontext: widget = QPushButton("<<+") widget.clicked.connect(self.addPrecontext) self.slotview.addWidget(widget) else: self.slotview.addStretch() slotnumber = self.makeASlot(slotnumber, self.rule.precontext, precontext_style) editingWidgets = self.makeEditingWidgets() slotnumber = self.makeASlot(slotnumber, self.rule.shaper_inputs(), editingWidgets=editingWidgets) if not hasattr(self.rule, "postcontext") or not self.rule.postcontext: widget = QPushButton("+>>") widget.clicked.connect(self.addPostcontext) self.slotview.addWidget(widget) else: self.makeASlot(slotnumber, self.rule.postcontext, postcontext_style) self.slotview.addStretch() def makeRepresentativeString(self): inputglyphs = [] if not self.rule: return inputglyphs # "x and x[0]" thing because slots may be empty if newly added if hasattr(self.rule, "precontext"): inputglyphs.extend([x and x[0] for x in self.rule.precontext]) inputglyphs.extend([x and x[0] for x in self.rule.shaper_inputs()]) if hasattr(self.rule, "postcontext"): inputglyphs.extend([x and x[0] for x in self.rule.postcontext]) representative_string = [x for x in inputglyphs if x] for ix, g in enumerate(representative_string): if (g.startswith("@") and g[1:] in self.project.fontfeatures.namedClasses.keys()): representative_string[ ix] = self.project.fontfeatures.namedClasses[g[1:]][0] # We use this representative string to guess information about # how the *real* shaping process will take place; buffer direction # and script, and hence choice of complex shaper, and hence from # that choice of features to be processed. unicodes = [ self.project.font.codepointForGlyph(x) for x in representative_string ] unicodes = [x for x in unicodes if x] tounicodes = "".join(map(chr, unicodes)) bufferForGuessing = Buffer(self.project.font, unicodes=tounicodes) self.buffer_direction = bufferForGuessing.direction self.buffer_script = bufferForGuessing.script # print("Guessed buffer direction ", self.buffer_direction) # print("Guessed buffer script ", self.buffer_script) shaper = Shaper(self.project.fontfeatures, self.project.font) bufferForGuessing = Buffer(self.project.font, glyphs=representative_string) shaper.execute(bufferForGuessing) self.availableFeatures = [] for stage in shaper.stages: if not isinstance(stage, list): continue for f in stage: if (f not in self.availableFeatures and f in self.project.fontfeatures.features): self.availableFeatures.append(f) self.makeFeatureButtons() return representative_string def makeFeatureButtons(self): self.clearLayout(self.featureButtonLayout) for f in self.availableFeatures: self.selectedFeatures.append(f) featureButton = QCheckBox(f) featureButton.setChecked(True) featureButton.stateChanged.connect(self.resetBuffer) self.featureButtonLayout.addWidget(featureButton) def makeShaperFeatureArray(self): features = [] for i in range(self.featureButtonLayout.count()): item = self.featureButtonLayout.itemAt(i).widget() features.append({"tag": item.text(), "value": item.isChecked()}) return features def makeBuffer(self, before_after="before"): buf = VariationAwareBuffer( self.project.font, direction=self.buffer_direction, ) if self.project.variations: buf.location = self.location buf.vf = self.project.variations buf.items = [ VariationAwareBufferItem.new_glyph(g, self.project.font, buf) for g in self.representative_string ] shaper = Shaper(self.project.fontfeatures, self.project.font) shaper.execute(buf, features=self.makeShaperFeatureArray()) routine = Routine(rules=[self.rule]) # print("Before shaping: ", buf.serialize()) if before_after == "after" and self.rule: print("Before application: ", buf.serialize()) print(self.rule.asFea()) buf.clear_mask() # XXX try: routine.apply_to_buffer(buf) except Exception as e: print("Couldn't shape: " + str(e)) print("After application: ", buf.serialize()) return buf
class Connect_Directions(QObject): """ This class is used to connect the directions modifications at the GUI level. It modifies the module according to the newly selected parameter value. """ signal_paramChanged = pyqtSignal(str, list) # ---------------------------------------------------------------------- def __init__(self, paramName, chosen_value, all_Values, nb_directions): """ Constructor """ super().__init__() self.paramName = paramName self.l = QHBoxLayout() self.chosen_value = chosen_value self.create_the_comboBoxes(chosen_value, all_Values, nb_directions) # ---------------------------------------------------------------------- def create_the_comboBoxes(self, chosen_value, all_Values, nb_directions): """ Creates nb_directions directions, list the possible values and select the chosen_value. paramName = Name of the parameter corresponding to the widget to create chosen_value = the subject's specific parameter values. all_Values = list of all possible values for a parameter nb_directions = number of directions to add. """ nb_val = range(len(chosen_value)) for i in nb_val: self.l.addWidget( self.add_To_ComboBox(all_Values, chosen_value[i], i)) # Add a vertical separator if i != nb_directions: add_v_separator(self.l) nb_val = range(len(chosen_value), nb_directions) for i in nb_val: self.l.addWidget(self.add_To_ComboBox(all_Values, None, i)) # Add a vertical separator if i != nb_val[-1]: add_v_separator(self.l) # ---------------------------------------------------------------------- def add_To_ComboBox(self, values, chosenValue, pos): """ Add the possibles values found in the structure file to a QComboBox. Highlight the subject's specific value. values = list of values. chosenValue = subject's specific value. pos = QComboBox position in the directions list """ templateChoices = QComboBox_Directions(pos) # Iterates over the possible choices for val in values: templateChoices.addItem(str(val), val) if val == chosenValue: templateChoices.setCurrentText(val) templateChoices.signal_paramChanged[int, object].connect(self.on_modify) return templateChoices #---------------------------------------------------------------------- def clear_hBoxLayout(self): """ #Removes all the widgets added to the layout """ for i in reversed(range(self.l.count())): self.l.itemAt(i).widget().setParent(None) @pyqtSlot(str, str) #---------------------------------------------------------------------- def on_new_tdef_file(self, key, trigger_file): """ Update the QComboBox with the new events from the new tdef file. """ self.clear_hBoxLayout() tdef = trigger_def(trigger_file) nb_directions = 4 # Convert 'None' to real None (real None is removed when selected in the GUI) tdef_values = [None if i == 'None' else i for i in list(tdef.by_name)] self.create_the_comboBoxes(self.chosen_value, tdef_values, nb_directions) @pyqtSlot(int, object) # ---------------------------------------------------------------------- def on_modify(self, pos, new_Value): """ Slot connected to comboBox param value change pos = QComboBox position in the directions list new_value = direction new_value """ if pos >= len(self.chosen_value): self.chosen_value.append(new_Value) else: self.chosen_value[pos] = (new_Value) try: self.chosen_value.remove(None) except: pass self.signal_paramChanged[str, list].emit(self.paramName, self.chosen_value)
class AddNewKey(QFrame): """ QFrame popup widget for adding new element """ def __init__(self, connection: sqlite3.Connection, cursor: sqlite3.Cursor, secret_key: str, data: typing.Union[typing.List, None], parent: object): super().__init__(parent) self.parent = parent self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.connection = connection self.cursor = cursor self.secret_key = secret_key self.data = data self.w = self.parent.width() self.h = self.parent.height() self.setFixedSize(self.w, self.h) self.setObjectName("AddFrame") self.setStyleSheet(qframe_css.add_new_key_style) # Elements current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") close_button = QPushButton() close_button.setObjectName("close") close_button.setFixedSize(QSize(13, 13)) close_button.setCursor(Qt.PointingHandCursor) close_button.setIcon(QIcon(":/quit")) close_button.clicked.connect(self._close) if not self.data: self.combo = QComboBox() self.combo.setObjectName("combo") self.combo.addItems( ("Web Sites", "Web Accounts", "Emails", "Credit Cards", "E-commerce", "Secrets", "Software", "Forums")) self.combo.currentTextChanged.connect(self.generate_child_list) self.child = QLineEdit() self.child.setPlaceholderText("Branch *") self.title = QLineEdit() self.title.setPlaceholderText("Title *") self.name = QLineEdit() self.name.setPlaceholderText("Login") self.email = QLineEdit() self.email.setPlaceholderText("Email") self.password = QLineEdit() self.password.setPlaceholderText("Password *") self.password.setEchoMode(QLineEdit.Password) self.url = QLineEdit() self.url.setPlaceholderText("Url") self.phone = QLineEdit() self.phone.setPlaceholderText("Phone") self.created = QLineEdit() self.created.setPlaceholderText("Create Date *") if not self.data: self.created.setText(current_time) else: self.created.setText(self.data[6]) self.created.setEnabled(False) self.modified = QLineEdit() self.modified.setPlaceholderText("Modified Date *") self.modified.setText(current_time) self.modified.setEnabled(False) if not self.data: for elements in (self.combo, self.child): elements.setFixedSize(400, 40) elements.setObjectName("field") for elements in (self.title, self.name, self.email, self.password, self.url, self.phone, self.created, self.modified): elements.setFixedSize(400, 40) elements.setObjectName("field") save_button = QPushButton() save_button.setObjectName("save") save_button.setText("Сохранить") save_button.setFixedSize(300, 40) save_button.setCursor(Qt.PointingHandCursor) save_button.clicked.connect(self._save) # Animation self.animation = AnimationService(self, b"geometry", 200, QRect(0, -self.h, self.w, self.h), QRect(0, 0, self.w, self.h)).init_animation() self.animation.start() # Timer self.timer = QTimer(self) self.timer.setInterval(210) self.timer.timeout.connect(self._quit) # LAYOUTS if not self.data: self.cc = QHBoxLayout() self.cc.setContentsMargins(0, 6, 0, 4) self.cc.setAlignment(Qt.AlignCenter) layout = QVBoxLayout() layout.addWidget(close_button, alignment=Qt.AlignRight) layout.addSpacing(10) if not self.data: layout.addWidget(self.combo, alignment=Qt.AlignCenter) layout.addLayout(self.cc) layout.addWidget(self.child, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.title, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.name, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.email, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.password, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.url, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.phone, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.created, alignment=Qt.AlignCenter) layout.addSpacing(10) layout.addWidget(self.modified, alignment=Qt.AlignCenter) layout.addSpacing(20) layout.addWidget(save_button, alignment=Qt.AlignCenter) layout.addStretch(1) self.setLayout(layout) self.title.setFocus(True) if not self.data: self.generate_child_list() else: self.title.setText(self.data[0]) self.name.setText(self.data[1]) self.email.setText(self.data[2]) self.url.setText(self.data[4]) self.phone.setText(self.data[5]) def generate_child_list(self): self.sender().blockSignals(True) for pos in reversed(range(self.cc.count())): curr_item = self.cc.takeAt(pos).widget() if curr_item is not None: curr_item.deleteLater() sql = "SELECT DISTINCT child " \ "FROM passwords " \ "WHERE parent=? " \ "ORDER BY child ASC" query = self.cursor.execute(sql, (self.combo.currentText(), )) for item in query.fetchall(): r_elem = QRadioButton() r_elem.setObjectName("child-element") r_elem.setText(item[0]) r_elem.setFixedHeight(20) r_elem.setCursor(Qt.PointingHandCursor) r_elem.clicked.connect(self.child_text_replace) self.cc.addWidget(r_elem) self.sender().blockSignals(False) def child_text_replace(self): self.child.setText(self.sender().text()) def _save(self): crypt_password = run_encode(self.secret_key, self.password.text().encode("utf-8")) if not self.data: sql = "INSERT INTO passwords " \ "(parent, child, title, login, email, password, url, phone, created, modified) " \ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" self.cursor.execute( sql, (self.combo.currentText(), self.child.text(), self.title.text(), self.name.text(), self.email.text(), crypt_password, self.url.text(), self.phone.text(), self.created.text(), self.modified.text())) else: sql = "UPDATE passwords " \ "SET title=?, login=?, email=?, password=?, url=?, phone=?, modified=? " \ "WHERE id=?" self.cursor.execute( sql, (self.title.text(), self.name.text(), self.email.text(), crypt_password, self.url.text(), self.phone.text(), self.modified.text(), self.data[3])) self.connection.commit() self._close() chime.success() def _close(self): self.animation.setStartValue(QRect(0, 0, self.w, self.h)) self.animation.setEndValue(QRect(0, -self.h, self.w, self.h)) self.animation.start() self.timer.start() def _quit(self): self.close() self.deleteLater() self.parent._add = None def keyPressEvent(self, event: QEvent) -> None: """ Track key press """ if event.key() == Qt.Key_Escape: self._close()
class MyWindow(QWidget): """main window""" def __init__(self): super().__init__() self.auths = {} self.activeaccounts = [] self.streams = [] self.tweets = [] self.tagarray = [] self.tweettags = [] self.receivetags = [] self.following = [] self.searchstream = None self.init_main() self.init_accounts() self.init_tweets() self.init_widgets() self.show() sys.exit(app.exec_()) def init_main(self): """options of main window""" self.setGeometry(300, 100, 1000, 600) self.setWindowTitle("tomoebi") self.timer = QTimer() self.timer.timeout.connect(self.update_timeline) self.timer.start(500) def init_tweets(self): """create initial tweet""" self.tweets = ["start"] #initialize widgets def init_widgets(self): """initialize widgets""" #upper half of main window consists of accounts, composer and buttons self.compose_vbox = QVBoxLayout() self.accounts_hbox = QHBoxLayout() self.accbuttons = [] for a in self.auths: accbutton = QPushButton(self) accbutton.setWhatsThis(a) accbutton.setCheckable(True) accbutton.toggled.connect(self.choose_account) accbutton.setChecked(True) accbutton.setIcon(PyQt5.QtGui.QIcon('images/' + a + '.jpg')) accbutton.setIconSize(QSize(48, 48)) self.accounts_hbox.addWidget(accbutton) self.addaccbutton = QPushButton("+", self) self.addaccbutton.clicked.connect(self.add_account) self.accounts_hbox.addWidget(self.addaccbutton) self.composer = ComposeTextEdit(self) #self.composer.setPlaceholderText("いまなにしてる?") self.composer.setMaximumHeight(60) self.compose_hbox = QHBoxLayout() self.imagebutton = QPushButton("image", self) self.submitbutton = QPushButton("tweet", self) self.submitbutton.clicked.connect(self.submit) self.hashtag_hbox = QHBoxLayout() self.hashtagedit = QLineEdit(self) self.hashtagedit.setPlaceholderText("hashtags") self.hashtagbutton = QPushButton("set") self.hashtagbutton.setCheckable(True) self.hashtagbutton.toggled.connect(self.sethashtag) self.hashtag_hbox.addWidget(self.hashtagedit) self.hashtag_hbox.addWidget(self.hashtagbutton) self.compose_hbox.addWidget(self.imagebutton) self.compose_hbox.addWidget(self.submitbutton) self.compose_vbox.addLayout(self.accounts_hbox) self.compose_vbox.addWidget(self.composer) self.compose_vbox.addLayout(self.compose_hbox) self.compose_vbox.addLayout(self.hashtag_hbox) #lower half of main window consists of timeline l = QTextEdit() l.setPlainText(self.tweets[0]) l.setReadOnly(True) l.setFixedHeight(350) self.inner = QWidget() self.timeline_vbox = QVBoxLayout(self.inner) self.timeline_vbox.addWidget(l) self.tweets = [] self.timeline_vbox.addStretch() self.scroll = QScrollArea() self.scroll.setWidgetResizable(True) self.scroll.setWidget(self.inner) #integrate upper and lower part of main window self.whole_vbox = QVBoxLayout() self.whole_vbox.addLayout(self.compose_vbox) self.whole_vbox.addWidget(self.scroll) #right half of the main window self.image_collumn = QVBoxLayout() self.imageinner = QWidget() self.imagetimeline = QVBoxLayout(self.imageinner) self.imagescroll = QScrollArea() self.imagescroll.setWidgetResizable(True) self.imagescroll.setWidget(self.imageinner) self.imagetext = QTextEdit() self.imagetext.setMaximumHeight(60) self.imagetext.setReadOnly(True) self.image_collumn.addWidget(self.imagetext) self.image_collumn.addWidget(self.imagescroll) self.whole_hbox = QHBoxLayout() self.whole_hbox.addLayout(self.whole_vbox) self.whole_hbox.addLayout(self.image_collumn) self.setLayout(self.whole_hbox) #initialize registered accounts def init_accounts(self): """load account AT and AS from local and create api object and stream""" if not os.path.exists("images"): os.mkdir("images") if os.path.isfile("auth.json"): with open('auth.json', 'r') as f: authdic = json.load(f) for name, keys in authdic["Twitter"].items(): api = twitter.connect(keys["ACCESS_TOKEN"], keys["ACCESS_SECRET"]) self.auths[name] = api #self.following = self.following + api.friends_ids(k) self.streams.append( twitter.open_userstream(api, self.receive_tweet, name)) if not os.path.isfile("images/" + name + ".jpg"): twitter.getmyicon(api, name) #twitter.open_filterstream(self.auths["XXXX"], self.receive_tweet, # "XXXX", [str(x) for x in self.following]) else: default = {"Twitter": {}, "Mastodon": {}} with open('auth.json', 'w') as f: json.dump(default, f, indent=2) self.authdic = {} def add_account(self): """add account and register it to local file""" api, screen_name = twitter.authentication() self.auths[screen_name] = api self.streams.append( twitter.open_userstream(api, self.receive_tweet, screen_name)) twitter.getmyicon(api, screen_name) accbutton = QPushButton(self) accbutton.setWhatsThis(screen_name) accbutton.setCheckable(True) accbutton.toggled.connect(self.choose_account) accbutton.setIcon(PyQt5.QtGui.QIcon('images/' + screen_name + '.jpg')) accbutton.setIconSize(QSize(48, 48)) self.accounts_hbox.insertWidget(self.accounts_hbox.count() - 1, accbutton) def choose_account(self): """ called when accbutton are toggled. add or remove active accounts """ acc = self.sender() if acc.isChecked(): self.activeaccounts.append(acc.whatsThis()) else: self.activeaccounts.remove(acc.whatsThis()) def receive_tweet(self, status, name, icon): """called when stream receive a tweet""" self.tweets.append((status, name, icon)) def update_timeline(self): """called every 500ms and update gui timeline according to self.tweets""" for t in self.tweets: if hasattr(t[0], "in_reply_to_status_id"): self.addTweet(*t) elif hasattr(t[0], "event"): self.addEvent(*t) self.tweets = [] def addTweet(self, t, name, icon): rtby = None if hasattr(t, "retweeted_status"): rtby = [t.user.profile_image_url_https, t.user.screen_name] t = t.retweeted_status tweet = self.create_tweet(t) tweet_hbox = QHBoxLayout() if icon: if not glob.glob("images/" + t.user.screen_name + ".*"): twitter.geticon(t.user.profile_image_url_https, t.user.screen_name) icon = PyQt5.QtGui.QPixmap( glob.glob("images/" + t.user.screen_name + ".*")[0]) scaled_icon = icon.scaled(QSize(48, 48), 1, 1) iconviewer = IconLabel(t.id, name, self.retweet, self.reply) iconviewer.setPixmap(scaled_icon) icon_vbox = QVBoxLayout() icon_vbox.addWidget(iconviewer, alignment=Qt.AlignTop) if rtby: if not glob.glob("images/" + rtby[1] + ".*"): twitter.geticon(*rtby) icon = PyQt5.QtGui.QPixmap( glob.glob("images/" + rtby[1] + ".*")[0]) scaled_icon = icon.scaled(QSize(24, 24), 1, 1) rticonviewer = QLabel() rticonviewer.setPixmap(scaled_icon) rticon_hbox = QHBoxLayout() #rticon_hbox.addStretch() rticon_hbox.addWidget(rticonviewer, alignment=(Qt.AlignRight | Qt.AlignTop)) icon_vbox.addLayout(rticon_hbox) icon_vbox.addStretch() tweet_hbox.addLayout(icon_vbox) tweet_hbox.addWidget(tweet) favbutton = QPushButton("fav") favbutton.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) favbutton.setCheckable(True) favbutton.toggled.connect(lambda: self.fav(t.id, name)) tweet_hbox.addWidget(favbutton) self.timeline_vbox.insertLayout(0, tweet_hbox) if "media" in t.entities: images = twitter.get_allimages(self.auths[name], t.id) self.imagetext.setPlainText("@" + t.user.screen_name + "\n" + t.text) for n, _ in enumerate(images): pixmap = PyQt5.QtGui.QPixmap() pixmap.loadFromData(QByteArray(images[n])) scaled = pixmap.scaled(QSize(320, 180), 1, 1) imageviewer = QLabel() imageviewer.setPixmap(scaled) self.imagetimeline.insertWidget(0, imageviewer) def addEvent(self, t, name, icon): if t.event == "favorite" and not (t.source["screen_name"] in self.auths.keys()): text = t.source["screen_name"] + " favored " + t.target_object[ "text"] favLabel = QLabel(text) self.timeline_vbox.insertWidget(0, favLabel) def create_tweet(self, t): """create tweet widget""" text = "@" + t.user.screen_name + "\n" + t.text tweetdocument = PyQt5.QtGui.QTextDocument() tweetdocument.setTextWidth( 300) #this line is not working so it needs to be fixed someday tweetdocument.setPlainText(text) tweettext = QTextEdit() tweettext.setDocument(tweetdocument) tweettext.setReadOnly(True) tweettext.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) tweettext.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) tweettext.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) tweettext.setAttribute(103) tweettext.show() tweettext.setFixedHeight(tweettext.document().size().height() + tweettext.contentsMargins().top() * 2) return tweettext def submit(self): """called when tweet button is pressed and submit tweet""" if not self.activeaccounts: return submittext = self.composer.toPlainText() for t in self.tweettags: submittext = submittext + " " + t if not submittext: return for a in self.activeaccounts: self.auths[a].update_status(submittext) self.composer.setPlainText("") def fav(self, tweetid, name): '''favor or unfavor a tweet from an account from which the tweet was obtained''' switch = self.sender() if switch.isChecked(): try: self.auths[name].create_favorite(tweetid) except: print("already favored") else: try: self.auths[name].destroy_favorite(tweetid) except: print("not favored") def retweet(self, id, account): self.auths[account].retweet(id) def reply(self, id, account): if not self.activeaccounts: return submittext = self.composer.toPlainText() if not submittext: return self.auths[account].update_status(submittext, in_reply_to_status_id=id, auto_populate_reply_metadata=True) self.composer.setPlainText("") def sethashtag(self): """set hashtab for receive and tweet""" switch = self.sender() if switch.isChecked(): htinput = self.hashtagedit.text() htlist = htinput.strip().split() for t in htlist: if not t[0] == "*": self.receivetags.append(t) self.tweettags.append(t) else: self.receivetags.append(t[1:]) repl_screen_name = list(self.auths.keys())[0] self.searchstream = twitter.open_filterstream( self.auths[repl_screen_name], self.receive_tweet, repl_screen_name, self.receivetags) else: self.receivetags = [] self.tweettags = [] self.searchstream.disconnect() def closeEvent(self, event): """called when gui window is closed and terminate all streams and thread""" for s in self.streams: s.disconnect() os._exit(1)
class PadCalc(QWidget): def __init__(self): super().__init__() load_data() card_tags = ['leader','sub1','sub2','sub3','sub4','friend'] self.cards = { t: CardIcon() for t in card_tags } self.vlayout = QVBoxLayout(self) self.vlayout.setSpacing(0) self.setLayout(self.vlayout) self.userbox = QHBoxLayout() userfield = QLineEdit() userbutton = QPushButton('Load') userbutton.clicked.connect(lambda: self.set_user(userfield.text())) self.userbox.addWidget(userfield) self.userbox.addWidget(userbutton) userfield.returnPressed.connect(userbutton.click) self.vlayout.addLayout(self.userbox) maxcheckbox = QCheckBox('Use maxed stats?') maxcheckbox.stateChanged[int].connect(self.setMaxed) self.vlayout.addWidget(maxcheckbox) self.teamchooser = QComboBox(self) self.teamchooser.currentIndexChanged[int].connect(self.set_team) self.vlayout.addWidget(self.teamchooser) teambox = QHBoxLayout() teambox.addStretch(1) for card in card_tags: teambox.addWidget(self.cards[card]) teambox.setSpacing(0) teambox.addStretch(1) teambox.setAlignment(Qt.AlignCenter) self.vlayout.addLayout(teambox) self.board = Board() self.vlayout.addWidget(self.board) self.vlayout.itemAt(self.vlayout.indexOf(self.board)).setAlignment(Qt.AlignCenter) self.orbchooser = QHBoxLayout() b = OrbButton(value = 0) b.clicked.connect(functools.partial(self.setPaintOrb,Orb.Null)) self.orbchooser.addWidget(b) for i in ORBS: b = OrbButton(value=i) #print('Setting click value of button %s to %s' % (id(b),i)) b.clicked.connect(functools.partial(self.setPaintOrb,i)) self.orbchooser.addWidget(b) self.vlayout.addLayout(self.orbchooser) self.damagereadout = QLabel() font = QFont() font.setPointSize(30) self.damagereadout.setAlignment(Qt.AlignCenter) self.damagereadout.setFont(font) self.vlayout.addWidget(self.damagereadout) self.board.valueChanged.connect(self.update_damage) labels = ['atk','combos','leaders','enhance','prongs','rows'] lfont = QFont() lfont.setPointSize(9) vfont = QFont() vfont.setPointSize(12) self.details = {key: QVBoxLayout() for key in labels} for i in labels: label = QLabel(i) label.setFont(lfont) label.setAlignment(Qt.AlignCenter) label.setMargin(0) label.setContentsMargins(0,0,0,0) label.setIndent(0) self.details[i].label = label self.details[i].addWidget(self.details[i].label) value = QLabel('1') value.setFont(vfont) value.setAlignment(Qt.AlignCenter) value.setMargin(0) value.setIndent(0) value.setContentsMargins(0,0,0,0) self.details[i].value = value self.details[i].addWidget(self.details[i].value) self.details[i].setContentsMargins(1,1,1,1) self.detailreadout = QHBoxLayout() for i in labels: self.detailreadout.addLayout(self.details[i]) timeslabel = QLabel('\u00d7') timeslabel.setMargin(0) timeslabel.setIndent(0) timeslabel.setAlignment(Qt.AlignCenter) timeslabel.setContentsMargins(0,0,0,0) self.detailreadout.addWidget(timeslabel) self.detailreadout.takeAt(self.detailreadout.count()-1) self.vlayout.addLayout(self.detailreadout) self.vlayout.addStretch(1000) self.skillbox = QHBoxLayout() self.vlayout.addLayout(self.skillbox) #self.set_user('korora') def setMaxed(self,state): Card.use_max_stats = (state == Qt.Checked) self.drawui() self.update_damage() self.set_team(self.teamchooser.currentIndex()) def setPaintOrb(self,orb): global paintOrb paintOrb = orb def set_user(self,username): newuser = User(username) if hasattr(self,'user'): olduser = self.user.username else: olduser = '' if hasattr(newuser,'teams') and len(newuser.teams) > 0: teamchooser = self.teamchooser self.user = newuser index = teamchooser.currentIndex() try: teamchooser.currentIndexChanged[int].disconnect() except: return teamchooser.clear() for team in self.user.teams: teamchooser.addItem('%s' % (team['name'])) if newuser.username != olduser: self.set_team(0) else: teamchooser.setCurrentIndex(index) self.set_team(index) teamchooser.currentIndexChanged[int].connect(self.set_team) def update_damage(self): (match,enhanced,row) = self.board.match() nmatch = sum(len(v) for v in match.values()) nrow = sum(v for v in row.values()) (dmg, multipliers) = compute_damage((match,enhanced,row),self.team) self.damagereadout.setText('{:,}'.format(round(sum([sum(i) for i in dmg.values()])))) for i in multipliers: if i is not 'atk': self.details[i].value.setText('%.2f' % multipliers[i]) else: self.details[i].value.setText('%d' % multipliers[i]) for card in self.cards.values(): # add a damage label dam = dmg[card.card] card.main_attack = dam[0] card.sub_attack = dam[1] card.repaint() def set_team(self,index): teamdata = self.user.teams[index] team = [] for i in ['leader','sub1','sub2','sub3','sub4']: team += [Card().load_from_id(self.user,teamdata[i])] friend = { 'monster': teamdata['friend_leader'], 'plus_atk': teamdata['friend_atk'], 'plus_hp': teamdata['friend_hp'], 'plus_rcv': teamdata['friend_rcv'], 'current_awakening': teamdata['friend_awakening'], 'lv': teamdata['friend_level'], 'current_skill': teamdata['friend_skill'] } team += [Card().load_from_card(friend)] self.team = Team(team) #print('|'+self.teamchooser.itemText(index)+'|') #print(len(self.teamchooser.itemText(index))) self.teamchooser.setMinimumContentsLength(len(self.teamchooser.itemText(index))-3) for i in range(self.skillbox.count()): w = self.skillbox.takeAt(i) w.widget().deleteLater() #svc = SkillViewController(skill=self.team.lskills[0]) #svc.skillsChanged.connect(self.update_damage) #self.skillbox.addWidget(svc) self.drawui() self.update_damage() def drawui(self): self.cards['leader'].card = (self.team.cards[0]) self.cards['sub1'].card = (self.team.cards[1]) self.cards['sub2'].card = (self.team.cards[2]) self.cards['sub3'].card = (self.team.cards[3]) self.cards['sub4'].card = (self.team.cards[4]) self.cards['friend'].card = (self.team.cards[5]) for card in self.cards.values(): card.load_icon()
class ParameterContainer(QWidget, object): """Container to hold Parameter Widgets.""" def __init__( self, parameters=None, description_text='', extra_parameters=None, parent=None, vertical=True): """Constructor .. versionadded:: 2.2 :param parameters: List of Parameter Widget :type parameters: list :param description_text: Text for description of the parameter container. :type description_text: str """ super().__init__(parent) # attributes if not parameters: self.parameters = [] else: self.parameters = parameters self.description_text = description_text self.extra_parameters = extra_parameters self.parent = parent self.validators = [] self.validators_kwargs = [] # UI if vertical: self.vertical_layout = QVBoxLayout() else: self.vertical_layout = QHBoxLayout() self.widget = QWidget() self.description_label = QLabel() self.description_label.setWordWrap(True) self.scroll_area = QScrollArea() self.group_frame = QFrame() self.qt5_parameter_factory = Qt5ParameterFactory() self.main_layout = QGridLayout() # NOTES(IS) : These functions are commented since the architecture is not # ready yet. # def register_widget(self, parameter, parameter_widget): # """Register new custom widget. # # :param parameter: # :type parameter: GenericParameter # # :param parameter_widget: # :type parameter_widget: GenericParameterWidget # """ # self.qt5_parameter_factory.register_widget( # parameter, parameter_widget) # # def remove_widget(self, parameter): # """Register new custom widget. # # :param parameter: # :type parameter: GenericParameter # """ # if parameter.__name__ in self.dict_widget.keys(): # self.dict_widget.pop(parameter.__name__) def get_parameters(self, validate=True): """Return list of parameters from the current state of widget. :param validate: If true, run validator, else no. :type validate: bool :returns: List of parameter :rtype: list """ if validate: validation_result = self.validate() if not validation_result['valid']: raise InvalidValidationException(validation_result['message']) parameter_widgets = self.get_parameter_widgets() parameters = [] for widget_item in parameter_widgets: parameter_widget = widget_item.widget() parameter = parameter_widget.get_parameter() parameters.append(parameter) # returns based on the object type of self.parameters if isinstance(self.parameters, list): return parameters else: # just return single parameter return parameters[0] def get_parameter_widgets(self): """Return list of parameter widgets from the current state of widget. :returns: List of parameter widget :rtype: list """ parameter_widgets = [self.vertical_layout.itemAt(i) for i in range( self.vertical_layout.count())] return parameter_widgets def setup_ui(self, must_scroll=True): """Setup the UI of this parameter container. """ # Vertical layout to place the parameter widgets self.vertical_layout.setContentsMargins(0, 0, 0, 0) self.vertical_layout.setSpacing(0) # Widget to hold the vertical layout self.widget = QWidget() self.widget.setLayout(self.vertical_layout) # Label for description self.description_label.setText(self.description_text) self.group_frame.setLineWidth(0) self.group_frame.setFrameStyle(QFrame.NoFrame) vlayout = QVBoxLayout() vlayout.setContentsMargins(0, 0, 0, 0) vlayout.setSpacing(0) self.group_frame.setLayout(vlayout) if must_scroll: vlayout.addWidget(self.scroll_area) self.scroll_area.setWidgetResizable(True) self.scroll_area.setWidget(self.widget) else: vlayout.addWidget(self.widget) # Main layout of the container if self.description_text: self.main_layout.addWidget(self.description_label) self.main_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.main_layout) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.MinimumExpanding) if not isinstance(self.parameters, list): parameters = [self.parameters] else: parameters = self.parameters if len(parameters) == 0: self.set_empty_parameters() return self.main_layout.addWidget(self.group_frame) self.qt5_parameter_factory = Qt5ParameterFactory() if self.extra_parameters is not None: for extra_parameter in self.extra_parameters: if (type(extra_parameter) == tuple and len(extra_parameter) == 2): self.qt5_parameter_factory.register_widget( extra_parameter[0], extra_parameter[1]) for parameter in parameters: parameter_widget = self.qt5_parameter_factory.get_widget(parameter) parameter_widget.setAutoFillBackground(True) self.vertical_layout.addWidget(parameter_widget) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) def set_description(self, description): """Set description of the parameter container. :param description: A new description fot the parameter container. :type description: str """ self.description_text = description self.description_label.setText(self.description_text) def set_empty_parameters(self): """Update UI if there is no parameters in the container. """ new_description = self.description_text new_description += '\n' new_description += 'But, currently there is no parameters available.' self.description_label.setText(new_description) def add_validator(self, validator, **kwargs): """Add validator for this parameter container. :param validator: validator function for this parameter container. :type validator: function """ validator.parent = self self.validators.append(validator) if kwargs: self.validators_kwargs.append(kwargs) else: self.validators_kwargs.append({}) def validate(self): """Validate of all rule for all parameter in this container. :return: True if all valid, False :rtype: dict """ for i in range(len(self.validators)): validator = self.validators[i] validator_kwargs = self.validators_kwargs[i] validation_result = validator(self, **validator_kwargs) if not validation_result['valid']: return validation_result return { 'valid': True, 'message': '' } def get_parameter_by_guid(self, parameter_guid): """Return a parameter based on its uuid :param parameter_guid: The parameter uuid :type parameter_guid: str :returns: The parameter or None if not exist :rtype: GenericParameter, None """ parameters = self.get_parameters(validate=False) for parameter in parameters: if parameter.guid == parameter_guid: return parameter return None def get_parameter_widget_by_guid(self, parameter_guid): """Return a parameter widget based on its uuid :param parameter_guid: The parameter uuid :type parameter_guid: str :returns: The parameter widget or None if not exist :rtype: GenericParameterWidget, None """ parameter_widgets = self.get_parameter_widgets() for parameter_widget in parameter_widgets: if (parameter_widget.widget().get_parameter().guid == parameter_guid): return parameter_widget.widget() return None
def refresh(self): addons = sorted(self._addons, key=lambda item: item.name) prev_checked = set(addon.identifier for addon in self.__checked_addons) prev_unchecked = set(addon.identifier for addon in self.__addons if addon not in self.__checked_addons) self.__addons = list(addons) self.__checked_addons = set() button_factory = self._addon_button_factory() self.setRowCount(len(self.__addons)) for no, addon in enumerate(self.__addons): check_widget = QWidget() check_layout = QHBoxLayout() check_layout.setAlignment(Qt.AlignCenter) check_check = QCheckBox() if addon.identifier not in prev_unchecked: check_check.setChecked(True) self.__checked_addons.add(addon) check_check.toggled.connect(partial(self.__check_checked, addon)) check_check.setFocusPolicy(Qt.NoFocus) check_layout.addWidget(check_check) check_widget.setLayout(check_layout) self.setCellWidget(no, 0, check_widget) if addon.icon: icon_widget = QWidget() icon_layout = QVBoxLayout() icon_layout.setSpacing(0) icon_widget.setLayout(icon_layout) icon_label = QLabel() icon_label.setAutoFillBackground(False) icon_label.setPixmap(image_loader.load(addon.icon)) icon_label.setAlignment(Qt.AlignTop) icon_layout.addWidget(icon_label) lp, tp, rp, bp = icon_layout.getContentsMargins() icon_layout.setContentsMargins(lp, tp, 0, bp) self.setCellWidget(no, 1, icon_widget) layout = QVBoxLayout() layout.setSpacing(0) layout.setAlignment(Qt.AlignTop) name_layout = QHBoxLayout() name_layout.setSpacing(20) name_layout.setAlignment(Qt.AlignLeft) name_label = QLabel(addon.name) name_label.setAutoFillBackground(False) name_label.setTextFormat(Qt.PlainText) font = name_label.font() font.setWeight(QFont.Bold) name_label.setFont(font) name_layout.addWidget(name_label) if isinstance(addon, OnlineAddOn): if self.__show_prev_version: version = "{0} ⟶ {1}".format(addon.local_addon.version, addon.latest_version.version) else: version = addon.latest_version.version else: version = addon.version version_label = QLabel(str(version)) version_label.setAutoFillBackground(False) version_label.setTextFormat(Qt.PlainText) name_layout.addWidget(version_label) layout.addLayout(name_layout) if addon.description: description_label = QLabel(addon.description) description_label.setAutoFillBackground(False) description_label.setTextFormat(Qt.PlainText) description_label.setWordWrap(True) layout.addWidget(description_label) addon_button_box = QHBoxLayout() addon_button_box.setAlignment(Qt.AlignRight) addon_button_box_widget = QWidget() addon_button_box_widget.setLayout(addon_button_box) if self.EXPANDING_BUTTON_BOX: addon_button_box_widget.setVisible(False) addon_button_box_widget.setObjectName("button_box") if button_factory is not None: button_factory.add_buttons(addon, addon_button_box, addon_button_box_widget) if addon_button_box.count() > 0: layout.addWidget(addon_button_box_widget) widget = QWidget() widget.setLayout(layout) self.setCellWidget(no, 2, widget) self.__refresh_selection_colors(widget, False) self.resizeColumnsToContents() self.resizeRowsToContents() cur_checked = set(addon.identifier for addon in self.__checked_addons) if prev_checked != cur_checked: self.check_changed.emit()
class LineEdit(QLineEdit): inactiveText = QtDynamicProperty('inactiveText', unicode) widgetSpacing = QtDynamicProperty('widgetSpacing', int) def __init__(self, parent=None, contents=u""): super(LineEdit, self).__init__(contents, parent) box_direction = QBoxLayout.RightToLeft if self.isRightToLeft() else QBoxLayout.LeftToRight self.inactiveText = u"" self.left_widget = SideWidget(self) self.left_widget.resize(0, 0) self.left_layout = QHBoxLayout(self.left_widget) self.left_layout.setContentsMargins(0, 0, 0, 0) self.left_layout.setDirection(box_direction) self.left_layout.setSizeConstraint(QLayout.SetFixedSize) self.right_widget = SideWidget(self) self.right_widget.resize(0, 0) self.right_layout = QHBoxLayout(self.right_widget) self.right_layout.setContentsMargins(0, 0, 0, 0) self.right_layout.setDirection(box_direction) self.right_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.widgetSpacing = 2 self.left_widget.sizeHintChanged.connect(self._update_text_margins) self.right_widget.sizeHintChanged.connect(self._update_text_margins) @property def left_margin(self): return self.left_widget.sizeHint().width() + 2*self.left_layout.spacing() @property def right_margin(self): return self.right_widget.sizeHint().width() + 2*self.right_layout.spacing() def _update_text_margins(self): self.setTextMargins(self.left_margin, 0, self.right_margin, 0) self._update_side_widget_locations() def _update_side_widget_locations(self): option = QStyleOptionFrame() self.initStyleOption(option) spacing = self.right_layout.spacing() text_rect = self.style().subElementRect(QStyle.SE_LineEditContents, option, self) text_rect.adjust(spacing, 0, -spacing, 0) mid_height = text_rect.center().y() + 1 - (text_rect.height() % 2) # need -1 correction for odd heights -Dan if self.left_layout.count() > 0: left_height = mid_height - self.left_widget.height()/2 left_width = self.left_widget.width() if left_width == 0: left_height = mid_height - self.left_widget.sizeHint().height()/2 self.left_widget.move(text_rect.x(), left_height) text_rect.setX(self.left_margin) text_rect.setY(mid_height - self.right_widget.sizeHint().height()/2.0) text_rect.setHeight(self.right_widget.sizeHint().height()) self.right_widget.setGeometry(text_rect) def event(self, event): event_type = event.type() if event_type == QEvent.LayoutDirectionChange: box_direction = QBoxLayout.RightToLeft if self.isRightToLeft() else QBoxLayout.LeftToRight self.left_layout.setDirection(box_direction) self.right_layout.setDirection(box_direction) elif event_type == QEvent.DynamicPropertyChange: property_name = event.propertyName() if property_name == 'widgetSpacing': self.left_layout.setSpacing(self.widgetSpacing) self.right_layout.setSpacing(self.widgetSpacing) self._update_text_margins() elif property_name == 'inactiveText': self.update() return QLineEdit.event(self, event) def resizeEvent(self, event): self._update_side_widget_locations() QLineEdit.resizeEvent(self, event) def paintEvent(self, event): QLineEdit.paintEvent(self, event) if not self.hasFocus() and not self.text() and self.inactiveText: options = QStyleOptionFrame() self.initStyleOption(options) text_rect = self.style().subElementRect(QStyle.SE_LineEditContents, options, self) text_rect.adjust(self.left_margin+2, 0, -self.right_margin, 0) painter = QPainter(self) painter.setPen(self.palette().brush(QPalette.Disabled, QPalette.Text).color()) painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter, self.inactiveText) def addHeadWidget(self, widget): if self.isRightToLeft(): self.right_layout.insertWidget(1, widget) else: self.left_layout.addWidget(widget) def addTailWidget(self, widget): if self.isRightToLeft(): self.left_layout.addWidget(widget) else: self.right_layout.insertWidget(1, widget) def removeWidget(self, widget): self.left_layout.removeWidget(widget) self.right_layout.removeWidget(widget) widget.hide()
class MainWindow(QMainWindow): def __init__(self, dataBaseName): super().__init__() self._dataBase = myDatabase.MyDataBase(dataBaseName) self._dictexe = { "первый запрос": (self.firstQuery, self.firstExe), "второй запрос": (self.secondQuery, self.secondExe), "третий запрос": (self.thirdQuery, self.thirdExe) } self._view = QTreeView() self._buttonAdd = QPushButton("Добавить") self._buttonAdd.clicked.connect(self.getItems) self._addSpinBox = QSpinBox() self._addComboBox = QComboBox() self._addComboBox.addItems(self._dataBase.dishes()) self._layout = QHBoxLayout() self._qSpinBox = QSpinBox() self._qComboBox = QComboBox() self._qLineEdit = QLineEdit() self._qCalendarWidget = QCalendarWidget() self._queryDisc = QLabel() self._buttonExe = QPushButton("Исполнить") self._buttonExe.clicked.connect(self.onButtonExe) self._combox = QComboBox() self._combox.currentTextChanged.connect(self.comboChanged) self._combox.addItems(self._dictexe.keys()) self.initUi() def initUi(self): self.setGeometry(300, 300, 200, 200) self.setWindowTitle('Ресторан') #self.setWindowIcon(QIcon('')) w = QWidget() mainLayout = QVBoxLayout() w.setLayout(mainLayout) self.setCentralWidget(w) mainLayout.addWidget(self._view) tmpLayout = QHBoxLayout() mainLayout.addLayout(tmpLayout) tmpLayout.addWidget(QLabel("Добавления ингредиента")) tmpLayout.addWidget(self._addSpinBox) tmpLayout.addWidget(self._addComboBox) tmpLayout.addWidget(self._buttonAdd) mainLayout.addWidget(self._queryDisc) tmpLayout = QHBoxLayout() mainLayout.addLayout(tmpLayout) tmpLayout.addWidget(self._combox) tmpLayout.addLayout(self._layout) tmpLayout.addWidget(self._buttonExe) self.adjustSize() def comboChanged(self, text): self._dictexe[text][0]() def clearLayout(self): while self._layout.count() > 0: self._layout.itemAt(0).widget().setParent(None) #Названия и калорийность блюд по рецептам автора X def firstQuery(self): self._queryDisc.setText( "Названия и калорийность блюд по рецептам автора X") self.clearLayout() #self._qSpinBox.setValue(0) #self._layout.insertWidget(1,self._qSpinBox) self._qComboBox.clear() self._qComboBox.addItems(self._dataBase.authors()) self._layout.insertWidget(1, self._qComboBox) def firstExe(self): model = self._dataBase.first(self._qComboBox.currentText()) self.setModel(model) #Названия ресторанов, к которым относятся повара, готовящие блюда содержащие в #названии подстроку X (например, «картофельный»), отсортированные по алфавиту def secondQuery(self): self._queryDisc.setText( "Названия ресторанов, к которым относятся повара,\n готовящие блюда содержащие в названии подстроку X (например, «картофельный»),\n отсортированные по алфавиту" ) self.clearLayout() self._qLineEdit.clear() self._layout.insertWidget(1, self._qLineEdit) def secondExe(self): model = self._dataBase.second(self._qLineEdit.text()) self.setModel(model) #Названия и количества ингредиентов и названия мероприятий, на которых разливают #напитки в количестве меньше X после даты Y def thirdQuery(self): self._queryDisc.setText( "Названия и количества ингредиентов и названия мероприятий, на которых разливают\n напитки в количестве меньше X после даты Y" ) self.clearLayout() self._layout.insertWidget(1, self._qCalendarWidget) self._qSpinBox.setMaximum(self._dataBase.maxDrinkCount() * 10) self._qSpinBox.setValue(0) self._layout.insertWidget(1, self._qSpinBox) def thirdExe(self): model = self._dataBase.third( self._qSpinBox.value(), self._qCalendarWidget.selectedDate().toPyDate()) self.setModel(model) def setModel(self, model): if model is None: return self._view.setVisible(False) self._view.setModel(model) for i in range(model.columnCount()): self._view.resizeColumnToContents(i) self._view.setVisible(True) self.adjustSize() def onButtonExe(self): self._dictexe[self._combox.currentText()][1]() def getItems(self): name, ok = QInputDialog.getText(self, "Ингредиент", "Введите название") if not ok: return self._dataBase.add(self._addSpinBox.value(), self._addComboBox.currentText(), name)
class EstimhabW(StatModUseful): """ The Estimhab class provides the graphical interface for the version of the Estimhab model written in HABBY. The Estimhab model is described elsewhere. EstimhabW() just loads the data for Estimhab given by the user. """ save_signal_estimhab = pyqtSignal() """ PyQtsignal to save the Estimhab data. """ def __init__(self, path_prj, name_prj): super().__init__() self.tab_name = "estimhab" self.tab_position = 7 self.model_type = "Estimhab" self.eq50 = QLineEdit() self.esub = QLineEdit() self.path_prj = path_prj self.name_prj = name_prj self.path_bio_estimhab = os.path.join(self.path_bio, 'estimhab') self.total_lineedit_number = 1 self.init_iu() self.process_manager = MyProcessManager( "estimhab_plot") # SC (Suitability Curve) self.read_estimhab_dict() self.fill_input_data() self.fill_fish_name() self.check_if_ready_to_compute() self.eq1.textChanged.connect(self.check_if_ready_to_compute) self.eq2.textChanged.connect(self.check_if_ready_to_compute) self.ew1.textChanged.connect(self.check_if_ready_to_compute) self.ew2.textChanged.connect(self.check_if_ready_to_compute) self.eh1.textChanged.connect(self.check_if_ready_to_compute) self.eh2.textChanged.connect(self.check_if_ready_to_compute) self.eq50.textChanged.connect(self.check_if_ready_to_compute) self.eqmin.textChanged.connect(self.check_if_ready_to_compute) self.eqmax.textChanged.connect(self.check_if_ready_to_compute) self.esub.textChanged.connect(self.check_if_ready_to_compute) self.selected_aquatic_animal_qtablewidget.model().rowsInserted.connect( self.check_if_ready_to_compute) self.selected_aquatic_animal_qtablewidget.model().rowsRemoved.connect( self.check_if_ready_to_compute) def init_iu(self): """ This function is used to initialized an instance of the EstimhabW() class. It is called by __init__(). **Technical comments and walk-through** First we looked if some data for Estimhab was saved before by an user. If yes, we will fill the GUI with the information saved before. Estimhab information is saved in hdf5 file format and the path/name of the hdf5 file is saved in the xml project file. So we open the xml project file and look if the name of an hdf5 file was saved for Estimhab. If yes, the hdf5 file is read. The format of hdf5 file is relatively simple. Each input data for Estimhab has its own dataset (qmes, hmes, wmes, q50, qrange, and substrate). Then, we have a list of string which are a code for the fish species which were analyzed. All the data contained in hdf5 file is loaded into variable. The different label are written on the graphical interface. Then, two QListWidget are modified. The first list contains all the fish species on which HABBY has info (see XML Estimhab format for more info). The second list is the fish selected by the user on which Estimhab will be run. Here, we link these lists with two functions so that the user can select/deselect fish using the mouse. The function name are add_fish() and remove_fish(). Then, we fill the first list. HABBY look up all file of xml type in the “Path_bio” folder (the one indicated in the xml project file under the attribute “Path_bio”). The name are them modified so that the only the name of species appears (and not the full path). We set the layout with all the different QLineEdit where the user can write the needed data. Estimhab model is saved using a function situated in MainWindows_1.py (frankly, I am not so sure why I did put the save function there, but anyway). So the save button just send a signal to MainWindows here, which save the data. """ available_model_label = QLabel(self.tr('Available')) selected_model_label = QLabel(self.tr('Selected')) self.lineedit_width = 50 self.spacer_width = 50 # input q1_layout = QHBoxLayout() q1_layout.addWidget(QLabel('Q1 [m<sup>3</sup>/s]')) q1_layout.addWidget(self.eq1) q1_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.eq1.setFixedWidth(self.lineedit_width) q2_layout = QHBoxLayout() q2_layout.addWidget(QLabel('Q2 [m<sup>3</sup>/s]')) q2_layout.addWidget(self.eq2) q2_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.eq2.setFixedWidth(self.lineedit_width) w1_layout = QHBoxLayout() w1_layout.addWidget(QLabel(self.tr("Width1 [m]"))) w1_layout.addWidget(self.ew1) w1_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.ew1.setFixedWidth(self.lineedit_width) w2_layout = QHBoxLayout() w2_layout.addWidget(QLabel(self.tr("Width2 [m]"))) w2_layout.addWidget(self.ew2) w2_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.ew2.setFixedWidth(self.lineedit_width) h1_layout = QHBoxLayout() h1_layout.addWidget(QLabel(self.tr("Height1 [m]"))) h1_layout.addWidget(self.eh1) self.eh1.setFixedWidth(self.lineedit_width) h2_layout = QHBoxLayout() h2_layout.addWidget(QLabel(self.tr("Height2 [m]"))) h2_layout.addWidget(self.eh2) self.eh2.setFixedWidth(self.lineedit_width) q50_layout = QHBoxLayout() q50_layout.addWidget(QLabel('Qmedian/Q50 [m<sup>3</sup>/s]')) q50_layout.addWidget(self.eq50) q50_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.eq50.setFixedWidth(self.lineedit_width) sub_layout = QHBoxLayout() sub_layout.addWidget(QLabel(self.tr('Mean substrate size [m]'))) sub_layout.addWidget(self.esub) sub_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.esub.setFixedWidth(self.lineedit_width) # output q1out_layout = QHBoxLayout() q1out_layout.addWidget(QLabel(self.tr("Qmin [m<sup>3</sup>/s]"))) q1out_layout.addWidget(self.eqmin) q1out_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.eqmin.setFixedWidth(self.lineedit_width) q2out_layout = QHBoxLayout() q2out_layout.addWidget(QLabel(self.tr("Qmax [m<sup>3</sup>/s]"))) q2out_layout.addWidget(self.eqmax) q2out_layout.addItem(QSpacerItem(self.spacer_width, 1)) self.eqmax.setFixedWidth(self.lineedit_width) self.q2target_layout = QHBoxLayout() self.q2target_layout.addWidget( QLabel(self.tr("Qtarget [m<sup>3</sup>/s]"))) self.q2target_layout.addWidget(self.add_qtarget_button) self.q2target_layout.addWidget(self.remove_qtarget_button) self.add_qtarget_button.clicked.connect(self.add_new_qtarget) self.remove_qtarget_button.clicked.connect(self.remove_one_qtarget) # create lists with the possible fishes self.list_f.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_f.setDragDropMode(QAbstractItemView.DragDrop) self.list_f.setDefaultDropAction(Qt.MoveAction) self.list_f.setAcceptDrops(True) self.list_f.setSortingEnabled(True) self.selected_aquatic_animal_qtablewidget.setSelectionMode( QAbstractItemView.ExtendedSelection) self.selected_aquatic_animal_qtablewidget.setDragDropMode( QAbstractItemView.DragDrop) self.selected_aquatic_animal_qtablewidget.setDefaultDropAction( Qt.MoveAction) self.selected_aquatic_animal_qtablewidget.setAcceptDrops(True) self.selected_aquatic_animal_qtablewidget.setSortingEnabled(True) # insist on white background color (for linux, mac) self.setAutoFillBackground(True) p = self.palette() p.setColor(self.backgroundRole(), Qt.white) self.setPalette(p) # send model self.run_stop_button = QPushButton(self.tr('Run Estimhab'), self) self.run_stop_button.clicked.connect(self.run_estmihab) change_button_color(self.run_stop_button, "#47B5E6") self.run_stop_button.setEnabled(False) # empty frame scrolable content_widget = QFrame() # hydraulic_data_group hydraulic_data_group = QGroupBox(self.tr('Hydraulic data input')) hydraulic_data_group.setToolTip( self.tr("Double click to reset the input data group.")) hydraulic_data_layout = QGridLayout(hydraulic_data_group) hydraulic_data_layout.addLayout(q1_layout, 0, 0) hydraulic_data_layout.addLayout(w1_layout, 0, 1) hydraulic_data_layout.addLayout(h1_layout, 0, 2) hydraulic_data_layout.addLayout(q2_layout, 1, 0) hydraulic_data_layout.addLayout(w2_layout, 1, 1) hydraulic_data_layout.addLayout(h2_layout, 1, 2) hydraulic_data_layout.addLayout(q50_layout, 2, 0) hydraulic_data_layout.addLayout(sub_layout, 2, 1) hydraulic_data_group.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.doubleclick_input_group = DoubleClicOutputGroup() hydraulic_data_group.installEventFilter(self.doubleclick_input_group) self.doubleclick_input_group.double_clic_signal.connect( self.reset_hydraulic_data_input_group) # hydraulic_data_output_group hydraulic_data_output_group = QGroupBox( self.tr('Desired hydraulic output data')) hydraulic_data_output_group.setToolTip( self.tr("Double click to reset the outpout data group.")) hydraulic_data_layout = QGridLayout(hydraulic_data_output_group) hydraulic_data_layout.addLayout(q1out_layout, 0, 0) hydraulic_data_layout.addLayout(q2out_layout, 0, 1) hydraulic_data_layout.addLayout(self.q2target_layout, 0, 2) hydraulic_data_output_group.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.doubleclick_output_group = DoubleClicOutputGroup() hydraulic_data_output_group.installEventFilter( self.doubleclick_output_group) self.doubleclick_output_group.double_clic_signal.connect( self.reset_hydraulic_data_output_group) # models_group models_group = QGroupBox(self.tr('Biological models')) models_layout = QGridLayout(models_group) models_layout.addWidget(available_model_label, 0, 0) models_layout.addWidget(selected_model_label, 0, 1) models_layout.addWidget(self.list_f, 1, 0) models_layout.addWidget(self.selected_aquatic_animal_qtablewidget, 1, 1) models_layout.addWidget(self.run_stop_button, 2, 1) self.doubleclick_models_group = DoubleClicOutputGroup() models_group.installEventFilter(self.doubleclick_models_group) self.doubleclick_models_group.double_clic_signal.connect( self.reset_models_group) # gereral_layout self.layout3 = QVBoxLayout(content_widget) self.layout3.addWidget(hydraulic_data_group, Qt.AlignLeft) self.layout3.addWidget(hydraulic_data_output_group) self.layout3.addWidget(models_group) # self.setLayout(self.layout3) self.setWidgetResizable(True) self.setFrameShape(QFrame.NoFrame) self.setWidget(content_widget) def add_new_qtarget(self): # count existing number of lineedit total_widget_number = self.q2target_layout.count() self.total_lineedit_number = total_widget_number - 2 # - first : qlabel and plus and moins button + New lineedit lineedit_name = 'new_qtarget' + str(self.total_lineedit_number) setattr(self, lineedit_name, QLineEdit()) getattr(self, lineedit_name).setFixedWidth(self.lineedit_width) self.target_lineedit_list.append(getattr(self, lineedit_name)) self.q2target_layout.insertWidget(total_widget_number - 2, getattr(self, lineedit_name)) def remove_one_qtarget(self): # count existing number of lineedit total_widget_number = self.q2target_layout.count() self.total_lineedit_number = total_widget_number - 3 # - first : qlabel and plus and moins button - New lineedit if self.total_lineedit_number > 0: self.target_lineedit_list.pop(-1) self.q2target_layout.itemAt(total_widget_number - 3).widget().setParent(None) self.total_lineedit_number = self.total_lineedit_number - 1 def reset_hydraulic_data_input_group(self): # remove txt in lineedit self.eq1.setText("") self.eq2.setText("") self.ew1.setText("") self.ew2.setText("") self.eh1.setText("") self.eh2.setText("") self.eq50.setText("") self.esub.setText("") def reset_hydraulic_data_output_group(self): # remove txt in lineedit self.eqmin.setText("") self.eqmax.setText("") # remove lineedits qtarget for i in reversed(range(1, self.q2target_layout.count() - 1)): self.q2target_layout.itemAt(i).widget().setParent(None) self.total_lineedit_number = self.total_lineedit_number - 1 self.target_lineedit_list = [] def reset_models_group(self): if self.selected_aquatic_animal_qtablewidget.count() > 0: self.selected_aquatic_animal_qtablewidget.clear() self.fill_fish_name() def read_estimhab_dict(self): """ This function opens the json data created by estimhab """ # load_project_properties self.estimhab_dict = load_specific_properties(self.path_prj, [self.model_type])[0] def fill_fish_name(self): """ This function reads all latin fish name from the xml files which are contained in the biological directory related to estimhab and fill GUI fish names """ all_xmlfile = glob.glob(os.path.join(self.path_bio_estimhab, r'*.xml')) if self.estimhab_dict: selected_fish = self.estimhab_dict["fish_list"] else: selected_fish = [] for f in all_xmlfile: # open xml try: try: docxml = ET.parse(f) root = docxml.getroot() except IOError: self.send_log.emit( self.tr("Warning: ") + self.tr("The .habby project file ") + f + self.tr(" could not be open.\n")) return except ET.ParseError: self.send_log.emit( self.tr("Warning: ") + self.tr("The .habby project file ") + f + self.tr(" is not well-formed.\n")) return # find fish name fish_name = root.find(".//LatinName") # None is null for python 3 if fish_name is not None: fish_name = fish_name.text.strip() # find fish stage stage = root.find(".//estimhab/stage") # None is null for python 3 if stage is not None: stage = stage.text.strip() if stage != 'all_stage': fish_name += ' ' + stage # check if not selected if fish_name not in selected_fish: # add to the list item = QListWidgetItem(fish_name) item.setData(1, f) self.list_f.addItem(item) else: # add to the list item2 = QListWidgetItem(fish_name) item2.setData(1, f) self.selected_aquatic_animal_qtablewidget.addItem(item2) def fill_input_data(self): if self.estimhab_dict: # input data self.eq1.setText(str(self.estimhab_dict["q"][0])) self.eq2.setText(str(self.estimhab_dict["q"][1])) self.eh1.setText(str(self.estimhab_dict["h"][0])) self.eh2.setText(str(self.estimhab_dict["h"][1])) self.ew1.setText(str(self.estimhab_dict["w"][0])) self.ew2.setText(str(self.estimhab_dict["w"][1])) self.eq50.setText(str(self.estimhab_dict["q50"])) self.eqmin.setText(str(self.estimhab_dict["qrange"][0])) self.eqmax.setText(str(self.estimhab_dict["qrange"][1])) self.esub.setText(str(self.estimhab_dict["substrate"])) # qtarg if len(self.estimhab_dict["qtarg"]) > 0: while self.total_lineedit_number != len( self.estimhab_dict["qtarg"]): self.add_new_qtarget() for qtarg_num, qtarg_value in enumerate( self.estimhab_dict["qtarg"][1:]): getattr(self, 'new_qtarget' + str(qtarg_num + 2)).setText( str(qtarg_value)) def check_if_ready_to_compute(self): all_string_selection = (self.eq1.text(), self.eq2.text(), self.ew1.text(), self.ew2.text(), self.eh1.text(), self.eh2.text(), self.eq50.text(), self.eqmin.text(), self.eqmax.text(), self.esub.text()) # minimum one fish and string in input lineedits to enable run_stop_button if self.selected_aquatic_animal_qtablewidget.count( ) > 0 and "" not in all_string_selection: self.run_stop_button.setEnabled(True) else: self.run_stop_button.setEnabled(False) def change_folder(self): """ A small method to change the folder which indicates where is the biological data """ # user find new path self.path_bio_estimhab = QFileDialog.getExistingDirectory( self, self.tr("Open Directory"), os.getenv('HOME')) # update list self.list_f.clear() all_file = glob.glob(os.path.join(self.path_bio_estimhab, r'*.xml')) # make it look nicer for i in range(0, len(all_file)): all_file[i] = all_file[i].replace(self.path_bio_estimhab, "") all_file[i] = all_file[i].replace("\\", "") all_file[i] = all_file[i].replace(".xml", "") item = QListWidgetItem(all_file[i]) # add them to the menu self.list_f.addItem(item) def run_estmihab(self): """ A function to execute Estimhab by calling the estimhab function. **Technical comment** This is the function making the link between the GUI and the source code proper. The source code for Estimhab is in src/Estimhab.py. This function loads in memory the data given in the graphical interface and call sthe Estimhab model. The data could be written by the user now or it could be data which was saved in the hdf5 file before and loaded when HABBY was open (and the init function called). We check that all necessary data is present and that the data given makes sense (e.g.,the minimum discharge should not be bigger than the maximal discharge, the data should be a float, etc.). We then remove the duplicate fish species (in case the user select one specie twice) and the Estimhab model is called. The log is then written (see the paragraph on the log for more information). Next, the figures created by Estimmhab are shown. As there is only a short number of outputs for Estimhab, we create a figure in all cases (it could be changed by adding a checkbox on the GUI like in the Telemac or other hydrological class). """ # prepare data try: q = [ float(self.eq1.text().replace(",", ".")), float(self.eq2.text().replace(",", ".")) ] w = [ float(self.ew1.text().replace(",", ".")), float(self.ew2.text().replace(",", ".")) ] h = [ float(self.eh1.text().replace(",", ".")), float(self.eh2.text().replace(",", ".")) ] q50 = float(self.eq50.text().replace(",", ".")) qrange = [ float(self.eqmin.text().replace(",", ".")), float(self.eqmax.text().replace(",", ".")) ] qtarget_values_list = [] for qtarg_lineedit in self.target_lineedit_list: if qtarg_lineedit.text(): qtarget_values_list.append( float(qtarg_lineedit.text().replace(",", "."))) substrate = float(self.esub.text().replace(",", ".")) except ValueError: self.send_log.emit('Error: ' + self.tr( 'Some data are empty or not float. Cannot run Estimhab')) return # get the list of xml file fish_list = [] fish_name2 = [] for i in range(0, self.selected_aquatic_animal_qtablewidget.count()): fish_item = self.selected_aquatic_animal_qtablewidget.item(i) fish_item_str = fish_item.text() fish_list.append(os.path.basename(fish_item.data(1))) fish_name2.append(fish_item_str) # check internal logic if not fish_list: self.send_log.emit( 'Error: ' + self.tr('No fish selected. Cannot run Estimhab.')) return if qrange[0] >= qrange[1]: self.send_log.emit('Error: ' + self.tr( 'Minimum discharge bigger or equal to max discharge. Cannot run Estimhab.' )) return if qtarget_values_list: for qtarg in qtarget_values_list: if qtarg < qrange[0] or qtarg > qrange[1]: self.send_log.emit('Error: ' + self.tr( 'Target discharge is not between Qmin and Qmax. Cannot run Estimhab.' )) return if q[0] == q[1]: self.send_log.emit( 'Error: ' + self.tr('Estimhab needs two differents measured discharges.')) return if h[0] == h[1]: self.send_log.emit( 'Error: ' + self.tr('Estimhab needs two different measured height.')) return if w[0] == w[1]: self.send_log.emit( 'Error: ' + self.tr('Estimhab needs two different measured width.')) return if (q[0] > q[1] and h[0] < h[1]) or (q[0] > q[1] and w[0] < w[1]) or (q[1] > q[0] and h[1] < h[0]) \ or (q[1] > q[0] and w[1] < w[0]): self.send_log.emit( 'Error: ' + self.tr('Discharge, width, and height data are not coherent.')) return if q[0] <= 0 or q[1] <= 0 or w[0] <= 0 or w[1] <= 0 or h[0] <= 0 or h[1] <= 0 or qrange[0] <= 0 or qrange[1] <= 0 \ or substrate <= 0 or q50 <= 0: self.send_log.emit('Error: ' + self.tr( 'Negative or zero data found. Could not run Estimhab.')) return if substrate > 3: self.send_log.emit( 'Error: ' + self.tr('Substrate is too large. Could not run Estimhab.')) return self.send_log.emit(self.tr('# Computing: Estimhab...')) # check if the discharge range is realistic with the result self.qall = [q[0], q[1], qrange[0], qrange[1], q50] self.check_all_q() # run and save project_properties = load_project_properties(self.path_prj) sys.stdout = mystdout = StringIO() self.estimhab_dict = dict(q=q, w=w, h=h, q50=q50, qrange=qrange, qtarg=qtarget_values_list, substrate=substrate, path_bio=self.path_bio_estimhab, xml_list=fish_list, fish_list=fish_name2) # change_specific_properties change_specific_properties(self.path_prj, ["Estimhab"], [self.estimhab_dict]) # process state = Value("d", 0) self.p = Process(target=estimhab_mod.estimhab_process, args=(self.estimhab_dict, project_properties, self.path_prj, state), name="Estimhab") self.p.start() self.p.join() # plot plot_attr = lambda: None plot_attr.name_hdf5 = self.name_prj + '_ESTIMHAB' + '.hab' plot_attr.nb_plot = 1 self.process_manager.set_estimhab_plot_mode( self.path_prj, plot_attr, load_project_properties(self.path_prj)) self.process_manager.start() # log info str_found = mystdout.getvalue() str_found = str_found.split('\n') for i in range(0, len(str_found)): if len(str_found[i]) > 1: self.send_log.emit(str_found[i]) self.send_log.emit( self. tr("Estimhab computation done. Figure and text files created in output project folder." )) self.send_log.emit("py data = [" + str(q) + ',' + str(w) + ',' + str(h) + ',' + str(q50) + ',' + str(substrate) + ']') self.send_log.emit("py qrange =[" + str(qrange[0]) + ',' + str(qrange[1]) + ']') self.send_log.emit( "py path1= os.path.join(os.path.dirname(path_bio),'" + self.path_bio_estimhab + "')") fish_list_str = "py fish_list = [" for i in range(0, len(fish_list)): fish_list_str += "'" + fish_list[i] + "'," fish_list_str = fish_list_str[:-1] + ']' self.send_log.emit(fish_list_str) self.send_log.emit( "py [OSI, WUA] = estimhab.estimhab(data[0], data[1], data[2], data[3] ," " qrange, data[4], path1, fish_list, '.', True, {}, '.')\n")
def __init__(self, parent=None): super().__init__(parent) self.record = -1 self.inspected = None self.oob_update = False prefs = QSettings() prefs.beginGroup("/General") timeout = prefs.value("/Timeout") dark_mode = prefs.value("/DarkMode") prefs.endGroup() # Instantiate core objects self.timeout_timer = QTimer() self.timeout_timer.setTimerType(Qt.VeryCoarseTimer) self.timeout_timer.setInterval(timeout * 1000) self.timeout_timer.setSingleShot(True) self.timeout_timer.timeout.connect(self.update_temp_log) self.systems = ActionsWidget(LogSource.SYSTEM) self.systems.acted.connect(self.log_item) self.events = ActionsWidget(LogSource.EVENT) self.events.acted.connect(self.log_item) self.compass = Compass() self.compass_widget = QWidget() compass_layout = QHBoxLayout() self.compass_widget.setLayout(compass_layout) compass_layout.addWidget(self.compass) self.compass.angle_event.connect(self.log_item) self.exact_angle = ExactAngle() self.exact_angle_widget = QWidget() exact_angle_layout = QHBoxLayout() self.exact_angle_widget.setLayout(exact_angle_layout) exact_angle_layout.addWidget(self.exact_angle) self.exact_angle.btn_event.connect(self.reset_timer) self.exact_angle.angle_event.connect(self.log_item) tab_widget = QTabWidget() tab_bar = tab_widget.tabBar() tab_bar.setFont(QFont('Consolas', 12, 3)) tab_widget.addTab(self.compass_widget, "Compass") tab_widget.addTab(self.exact_angle_widget, "Precise Angle") tab_widget.setStyleSheet(""" QTabWidget::pane { border-top: 2px solid #C2C7CB; } /* Style the tab using the tab sub-control. Note that it reads QTabBar _not_ QTabWidget */ QTabBar::tab { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); border: 2px solid #C4C4C3; border-bottom-color: #C2C7CB; /* same as the pane color */ border-top-left-radius: 4px; border-top-right-radius: 4px; min-width: 8ex; padding: 2px; color: black; } QTabBar::tab:selected, QTabBar::tab:hover { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #fafafa, stop: 0.4 #f4f4f4, stop: 0.5 #e7e7e7, stop: 1.0 #fafafa); } QTabBar::tab:selected { border-color: #ff0000; border-bottom-color: #C2C7CB; /* same as pane color */ } QTabBar::tab:!selected { margin-top: 2px; /* make non-selected tabs look smaller */ } """) header_layout = QHBoxLayout() self.zulu_time_label = QLabel() self.assessor_label = QLabel() self.date_label = QLabel() self.dl_label = QLabel() self.mnemonic_label = QLabel() header_layout.addWidget(self.zulu_time_label) header_layout.addWidget(self.assessor_label) header_layout.addWidget(self.date_label) header_layout.addWidget(self.dl_label) header_layout.addWidget(self.mnemonic_label) res = QApplication.primaryScreen().size() w, h = res.width(), res.height() if w > 1920 or h > 1080: hdr_font = QFont("Consolas", 16, 2) end_font = QFont("Consolas", 32, 5) else: hdr_font = QFont("Consolas", 14, 2) end_font = QFont("Consolas", 28, 5) for index in range(header_layout.count()): widget = header_layout.itemAt(index).widget() widget.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Maximum ) widget.setFont(hdr_font) widget.setAlignment(Qt.AlignCenter) # Setup logging state machine self.init_log_sm() # Setup splitters actions_splitter = QSplitter( Qt.Horizontal, frameShape=QFrame.StyledPanel, frameShadow=QFrame.Plain ) actions_splitter.addWidget(self.systems) actions_splitter.addWidget(self.events) actions_splitter.addWidget(tab_widget) actions_splitter.setChildrenCollapsible(False) main_splitter = QSplitter( Qt.Vertical, frameShape=QFrame.StyledPanel, frameShadow=QFrame.Plain ) self.log_area = QTableWidget(0, 3) self.log_area.cellDoubleClicked.connect(self.entry_inspected) self.log_area.cellChanged.connect(self.entry_changed) self.log_area.setHorizontalHeaderLabels( ["Time", "System", "Events"] ) self.log_area.horizontalHeader().setStretchLastSection(True) self.set_dark_mode(dark_mode) end_msn_btn = QPushButton("END\r\nMISSION") end_msn_btn.clicked.connect(self.end_mission) end_msn_btn.setFont(end_font) end_msn_btn.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) end_msn_btn.setStyleSheet("background-color: red; color: white") bottom_layout = QGridLayout() bottom_widget = QWidget() bottom_widget.setLayout(bottom_layout) bottom_layout.addWidget(self.log_area, 0, 0, 1, 7) bottom_layout.addWidget(end_msn_btn, 0, 8, 1, 1) main_splitter.addWidget(actions_splitter) main_splitter.addWidget(bottom_widget) main_splitter.setChildrenCollapsible(False) handle_css = """ QSplitter::handle { background-image: url(:/imgs/dot_pattern.png); background-repeat: repeat-xy; background-color: none; border: 1px solid gray; } QSplitter::handle:pressed { background-image: url(:/imgs/pressed.png); } """ actions_splitter.setStyleSheet(handle_css) main_splitter.setStyleSheet(handle_css) # Finalize layout main_layout = QVBoxLayout() main_layout.addLayout(header_layout) main_layout.addWidget(main_splitter) self.setLayout(main_layout)
class ProblemPage(QWidget): def __init__(self, mode, timer): super().__init__() self.solution_stack = [] self.start_time = 0 self.timer = timer self.mode = mode self.numbers = [] self.target = 0 self.back_button = QPushButton("Back") self.big_number_count = QSpinBox() self.big_number_count.setRange(1, 4) self.generate_number_box = QPushButton("Generate") self.reset_button = QPushButton("Reset") self.reset_button.setDisabled(True) self.number_group = QHBoxLayout() self.set_numbers = QPushButton("Set Numbers") self.set_numbers.setDisabled(True) self.number_line() self.target_window = QLineEdit("Target") self.set_target = QPushButton("Set Target") self.set_target.setDisabled(True) self.target_check = QLabel("Target should be between 100 and 999") self.timer_bar = QProgressBar() self.timer_bar.setRange(0, 100) self.time_keeper = QTimer() self.counter = 0 self.time_keeper.timeout.connect(lambda: self.timer_props()) self.setLayout(self.set_layout()) if self.mode == 0: self.generate_number_box.clicked.connect( lambda: self.generate_button_mode0_action()) self.set_numbers.clicked.connect(lambda: self.set_numbers_action()) self.reset_button.clicked.connect(lambda: self.reset_actions()) self.set_target.clicked.connect( lambda: self.target_button_action()) self.back_button.clicked.connect(lambda: self.reset_actions()) def solve(self): solver = CountDownSolver(self.big_number_count.value(), self.numbers, self.target) print('Solver started') start_time = time.time() solver.solve() self.solution_stack = solver.solution_stack print('solver done') end_time = time.time() print('solver took :', round(end_time - start_time, 3), 'secs') def timer_props(self): self.counter += 1 value = (self.counter / 100) / self.timer * 100 self.timer_bar.setValue(value) if value > 100: self.time_keeper.stop() self.solve() print(self.solution_stack) def target_button_action(self): try: if 100 <= int(self.target_window.text()) <= 999: self.set_target.setDisabled(True) self.target = int(self.target_window.text()) print('valid target: ', self.target) self.time_keeper.start(1) else: self.target = 0 print('target not in range') except ValueError: print('invalid target') def reset_actions(self): self.generate_number_box.setDisabled(False) self.set_numbers.setDisabled(True) self.set_target.setDisabled(True) for i in range(self.number_group.count()): item = self.number_group.itemAt(i).widget() if isinstance(item, QComboBox): for j in range(item.count()): item.removeItem(0) def set_numbers_action(self): self.numbers = [] for i in range(self.number_group.count()): item = self.number_group.itemAt(i).widget() if isinstance(item, QComboBox): self.numbers.append(int(item.currentText())) print(self.numbers) self.set_target.setDisabled(False) def generate_button_mode0_action(self): big = self.big_number_count.value() box = 0 for i in range(self.number_group.count()): item = self.number_group.itemAt(i).widget() if isinstance(item, QComboBox): if box < big: item.addItems(str(25 * j) for j in [1, 2, 3, 4]) elif big <= box < 6: item.addItems(str(j) for j in range(1, 11)) box += 1 self.generate_number_box.setDisabled(True) self.set_numbers.setDisabled(False) self.reset_button.setDisabled(False) def set_layout(self): back_line = QHBoxLayout() back_line.addStretch(1) back_line.addWidget(self.back_button) back_line.addStretch(1) big_number_line = QHBoxLayout() big_number_line.addWidget(QLabel("Big Number Count : ")) big_number_line.addStretch(1) big_number_line.addWidget(self.big_number_count) big_number_line.addStretch(1) big_number_line.addWidget(self.generate_number_box) big_number_line.addStretch(1) big_number_line.addWidget(self.reset_button) target_line = QHBoxLayout() target_line.addWidget(self.target_window) target_line.addStretch(1) target_line.addWidget(self.set_target) target_line.addStretch(1) target_line.addWidget(self.target_check) main_layout = QVBoxLayout() main_layout.addLayout(back_line) main_layout.addStretch(1) main_layout.addLayout(big_number_line) main_layout.addStretch(1) main_layout.addLayout(self.number_group) main_layout.addStretch(1) main_layout.addLayout(target_line) main_layout.addStretch(1) main_layout.addWidget(self.timer_bar) main_layout.addStretch(1) return main_layout def number_line(self): for i in range(6): if self.mode == 1: self.number_group.addWidget(QLabel(" ")) self.number_group.addStretch(1) elif self.mode == 0: self.number_group.addWidget(QComboBox()) self.number_group.addStretch(1) self.number_group.addWidget(self.set_numbers)
class Weijiejue(QDialog): def __init__(self,list,i,parent = None): super(Weijiejue,self).__init__(parent) #设置界面大小、名称、背景 #self.resize(800,900) self.setWindowTitle('故障诊断专家系统') self.setStyleSheet("background-image:url(tubiao_meitu.jpg)") QApplication.setStyle("Fusion") font = QtGui.QFont('微软雅黑',20) font1 = QtGui.QFont('微软雅黑light', 15) #窗体属性 self.i1=i self.list1=list # #查询self.list1[self.i1],得到cankaoziliao字符串,转换为列表 db = pymysql.connect("localhost", "root", "123456", "expertsy", charset='utf8') cur=db.cursor() #self.ckzl= ["手册1.pdf","图片1.jpg"] vbj = QVBoxLayout() vbj.setAlignment(Qt.AlignCenter) vbj.setSpacing(50) self.hbj1=QHBoxLayout() hbj2=QHBoxLayout() # self.setWindowFlags(Qt.Widget) self.paichatishi = QLabel(self.list1[self.i1]) self.paichatishi.setFont(font) sql = 'SELECT 参考资料 FROM 问题关联 WHERE 问题="%s"' % self.list1[self.i1] cur.execute(sql) string = cur.fetchone() print(string) if len(string)!=0: string0 = string[0] self.ckzl = string0.split(",") for fuzu in self.ckzl: self.title1= QPushButton(fuzu) self.title1.setStyleSheet(''' QPushButton{ border:none; color:blue; font-size:15px; height:30px; padding-left:5px; padding-right:5px; text-align:center; } QPushButton:hover{ color:black; border:1px solid #F3F3F5; border-radius:10px; background:LightGray; } ''') file_path= fuzu self.title1.clicked.connect(partial(self.lianjie)) #title1.clicked.connect(lambda :self.lianjie(file_path)) self.hbj1.addWidget(self.title1) self.jiejuebut=QPushButton("解决了") self.jiejuebut.setFont(font1) self.weijiejuebut = QPushButton("未解决") self.weijiejuebut.setFont(font1) self.weijiejuebut.clicked.connect(partial(self.tonext)) self.jiejuebut.clicked.connect(partial(self.showjiejue)) hbj2.addStretch(1) hbj2.addWidget(self.weijiejuebut) hbj2.addStretch(2) hbj2.addWidget(self.jiejuebut) hbj2.addStretch(1) vbj.addStretch(1) vbj.addWidget(self.paichatishi) vbj.addStretch(2) vbj.addLayout(self.hbj1) vbj.addStretch(2) vbj.addLayout(hbj2) vbj.addStretch(1) self.setLayout(vbj) def tonext(self): for i in reversed (range(self.hbj1.count())): self.hbj1.itemAt(i).widget().close() self.hbj1.takeAt(i) changdu=len(self.list1) self.i1=self.i1+1 if self.i1+1<changdu: self.paichatishi.setText(self.list1[self.i1]) if self.i1+1==changdu: self.paichatishi.setText(self.list1[self.i1]) self.weijiejuebut.setText("已经是最后一条") self.weijiejuebut.setEnabled(False) db = pymysql.connect("localhost", "root", "123456", "expertsy", charset='utf8') cur = db.cursor() sql = 'SELECT 参考资料 FROM 问题关联 WHERE 问题="%s"' % self.list1[self.i1] cur.execute(sql) string = cur.fetchone() string0 = string[0] if not string0 is None: print("assa") self.ckzl = string0.split(",") for fuzu in self.ckzl: self.title1= QPushButton(fuzu) self.title1.setStyleSheet(''' QPushButton{ border:none; color:blue; font-size:15px; height:30px; padding-left:5px; padding-right:5px; text-align:center; } QPushButton:hover{ color:black; border:1px solid #F3F3F5; border-radius:10px; background:LightGray; } ''') self.title1.clicked.connect(partial(self.lianjie)) #title1.clicked.connect(lambda :self.lianjie(file_path)) self.hbj1.addWidget(self.title1) def showjiejue(self): self.w3=Jiejue() self.w3.show() def lianjie(self): sender=self.sender() os.startfile(sender.text())
class MainWindow(QWidget): ## # \brief Initialization Function def __init__(self): super(MainWindow, self).__init__() #Default variables self.valid = False #Field to determine if the value is valid self.selectorLayout = None #Layout used for selecting a specific source self.sources = ["none", "text", "file", "database"] self.source = {"type": None} self.dests = ["console", "file"] self.dest = {"type": "console"} self.sourceValue = None self.sourceSchema = None #Determine screen settings geo = self.frameGeometry() self.width = QDesktopWidget().availableGeometry().width() self.height = QDesktopWidget().availableGeometry().height() #Define window par meters self.resize(self.width * .5, self.height * .5) self.setWindowTitle("Aqueti Schema Editor") self.show() #create Layouts in UI self.titleLayout = QHBoxLayout() self.mainLayout = QVBoxLayout() self.sourceLayout = QHBoxLayout() self.destLayout = QHBoxLayout() self.valueLayout = QVBoxLayout() self.buttonLayout = QHBoxLayout() #Create frames self.sourceFrame = QFrame() self.destFrame = QFrame() self.valueFrame = QFrame() self.sourceFrame.setFrameStyle(QFrame.Box) self.valueFrame.setFrameStyle(QFrame.Box) self.destFrame.setFrameStyle(QFrame.Box) self.sourceFrame.setLayout(self.sourceLayout) self.destFrame.setLayout(self.destLayout) self.valueFrame.setLayout(self.valueLayout) self.valueFrame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) #Create Scoll Area for valueFrame self.valueScrollArea = QScrollArea() self.valueScrollArea.updateGeometry() self.valueScrollArea.setWidget(self.valueFrame) self.valueScrollArea.setWidgetResizable(True) #Create title title = QLabel() title.setText("Aqueti Schema Editor") self.titleLayout.addWidget(title) #Add persistent source items sourceTitle = QLabel() sourceTitle.setText("Source:") self.sourceCombo = QComboBox() self.sourceCombo.addItems(self.sources) self.sourceCombo.currentTextChanged.connect( lambda: self.sourceChangeCallback()) selectSourceButton = QPushButton("Load") self.sourceLayout.addWidget(sourceTitle) self.sourceLayout.addWidget(self.sourceCombo) self.sourceMetaLayout = QHBoxLayout() self.sourceMetaLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize) self.sourceLayout.addLayout(self.sourceMetaLayout) self.sourceLayout.addWidget(selectSourceButton) #Add persistent dest destTitle = QLabel() destTitle.setText("Dest:") self.destCombo = QComboBox() self.destCombo.addItems(self.dests) self.destCombo.currentTextChanged.connect( lambda: self.destChangeCallback()) selectDestButton = QPushButton("Load") self.destLayout.addWidget(destTitle) self.destLayout.addWidget(self.destCombo) self.destMetaLayout = QHBoxLayout() self.destMetaLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize) self.destLayout.addLayout(self.destMetaLayout) self.destLayout.addWidget(selectDestButton) #Add Submit Button self.submitButton = QPushButton("Submit") self.submitButton.clicked.connect(lambda: self.submitCallback()) self.buttonLayout.addWidget(self.submitButton) #Add cancel Button cancelButton = QPushButton("Cancel") cancelButton.clicked.connect(lambda: self.cancelCallback()) self.buttonLayout.addWidget(cancelButton) #Add Layouts and draw self.mainLayout.addLayout(self.titleLayout) self.mainLayout.addWidget(self.sourceFrame) self.mainLayout.addWidget(self.destFrame) # self.mainLayout.addWidget( self.valueFrame ) self.mainLayout.addWidget(self.valueScrollArea) # self.mainLayout.addStretch(1) self.mainLayout.addLayout(self.buttonLayout) self.draw() ## # \brief updates the source Layout def updateSourceLayout(self): #Remove current layout information #Remove all widgets from the current layout while self.sourceMetaLayout.count(): item = self.sourceMetaLayout.takeAt(0) self.sourceMetaLayout.removeItem(item) widget = item.widget() if widget is not None: widget.deleteLater() try: item.deleteLater() except: pass #Find what our current source is and set the appropriate index index = 0 for i in range(0, self.sourceCombo.count()): if self.sourceCombo.itemText(i) == self.source["type"]: index = i self.sourceCombo.setCurrentIndex(index) #Add fields based on source type if self.source["type"] == "file": #Add filename fileLabel = QLabel() fileLabel.setText("file: ") try: name = self.source["filename"] except: name = "" self.sourceFilenameBox = QLineEdit() self.sourceFilenameBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self.sourceFilenameBox.setText(name) # self.sourceFilenameBox.readOnly = True # self.sourceFilenameBox.sizeHint() # self.sourceFilenameBox.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.sourceMetaLayout.addWidget(fileLabel) self.sourceMetaLayout.addWidget(self.sourceFilenameBox) # #Add a submitSource Button # selectSourceButton = QPushButton("Load") # selectSourceButton.clicked.connect( lambda: self.sourceChangeCallback()) # self.sourceLayout.addWidget(selectSourceButton) # self.sourceLayout.addStretch(1) ## # \brief updates the destination layout # def updateDestLayout(self): #Remove current layout information #Remove all widgets from the current layout while self.destMetaLayout.count(): item = self.destMetaLayout.takeAt(0) self.destMetaLayout.removeItem(item) widget = item.widget() if widget is not None: widget.deleteLater() try: item.deleteLater() except: pass """ ############################################# # Layout to select a destination ############################################# destTitle = QLabel() destTitle.setText("Dest:") self.destCombo = QComboBox() self.destCombo.addItems(self.dests) """ #Find what our current dest is and set the appropriate index index = 0 for i in range(0, self.destCombo.count()): if self.destCombo.itemText(i) == self.dest["type"]: index = i self.destCombo.setCurrentIndex(index) self.destCombo.currentTextChanged.connect( lambda: self.destChangeCallback()) # self.destLayout.addWidget(destTitle) # self.destLayout.addWidget(self.destCombo) # self.destLayout.addStretch(1) #### # Fill in details base on dest tpye #### if self.dest["type"] == "console": pass elif self.dest["type"] == "file": fileLabel = QLabel() fileLabel.setText("file: ") try: name = self.dest["filename"] except: name = "" self.fileNameBox = QLineEdit() self.fileNameBox.setText(name) # self.destMetaLayout.addWidget(fileLabel) self.destMetaLayout.addWidget(self.fileNameBox) ## # \brief function that is called when the source is changed # def destChangeCallback(self): print("Changing dest") newType = self.destCombo.itemText(self.destCombo.currentIndex()) print("New Type: " + str(newType)) if newType != self.dest["type"]: self.dest = {} self.dest["type"] = newType if self.dest["type"] == "console": pass elif self.dest["type"] == "file": options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog destName, _ = QFileDialog.getSaveFileName( self, "QFileDialog.getSaveFileName()", "", "All Files (*);;JSON Files (*.json)", options=options) self.dest["filename"] = str(destName) else: print("Unsupported Type") self.draw() ## # \brief Update the value layout def updateValueLayout(self): #Remove all widgets from the current layout while self.valueLayout.count(): item = self.valueLayout.takeAt(0) self.valueLayout.removeItem(item) widget = item.widget() if widget is not None: widget.deleteLater() try: item.deleteLater() except: pass #If we have data, let's display it if self.sourceSchema != None: valueTitle = QLabel() valueTitle.setText("Schema") self.schemaWidget = SmartWidget().init("Schema", self.sourceValue, self.sourceSchema, showSchema=False) self.valueLayout.addWidget(self.schemaWidget.frame) #Disable the submit button if we don't have a schema if self.sourceSchema == None: self.submitButton.setEnabled(False) else: self.submitButton.setEnabled(True) self.setLayout(self.mainLayout) ## # \brief redraws all dynamic layouts def draw(self): self.updateDestLayout() self.updateSourceLayout() self.updateValueLayout() ## # \brief callback for when the source type changes # def sourceChangeCallback(self): #SDF Add popup to notify of schema loss #Clear the schema to disable the submit button self.sourceSchema = None self.source["type"] = self.sourceCombo.itemText( self.sourceCombo.currentIndex()) if self.source["type"] == "none": self.sourceSchema = {"bsonType": "object"} #If we are a file read the file contents as the value elif self.source["type"] == "file": options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog sourceName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "All Files (*);;JSON Files (*.json)", options=options) self.source["filename"] = str(sourceName) print("Loading: " + str(self.source["filename"])) with open(self.source["filename"]) as json_file: self.sourceSchema = json.load(json_file) print("Loaded Schema:" + str(json.dumps(self.sourceSchema, indent=4))) self.updateSourceLayout() self.updateValueLayout() ## #\brief callback to get result from SmartWidget # # This function assumes that the schema is done. It will produce a popup # asking where and how to save the data # def submitCallback(self): schema = self.schemaWidget.getSchema() if self.dest["type"] == "console": print() print("Schema: (" + str(time.time()) + ")") print(json.dumps(schema, indent=4)) elif self.dest["type"] == "file": print("Writing to: " + str(self.dest["filename"])) with open(self.dest["filename"], 'w') as outfile: json.dump(schema, outfile, indent=4) else: print("Source type: " + str(self.dest["type"]) + " is not currently supported") self.close() #Use save pop-up to save data #self.saveWindow = SaveDataWindow(self.source, schema, self.saveCallback ) print(str(time.time()) + "- schema:") print(str(schema)) ## # \brief Function called after data is saved # def saveCallback(self, success): print("Data Result: " + str(success)) ## # \brief Cancels the change and exits # def cancelCallback(self): print("Exited. No changes were saved") sys.exit(1)
class Calendar(QWidget): def __init__(self, parent=None, year=int(time.strftime('%Y', time.localtime(time.time()))), month=int(time.strftime('%m', time.localtime(time.time())))): super().__init__(parent) # Set Schedule Layout Components =============== self.scheduleLayout = QVBoxLayout() # ------------------------------- self.titleBox = QHBoxLayout() self.titleLabel = QLabel("title: ") self.titleLineEdit = QLineEdit() # ------------------------------- self.placeBox = QHBoxLayout() self.placeLabel = QLabel("place: ") self.placeLineEdit = QLineEdit() # ------------------------------- self.dateBox = QHBoxLayout() self.dateLabel = QLabel("time:") self.fromHour = QSpinBox() self.fromMin = QSpinBox() self.toHour = QSpinBox() self.toMin = QSpinBox() # ------------------------------- self.discription = QHBoxLayout() self.contentLabel = QLabel("content: ") self.content = QTextEdit() # ------------------------------- self.modifyBtn = Button("Modifying", self.modifying) # ============================================== self.displayCalendar = MyCalendar() self.startDay = 0 self.maxDay = 0 self.currentYear = year self.currentMonth = month self.currentDay = 0 self.firstClick = True self.displayCalendar.loadHoliday() self.today = time.localtime() if os.name == 'nt': self.fileRoot = ".\schedules\schedules.txt" else: self.fileRoot = "./schedules/schedules.txt" try: scheduleFile = open(self.fileRoot, "rb") self.displayCalendar.schedule = pickle.load(scheduleFile) print(self.displayCalendar.schedule) except EOFError: pass # main layout self.mainLayout = QHBoxLayout() # Left side Layout ================================ self.leftLayout = QVBoxLayout() # Stacked Widget Part ----------------------------- # Setting Stacked Widget(like a switching Tabs) self.setSchedule = QWidget() self.lunaDate = QWidget() # Design And Setting Actions each Tab. if want to Append any action, plz input the action in here. self.setScheduleUI() self.lunaDateUI() # Appending tabs in Stack self.Stack = QStackedWidget() self.Stack.addWidget(self.setSchedule) self.Stack.addWidget(self.lunaDate) # Switching Button layout Design And binding button with action. self.tabLayout = QHBoxLayout() self.tabLayout.addWidget(Button("스케쥴러", lambda: self.display(0))) self.tabLayout.addWidget(Button("음력", lambda: self.display(1))) for i in range(self.tabLayout.count()): self.tabLayout.itemAt(i).widget().setStyleSheet('font-size: 8pt') self.leftLayout.addLayout(self.tabLayout) # ------------------------------------------------- # handling Year & month ---------------------------------- self.moveMonth = QHBoxLayout() self.previousBtn = Button("<", self.previousMonth) # showing Year and month using Combobox(Year range: 1980 ~ 2040, Month range: 1, 12) self.yearCombo = QComboBox() self.yearCombo.addItems([str(x) for x in range(1980, 2041)]) self.yearCombo.setCurrentText(str(self.currentYear)) self.monthCombo = QComboBox() self.monthCombo.addItems([str(x) for x in range(1, 13)]) self.monthCombo.setCurrentText(str(self.currentMonth)) self.nextBtn = Button(">", self.nextMonth) self.moveMonth.addStretch() self.moveMonth.addWidget(self.previousBtn) self.moveMonth.addWidget(self.yearCombo) self.moveMonth.addWidget(self.monthCombo) self.moveMonth.addWidget(self.nextBtn) self.moveMonth.addStretch() self.leftLayout.addLayout(self.moveMonth) self.leftLayout.addStretch() # ------------------------------------------------- # Set Day of Week --------------------------------- self.weekDayLayout = QHBoxLayout() enumDays = ["일", "월", "화", "수", "목", "금", "토"] for i in enumDays: label = QLabel(i) label.setAlignment(Qt.AlignCenter) self.weekDayLayout.addWidget(label) self.leftLayout.addLayout(self.weekDayLayout) # ------------------------------------------------- # grid layout to appending date Buttons self.calendarGrid = QGridLayout() self.calendarGrid.setSizeConstraint(QLayout.SetFixedSize) self.leftLayout.addLayout(self.calendarGrid) self.leftLayout.addStretch(7) # showing status self.statusLabel = QLabel("btn Status") self.leftLayout.addWidget(self.statusLabel) # ================================================== # Set grid self.displayCalendar.setCalander(self.currentYear, self.currentMonth) self.renderDate(self.displayCalendar.getCalander()) # Set ComboBox Changing Event self.yearCombo.currentTextChanged.connect( lambda: self.selectionChanged()) self.monthCombo.currentTextChanged.connect( lambda: self.selectionChanged()) self.mainLayout.addLayout(self.leftLayout) self.mainLayout.addWidget(self.Stack) self.Stack.setCurrentIndex(0) # default Tab -> set calendar self.setLayout(self.mainLayout) self.setWindowTitle("Calendar") def renderDate(self, newCalendar): # =========== Append Day Buttons =============== self.clearLayout(self.calendarGrid) todayYear = self.today.tm_year todayMonth = self.today.tm_mon todayDay = self.today.tm_mday toggle = True for i in newCalendar: print(i) # Enroll button for row, column in enumerate(newCalendar): for col, day in enumerate(column): btn = Button(str(day), self.btnEvent) # deactivate button condition if toggle: if day != 1: btn.setEnabled(False) else: toggle = False else: if (row == len(newCalendar) - 1) and (day // 10 == 0): btn.setEnabled(False) # set today button color if (self.currentYear, self.currentMonth, day) == (todayYear, todayMonth, todayDay): btn.setStyleSheet('font-style: italic;') # if this day have any event represent event key = '-'.join( [str(self.currentYear), str(self.currentMonth), str(day)]) if key in self.displayCalendar.schedule.keys( ) and btn.isEnabled(): btn.setStyleSheet('color: blue;') btn.setStyleSheet('background-color: skyblue;') btn.setToolTip( self.displayCalendar.schedule[key].getTitle()) for restMonth, restDay, title in self.displayCalendar.holidays: if restMonth == self.currentMonth and restDay == day and btn.isEnabled( ): if key in self.displayCalendar.schedule.keys( ) and btn.isEnabled(): btn.setStyleSheet( 'background-color: skyblue; color: red;') btn.setToolTip(title) else: btn.setStyleSheet('color: red;') btn.setToolTip(title) break # 공휴일은 빨간색으로 설정해준다. if col == 0 and btn.isEnabled(): btn.setStyleSheet('color: red;') self.calendarGrid.addWidget(btn, row, col) # =============================================== self.displayCalendar.enrollHoliday(self.currentYear) self.displayCalendar.loadHoliday() def btnEvent(self): # self.showingWidget(self.scheduleLayout) self.setFixedSize(self.mainLayout.sizeHint()) btn = self.sender() self.statusLabel.setText("Day: " + btn.text() + " is Clicked.") self.currentDay = btn.text() target = "-".join([ str(self.currentYear), str(self.currentMonth), str(self.currentDay) ]) targetEvent = self.displayCalendar.schedule.get(target) if not targetEvent: self.titleLineEdit.setText("None") self.placeLineEdit.clear() self.fromHour.setValue(0) self.fromMin.setValue(0) self.toHour.setValue(0) self.toMin.setValue(0) self.content.clear() else: self.titleLineEdit.setText(targetEvent.getTitle()) self.placeLineEdit.setText(targetEvent.getPlace()) timeSet = targetEvent.getDate().split(",") self.fromHour.setValue(int(timeSet[0])) self.fromMin.setValue(int(timeSet[1])) self.toHour.setValue(int(timeSet[2])) self.toMin.setValue(int(timeSet[3])) self.content.setText(targetEvent.getDescription()) def modifying(self): newEvent = MyEvent() eventList = [ self.titleLineEdit.text(), self.placeLineEdit.text(), ",".join([ str(self.fromHour.value()), str(self.fromMin.value()), str(self.toHour.value()), str(self.toMin.value()) ]), self.content.toPlainText(), ] newEvent.setEvent(*eventList) target = "-".join([ str(self.currentYear), str(self.currentMonth), str(self.currentDay) ]) self.displayCalendar.schedule[target] = newEvent self.statusLabel.setText("modified") # rendering previous month calendar def previousMonth(self): if self.currentMonth is 1: self.currentYear -= 1 self.yearCombo.setCurrentText(str(self.currentYear)) self.currentMonth = 12 self.monthCombo.setCurrentText(str(self.currentMonth)) else: self.currentMonth -= 1 self.monthCombo.setCurrentText(str(self.currentMonth)) # rendering next month calendar def nextMonth(self): if self.currentMonth is 12: self.currentYear += 1 self.yearCombo.setCurrentText(str(self.currentYear)) self.currentMonth = 1 self.monthCombo.setCurrentText(str(self.currentMonth)) else: self.currentMonth += 1 self.monthCombo.setCurrentText(str(self.currentMonth)) def selectionChanged(self): self.currentYear = int(self.yearCombo.currentText()) self.currentMonth = int(self.monthCombo.currentText()) self.displayCalendar.setYear(self.currentYear) self.displayCalendar.setMonth(self.currentMonth) self.displayCalendar.setCalander(self.currentYear, self.currentMonth) self.renderDate(self.displayCalendar.getCalander()) def clearLayout(self, layout): while layout.count(): child = layout.takeAt(0) if child.widget(): child.widget().deleteLater() def closeEvent(self, event): keys = [] myEvent = self.displayCalendar.schedule for target in myEvent.keys(): title = myEvent[target].title place = myEvent[target].place description = myEvent[target].description if (title, place, description) == ('', '', ''): keys.append(target) for target in keys: del self.displayCalendar.schedule[target] with open(self.fileRoot, "wb") as file: pickle.dump(self.displayCalendar.schedule, file) def setScheduleUI(self): # Schedules layout ================================== self.titleBox.addWidget(self.titleLabel) self.titleBox.addWidget(self.titleLineEdit) self.placeBox.addWidget(self.placeLabel) self.placeBox.addWidget(self.placeLineEdit) self.fromHour.setRange(0, 24) self.toHour.setRange(0, 24) self.fromMin.setRange(0, 59) self.toMin.setRange(0, 59) self.fromHour.valueChanged.connect( lambda: self.toHour.setRange(self.fromHour.value(), 24)) # self.toHour.valueChanged.connect(lambda: self.fromHour.setRange(0, self.toHour.value())) self.fromMin.valueChanged.connect( lambda: self.toMin.setRange(self.fromMin.value(), 59)) # self.toMin.valueChanged.connect(lambda: self.fromMin.setRange(0, self.toMin.value())) self.dateBox.addWidget(self.dateLabel) self.dateBox.addWidget(self.fromHour) self.dateBox.addWidget(self.fromMin) self.dateBox.addWidget(QLabel(" ~ ")) self.dateBox.addWidget(self.toHour) self.dateBox.addWidget(self.toMin) self.contentLabel.setAlignment(Qt.AlignTop) self.discription.addWidget(self.contentLabel) self.discription.addWidget(self.content) self.scheduleLayout.addLayout(self.titleBox) self.scheduleLayout.addLayout(self.placeBox) self.scheduleLayout.addLayout(self.dateBox) self.scheduleLayout.addLayout(self.discription) # modifying schedule Button self.scheduleLayout.addWidget(self.modifyBtn) self.setSchedule.setLayout(self.scheduleLayout) def lunaDateUI(self): month_31 = [1, 3, 5, 7, 8, 10, 12] layout = QVBoxLayout() #layout.addWidget(QLabel("Luna Date")) topLayout = QHBoxLayout() self.yearSpinner = QSpinBox() self.yearSpinner.setRange(1980, 2040) self.monthSpinner = QSpinBox() self.monthSpinner.setRange(1, 12) self.daySpinner = QSpinBox() self.yearSpinner.setValue(self.today.tm_year) self.monthSpinner.setValue(self.today.tm_mon) self.daySpinner.setValue(self.today.tm_mday) self.monthSpinner.valueChanged.connect( lambda: self.daySpinner.setRange( 1, self.displayCalendar.getMaxday(self.yearSpinner.value(), self.monthSpinner.value()))) self.modeComboBox = QComboBox() self.modeComboBox.addItems(["양력 -> 음력", "음력 -> 양력"]) convertBtn = Button("convert", self.lunarBtnEvent) resetBtn = Button("reset", self.lunarBtnEvent) bottomLayout = QVBoxLayout() titleBox = QHBoxLayout() solarBox = QHBoxLayout() lunarBox = QHBoxLayout() self.todayLabel = QLabel("오늘의 날짜정보") self.todayLabel.setStyleSheet('color: red; font-size: 18px;') solarLabel = QLabel("양력날짜") solarLabel.setStyleSheet('color: gray;') todaySolarDay = "%04d-%02d-%02d" % ( self.today.tm_year, self.today.tm_mon, self.today.tm_mday) self.solarDateLabel = QLabel(todaySolarDay) lunarLabel = QLabel("음력날짜") lunarLabel.setStyleSheet('color: gray;') self.lunarDateLabel = QLabel( self.displayCalendar.calculator.getToLunarDate( self.today.tm_year, self.today.tm_mon, self.today.tm_mday)) titleBox.addWidget(self.todayLabel) solarBox.addWidget(solarLabel) solarBox.addWidget(self.solarDateLabel) lunarBox.addWidget(lunarLabel) lunarBox.addWidget(self.lunarDateLabel) bottomLayout.addLayout(titleBox) bottomLayout.addLayout(solarBox) bottomLayout.addLayout(lunarBox) topLayout.addWidget(self.yearSpinner) topLayout.addWidget(self.monthSpinner) topLayout.addWidget(self.daySpinner) topLayout.addWidget(self.modeComboBox) topLayout.addWidget(convertBtn) topLayout.addWidget(resetBtn) layout.addStretch() layout.addLayout(bottomLayout) layout.addLayout(topLayout) layout.addStretch() self.lunaDate.setLayout(layout) def display(self, i): self.Stack.setCurrentIndex(i) def hidingWidget(self, layout): for i in range(layout.count()): item = layout.itemAt(i) if item.widget() is not None: layout.itemAt(i).widget().hide() elif item.layout() is not None: self.hidingWidget(layout.itemAt(i).layout()) def showingWidget(self, layout): for i in range(layout.count()): item = layout.itemAt(i) if item.widget() is not None: layout.itemAt(i).widget().show() elif item.layout() is not None: self.showingWidget(layout.itemAt(i).layout()) def lunarBtnEvent(self): btn = self.sender() key = btn.text() if key == 'reset': self.todayLabel.setText('오늘의 날짜정보') self.todayLabel.setStyleSheet('color: red; font-size: 18px;') elif key == 'convert': if self.modeComboBox.currentIndex() is 0: self.todayLabel.setText("양력 {}년 {}월 {}일".format( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value())) self.todayLabel.setStyleSheet( 'font-weight: bold; color: black; font-size: 12px;') lunarDate = self.displayCalendar.calculator.getToLunarDate( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value()) self.solarDateLabel.setText( str(self.yearSpinner.value()) + "-" + str(self.monthSpinner.value()) + "-" + str(self.daySpinner.value())) self.lunarDateLabel.setText(lunarDate) else: self.todayLabel.setText("음력 {}년 {}월 {}일".format( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value())) self.todayLabel.setStyleSheet( 'font-weight: bold; color: black; font-size: 12px;') solarDate = self.displayCalendar.calculator.toSolarDate( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value()) self.solarDateLabel.setText( str(solarDate[0]) + "-" + str(solarDate[1]) + "-" + str(solarDate[2])) self.lunarDateLabel.setText( str(self.yearSpinner.value()) + "-" + str(self.monthSpinner.value()) + "-" + str(self.daySpinner.value()))
class SaveDataWindow(QWidget): def __init__(self, dest, schema, callback, parent=None): super(SaveDataWindow, self).__init__() self.dests = ["console", "text", "file", "database"] self.dest = dest self.schema = schema #Determine screen settings geo = self.frameGeometry() self.width = QDesktopWidget().availableGeometry().width() self.height = QDesktopWidget().availableGeometry().height() #Define window par meters self.resize(self.width * .5, self.height * .5) self.setWindowTitle("Aqueti Schema Editor") # self.mainLayout = QVBoxLayout() self.titleLayout = QHBoxLayout() self.destLayout = QHBoxLayout() #Create title title = QLabel() title.setText("Schema Saving Dialog") self.titleLayout.addWidget(title) self.mainLayout.addLayout(self.titleLayout) #Destination Layout self.destLayout = QHBoxLayout() self.mainLayout.addLayout(self.destLayout) #Add Button Layout self.buttonLayout = QHBoxLayout() self.submitButton = QPushButton("Save") self.submitButton.clicked.connect(lambda: self.saveButtonCallback()) self.buttonLayout.addWidget(self.submitButton) cancelButton = QPushButton("Cancel") cancelButton.clicked.connect(lambda: self.cancelButtonCallback()) self.buttonLayout.addWidget(cancelButton) self.mainLayout.addLayout(self.buttonLayout) self.setLayout(self.mainLayout) self.show() self.updateDestLayout() self.draw() ## # \brief updates the destinatino layout # def updateDestLayout(self): #Remove current layout information #Remove all widgets from the current layout while self.destLayout.count(): item = self.destLayout.takeAt(0) self.destLayout.removeItem(item) widget = item.widget() if widget is not None: widget.deleteLater() try: item.deleteLater() except: pass ############################################# # Layout to select a destination ############################################# destTitle = QLabel() destTitle.setText("OutputType:") self.destCombo = QComboBox() self.destCombo.addItems(self.dests) #Find what our current dest is and set the appropriate index index = 0 for i in range(0, self.destCombo.count()): if self.destCombo.itemText(i) == self.dest["type"]: index = i self.destCombo.setCurrentIndex(index) self.destLayout.addWidget(destTitle) self.destLayout.addWidget(self.destCombo) #### # Fill in details base on source tpye #### if self.dest["type"] == "console": pass elif self.dest["type"] == "file": fileLabel = QLabel() fileLabel.setText("file: ") try: name = self.dest["filename"] except: name = "" self.fileNameBox = QLineEdit() self.fileNameBox.setText(name) self.destLayout.addWidget(fileLabel) self.destLayout.addWidget(self.fileNameBox) ## # \brief Function to draw the object def draw(self): #Add a submitDest Button selectDestButton = QPushButton("Select") selectDestButton.currentIndexChanged.connect( lambda: self.destChangeCallback()) self.destLayout.addWidget(destTitle) self.destLayout.addWidget(self.destCombo) self.destLayout.addWidget(selectDestButton) self.destLayout.addLayout(self.destLayout) ## # \brief callback for the Cancel button # def cancelButtonCallback(self): self.close() ## # \brief callback for a save button press # def saveButtonCallback(self): print("Saving:" + str(self.dest)) if self.dest["type"] == "console": print() print("Schema (" + str(time.time()) + ")") print(str(self.schema)) elif self.dest["type"] == "file": with open(self.dest["filename"], 'w') as outfile: json.dump(self.schema, outfile) else: print("Source type: " + str(self.dest["type"]) + " is not currently supported") self.close()
class MyPlotWidget(QWidget): COLORS=('r','g','b','c','m') def __init__(self): QWidget.__init__(self) vBox = QVBoxLayout(self) self._labels = QHBoxLayout() #self._labels.setSpacing(0) self._labels.addStretch(1) vBox.addLayout(self._labels) self.pw = pg.PlotWidget() vBox.addWidget(self.pw) self.pw.setBackground('w') self.pw.showGrid(x=True, y=True) self.setAcceptDrops(True) self.pw.setAcceptDrops(True) self.cidx = 0 def dragEnterEvent(self, e): if e.mimeData().hasFormat("application/x-DataItem"): e.accept() else: e.ignore() def dropEvent(self, e): data = e.mimeData() bstream = data.retrieveData("application/x-DataItem", QVariant.ByteArray) selected = pickle.loads(bstream) name = f"{e.source().filename} : {selected.var_name}" print(type(selected.data)) item = self.pw.getPlotItem().plot(x=selected.time.to_numpy(), y=selected.data.to_numpy(), pen=pg.mkPen(color=MyPlotWidget.COLORS[self.cidx], width=2), name=name) label = self.makeLabel(item) self._labels.insertWidget(self._labels.count()-1, label) e.source().onClose.connect(lambda : self.removeItem(item, label)) self.pw.autoRange() self.cidx = (self.cidx + 1) % len(MyPlotWidget.COLORS) e.accept() def makeLabel(self, plot_item): label = QLabel(plot_item.name()) label.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) palette = QPalette() color = plot_item.opts['pen'].color() palette.setColor(QPalette.WindowText, color) label.setPalette(palette) return label def removeItem(self, item, label): self.pw.removeItem(item) self._labels.removeWidget(label) # self._labels.takeAt(self._labels.indexOf(label)) label.deleteLater()
class ProjectsWindow(QMdiSubWindow): def __init__(self, app, OAuth_token, parent): super().__init__() self.app = app self.token = OAuth_token self.parent = parent self.open_windows = self.parent.open_windows self.initFig() self.initUI() def initFig(self): """ Initialize Figshare information """ self.project_list = self.get_project_list(self.token) def initUI(self): self.format_window() # Create a horizontal box layout to hold the project buttons self.project_buttons_box = QHBoxLayout() # Create a vertical box layout to hold the project window widgets and layouts self.vbox = QVBoxLayout() # Add the Projects button to the vertical box layout init_finish = len(self.project_list) if init_finish > 4: init_finish = 4 self.create_project_bar(0, init_finish) self.vbox.addLayout(self.project_buttons_box) # Add the scroll bar to the vertical box layout self.s_bar = self.scroll_bar() self.vbox.addWidget(self.s_bar) self.hbox = QHBoxLayout() temp = QVBoxLayout() temp.addWidget(self.search_bar()) temp.addLayout(self.management_buttons()) self.hbox.addLayout(temp) self.hbox.addLayout(self.vbox) # Create a central widget for the projects window window_widget = QWidget() # Add the vertical box layout window_widget.setLayout(self.hbox) # Set the projects window widget self.setWidget(window_widget) ##### # Window Formatting ##### def format_window(self): """ Formats the Projects window """ # Gets the QRect of the main window geom = self.parent.geometry() # Gets the Qrect of the sections window section_geom = self.parent.section_geom # Define geometries for the projects window x0 = section_geom.x() + section_geom.width() y0 = section_geom.y() w = geom.width() - x0 h = ((geom.height() - y0) / 6) self.setGeometry(x0, y0, w, h) # Remove frame from projects window self.setWindowFlags(Qt.FramelessWindowHint) ##### # Window Widgets ##### def scroll_bar(self): """ Creates a scroll bar set to the size of the projects list :return: QScrollBar Object """ s_bar = QScrollBar(Qt.Horizontal) s_bar.setMaximum(len(self.project_list) - 4) s_bar.sliderMoved.connect(self.slider_val) s_bar.valueChanged.connect(self.slider_val) return s_bar def create_proj_thumb(self, title, published_date, project_id): """ Creates a large pushbutton for a project :param title: string. Project title :param published_date: string. project published date :param id: int. figshare project id number :return: QPushButton object """ geom = self.geometry() # Get the scalig ratios for the current window w_ratio, f_ratio = scaling_ratio(self.app) # Scale the font sizes title_fnt_size = 12 * f_ratio date_ftn_size = 8 * f_ratio # Create the title label title_lbl = QLabel() title_lbl.setText("{}".format(title)) title_lbl_fnt = QFont('SansSerif', title_fnt_size) title_lbl_fnt.setBold(True) title_lbl.setFont(title_lbl_fnt) title_lbl.setWordWrap(True) # Create the date label date_lbl = QLabel() if published_date is None: published_date = 'Private' date_lbl.setText("Published: {}".format(published_date)) date_lbl_fnt = QFont('SansSerif', date_ftn_size) date_lbl.setFont(date_lbl_fnt) date_lbl.setStyleSheet('color: gray') date_lbl.setWordWrap(True) # Create a layout to hold the labels lbl_box = QVBoxLayout() # Add labels to layout lbl_box.addWidget(title_lbl) lbl_box.addWidget(date_lbl) # Create a button for the project btn = QPushButton(self) checkable_button(self.app, btn) btn.setLayout(lbl_box) btn.clicked[bool].connect(lambda: self.on_project_pressed(project_id)) self.project_buttons_box.addWidget(btn) def create_project_bar(self, start, finish): """ Creates a series of Project push buttons :param start: start position in projects list :param finish: finish position in projects list """ self.buttons = {} i = 0 for project_pos in range(start, finish): title = self.project_list[project_pos]['title'] pub_date = self.project_list[project_pos]['published_date'] project_id = self.project_list[project_pos]['id'] self.create_proj_thumb(title, pub_date, project_id) self.buttons[project_id] = self.project_buttons_box.itemAt( i).widget() i += 1 def management_buttons(self): """ Creates a layout that holds buttons to be used for creating and deleting projects :return: QVBoxLayout holding the create, and delete projects buttons """ # Create New Project Button np_btn = QPushButton() np_btn.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) np_btn.setIcon( QIcon(os.path.abspath(__file__ + '/../..' + '/img/Folder-48.png'))) np_btn.setToolTip('Create a new Figshare Project') np_btn.setToolTipDuration(1) np_btn.pressed.connect(self.on_projects_btn_pressed) # Create Delete Project Button del_btn = QPushButton() del_btn.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) del_btn.setIcon( QIcon(os.path.abspath(__file__ + '/../..' + '/img/del_folder.png'))) del_btn.setToolTip('Delete Selected Project') del_btn.setToolTipDuration(1) del_btn.pressed.connect(self.on_delete_btn_pressed) # Create layout to hold buttons hbox = QHBoxLayout() # Add Buttons to layout hbox.addWidget(np_btn) hbox.addWidget(del_btn) return hbox def search_bar(self): """ Creates a QLineEdit object for the user to enter a search query :return: Edits the projects list object according to the filter """ # Create text box edit = QLineEdit() # Set font style search_bar(self.app, edit) # Set place holder text edit.setPlaceholderText('Search') # Add a clear button to the line edit edit.setClearButtonEnabled(True) # Add mouse over text edit.setToolTip('Search for specific Figshare Projects') edit.setToolTipDuration(1) # Connect search function to the return key edit.returnPressed.connect(lambda: self.search_on_return(edit.text())) edit.textChanged.connect(lambda: self.search_on_clear(edit.text())) return edit ##### # Widget Actions ##### def slider_val(self): """ Called when the projects button slider is changed. Removes all existing buttons and regenerates from the new position :return: """ while self.project_buttons_box.count(): item = self.project_buttons_box.takeAt(0) item.widget().deleteLater() s_bar_pos = self.s_bar.value() if 1 < len(self.project_list) < 4: number = len(self.project_list) else: number = 4 self.s_bar.setMaximum(len(self.project_list) - number) self.create_project_bar(s_bar_pos, s_bar_pos + number) def search_init(self): """ Called when the projects search bar is used. Removes all existing buttons and regenerates from new projects list :return: """ while self.project_buttons_box.count(): item = self.project_buttons_box.takeAt(0) item.widget().deleteLater() if 1 <= len(self.project_list) <= 4: number = len(self.project_list) else: number = 4 self.s_bar.setMaximum(len(self.project_list) - number) self.create_project_bar(0, number) def search_on_return(self, search_text): """ Called when return is pressed in the search bar. :return: """ if search_text != '': self.project_list = self.search_projects(search_text, self.token) self.search_init() def search_on_clear(self, lineedit_text): """ Called when the search bar is cleared :return: """ if lineedit_text == '': self.project_list = self.get_project_list(self.token) self.slider_val() def on_projects_btn_pressed(self): """ Called when the create new project button is pressed """ if 'new_project_window' in self.open_windows: self.open_windows.remove('new_project_window') self.parent.new_project_window.close() else: self.open_windows.remove('projects_window') self.close() if 'project_info_window' in self.open_windows: self.parent.project_info_window.close() self.open_windows.remove('project_info_window') if 'project_articles_window' in self.open_windows: self.parent.project_articles_window.close() self.open_windows.remove('project_articles_window') if 'article_edit_window' in self.open_windows: self.open_windows.remove('article_edit_window') self.parent.article_edit_window.close() self.open_windows.add('new_project_window') self.parent.new_project_window = NewProjectWindow( self.app, self.token, self.parent) self.parent.mdi.addSubWindow(self.parent.new_project_window) self.parent.new_project_window.show() def on_project_pressed(self, project_id): """ Called when a project is clicked. :return: """ # For if there is already a project info window open if 'project_info_window' in self.open_windows: # Get the project id number of the current window open_proj = self.parent.project_info_window.project_id # For a different project than the currently open project if open_proj != project_id: # If the current project is in the current view of project buttons (it may have been scrolled away from) if open_proj in self.buttons: # If that button is checked, uncheck it if self.buttons[open_proj].isChecked(): self.buttons[open_proj].toggle() # Close the currently open project info window self.parent.project_info_window.close() # Create a new project info window for the different project self.parent.project_info_window = ProjectInfoWindow( self.app, self.token, self.parent, project_id) # Add it as a sub window to the framing window self.parent.mdi.addSubWindow(self.parent.project_info_window) self.parent.project_info_window.show() # If the current projects button is pressed else: # Close the window and remove from the open window list self.open_windows.remove('project_info_window') self.parent.project_info_window.close() # If any sub windows are open close them as well if 'project_articles_window' in self.open_windows: self.open_windows.remove('project_articles_window') self.parent.project_articles_window.close() if 'article_edit_window' in self.open_windows: self.open_windows.remove('article_edit_window') self.parent.article_edit_window.close() # For when no project info window is open else: self.open_windows.add('project_info_window') self.parent.project_info_window = ProjectInfoWindow( self.app, self.token, self.parent, project_id) self.parent.mdi.addSubWindow(self.parent.project_info_window) self.parent.project_info_window.show() def on_delete_btn_pressed(self): """ Called when the project delete button is pressed/ :return: """ open_proj = self.parent.project_info_window.project_id project_title = self.parent.project_info_window.project_info['title'] msg = "Are you sure you want to delete Figshare Project: {}".format( project_title) msg_box = QMessageBox.question(self, 'Message', msg, QMessageBox.Yes, QMessageBox.No) if msg_box == QMessageBox.Yes: successful = self.delete_project(self.token, open_proj) if successful: con_reply = QMessageBox.information( self, 'Deletion Confirmation', 'Project successfully deleted', QMessageBox.Ok) if con_reply == QMessageBox.Ok: self.reopen_projects() else: self.reopen_projects() else: con_reply = QMessageBox.warning( self, 'Deletion Confirmation', 'Unknown error occurred.\n Project may not have been deleted.', QMessageBox.Ok) if con_reply == QMessageBox.Ok: self.reopen_projects() else: self.reopen_projects() def reopen_projects(self): """ Called to open and close the projects window. :return: """ for i in range(2): self.parent.section_window.on_projects_btn_pressed() ##### # Figshare API Interface Calls ##### def get_project_list(self, token): """ Returns the users private project list :param token: Figshare OAuth token :return: array of project """ projects = Projects(token) return projects.get_list() def search_projects(self, search_text, token): """ Returns a list of projects matching the users search criteria :param search_text: String. Figshare style elastic search string :param token: Figshare OAuth token :return: """ projects = Projects(token) result = projects.search(search_text) if len(result) == 0: result = projects.get_list() return result def delete_project(self, token, project_id): """ Deletes the given project from Figshare :param token: :param project_id: Int. Figshare project ID number :return: """ projects = Projects(token) try: projects.delete( project_id, safe=False ) # Suppresses command line requirement for acknowledgement return True except: return False