示例#1
0
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)
示例#2
0
文件: tool.py 项目: zaswed76/note_tab
class Tool(QFrame):
    def __init__(self, cfg):
        super().__init__()
        self.cfg = cfg
        self.custom_groups = {}
        self.selected_line_text = []

        self.setFixedHeight(cfg["tool_height"])
        # self.setStyleSheet("background-color: lightgrey")

        self.box = QHBoxLayout(self)

        self.word_line = LineEdit()

        # self.word_line.selectionChanged.connect(self.set_select_text)
        # self.word_line.editingFinished.connect(self.set_edit_text)

        self.search_btn = ToolBtn("search")
        self.config_btn = ToolBtn("config")

        self.box.addWidget(self.word_line)
        self.box.addWidget(self.search_btn)
        self.box.addWidget(CustomSpace(min_w=10))
        # self.add_btn("uk")
        self.box.addStretch(1)

        self.box.addWidget(self.config_btn)
        #

    def set_select_text(self):
        self.selected_line_text.append(self.word_line.selectedText())

    def set_edit_text(self):
        print(self.word_line.selectedText(), 888)

    @property
    def line_edit_text(self):
        return self.word_line.toPlainText()

    def set_line_validator(self, reg=None):
        if reg is None:
            reg = "[\d\w!]+"
        validator = QRegExpValidator(QRegExp(reg))
        self.word_line.setValidator(validator)

    def set_custom_dict(self):
        self.custom_groups["dict"] = CustomButton("ru")
        self.box.insertWidget(3, self.custom_groups["dict"])

    def del_custom_dict(self):
        try:
            self.box.removeWidget(self.custom_groups["dict"])
        except KeyError:
            pass

    def selected_text(self):
        self.word_line.setFocus()
        cursor = self.word_line.textCursor()
        textSelected = cursor.selectedText()
        return textSelected
示例#3
0
class PreviewWidget(QScrollArea):

    def __init__(self, parent=None):
        super(PreviewWidget, self).__init__(parent)
        self.setWidgetResizable(True)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.widget = QWidget()
        self.layout = QHBoxLayout()
        self.layout.addStretch(1)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.widget.setLayout(self.layout)
        self.setWidget(self.widget)

    def removeLast(self):
        item = self.layout.takeAt(0)
        if item:
            item.widget().deleteLater()

    def resizeEvent(self, event):
        self.widget.setFixedHeight(self.viewport().height())
        super(PreviewWidget, self).resizeEvent(event)

    def addPixmap(self, pixmap):
        label = ImageLabel(pixmap, self)
        self.layout.insertWidget(0, label)
class ContainerItemWidget(QWidget):
    def __init__(self, label_text, init_color=(0., 0., 0., 0.)):
        super().__init__()

        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setAlignment(Qt.AlignLeft)

        self.check_box = QCheckBox()
        #        self.check_box.setMaximumWidth(18)
        self.check_box.setCheckState(Qt.Checked)

        self.color_select = ColorButton(init_color)

        self.label = QLabel(label_text)

        self.setLayout(self.layout)

        self.layout.addWidget(self.check_box)
        self.layout.addWidget(self.color_select)
        self.layout.addWidget(self.label)

    def start_edit_label(self):
        self.line_edit = QLineEdit(self.label.text())
        self.layout.insertWidget(2, self.line_edit)
        self.line_edit.returnPressed.connect(self.finish_edit_label)

    def finish_edit_label(self):
        self.label.setText(self.line_edit.text())
        self.line_edit.hide()

    def set_edit_label(self, text):
        self.label.setText(text)
示例#5
0
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)
示例#6
0
class ItemDialog(QWidget):
    def __init__(self, parent=None):
        QListWidgetItem.__init__(self, parent)

        layNameMessage = QVBoxLayout()
        self.user_id = 0
        self.name = QLabel()
        self.message = QLabel()
        self.layMessage = QHBoxLayout()
        self.layMessage.addWidget(self.message, alignment=Qt.AlignLeft)

        layNameMessage.addWidget(self.name)
        layNameMessage.addLayout(self.layMessage)

        allLayout = QHBoxLayout()
        allLayout.addLayout(layNameMessage)

        self.w = QWidget()
        self.w.setLayout(allLayout)

        mainLayout = QHBoxLayout()
        mainLayout.addWidget(self.w)

        self.setLayout(mainLayout)

    def setUnread(self, unread):

        if unread > 0:
            self.w.setStyleSheet("background-color:" +
                                 MyData.COLOR_BACKGROUND + ";")

    def setReadState(self, readState):
        if readState == 0:
            myAvatar = QLabel()
            pixmap = QPixmap()

            self.layMessage.insertWidget(0, myAvatar, alignment=Qt.AlignLeft)
            pixmap.load('src/sheep_icon.png')
            myPixmap = pixmap.scaled(20, 20, Qt.KeepAspectRatio,
                                     Qt.FastTransformation)
            myAvatar.setPixmap(myPixmap)

            self.message.setStyleSheet('color:gray;')

    def setName(self, name, user_id):
        self.user_id = user_id
        self.name.setText("<p> <strong>" + name + "</strong></p>")

    def setMessage(self, message):

        self.message.setText(message)
示例#7
0
class GameView(QObject):
    unit_changed = pyqtSignal(UnitType)

    def __init__(self):
        super().__init__()
        self.game_window_ = QMainWindow()
        self.main_widget_ = QWidget()
        self.view_widget_ = QGraphicsView()
        self.british_creating_panel_ = UnitCreatingPanel(
            Game().get_british_player())
        self.french_creating_panel_ = UnitCreatingPanel(
            Game().get_french_player())
        self.current_creating_panel = self.british_creating_panel_

        self.init_window()
        self.init_main_widget()

        self.british_creating_panel_.connect_radios(self.unit_changed)
        self.french_creating_panel_.connect_radios(self.unit_changed)

    def init_window(self):
        self.game_window_.setCentralWidget(self.main_widget_)
        self.game_window_.setWindowTitle('rly stupid game')
        self.game_window_.show()

    def init_main_widget(self):
        self.main_widget_.setMinimumSize(1000, 700)
        self.main_widget_layout = QHBoxLayout()
        self.main_widget_layout.addWidget(self.british_creating_panel_)
        self.main_widget_layout.addWidget(self.view_widget_)
        self.main_widget_.setLayout(self.main_widget_layout)

    def set_scene(self, scene):
        self.view_widget_.setScene(scene)
        scene.add_map_tiles()

    def update_after_adding_unit(self):
        self.british_creating_panel_.update_after_adding_unit()
        self.french_creating_panel_.update_after_adding_unit()

    def update_after_turn_change(self):
        self.main_widget_layout.removeWidget(self.current_creating_panel)
        self.current_creating_panel.hide()
        if self.current_creating_panel is self.british_creating_panel_:
            self.current_creating_panel = self.french_creating_panel_
            self.main_widget_layout.addWidget(self.current_creating_panel)
        else:
            self.current_creating_panel = self.british_creating_panel_
            self.main_widget_layout.insertWidget(0,
                                                 self.current_creating_panel)
        self.current_creating_panel.show()
示例#8
0
class PopupContainer(BaseView):
    on_drop = pyqtSignal()

    def __init__(self, *args, widget:QWidget, size_w=600, backdrop=True, **kwargs):
        super(PopupContainer, self).__init__(*args, **kwargs)

        self.backdrop = backdrop
        self.widget = widget
        self.size_w = size_w
        self.procedure()

    def set_ui(self):
        self.container = QWidget()
        self.container.setObjectName('Container')
        self.container.setFixedWidth(self.size_w)
        self.btn_close = QPushButton()
        self.btn_close.setText("关闭")

    def place(self):
        layout = QGridLayout(self)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        layout_container = QVBoxLayout()
        layout_container.setSpacing(0)
        layout_container.setContentsMargins(12, 12, 12, 12)
        self.layout_action = QHBoxLayout()
        self.layout_action.setContentsMargins(0, 10, 0, 0)

        layout.addItem(QSpacerItem(1, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding), 0, 1, 1, 1)
        layout.addWidget(self.container, 2, 2, 1, 1)
        self.container.setLayout(layout_container)
        layout_container.addWidget(self.widget)
        self.layout_action.addSpacerItem(QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum))
        self.layout_action.addWidget(self.btn_close)
        layout_container.addLayout(self.layout_action)

    def configure(self):
        self.btn_close.clicked.connect(lambda :self.on_drop.emit())

    def add_action(self, data, callback):
        btn = QPushButton()
        btn.setText(data)
        self.layout_action.insertWidget(0, btn)
        btn.clicked.connect(callback)

    def mousePressEvent(self, event) -> None:
        pos = event.pos()
        if self.backdrop and self.childAt(pos) == None and pos.x() < self.container.x() or pos.x() > self.container.x() + self.size_w or pos.y() < self.container.y():
            self.on_drop.emit()
            return
        super(PopupContainer, self).mousePressEvent(event)
示例#9
0
    def __init__(self, layout, parent=None):
        super().__init__(parent)

        self.player = QMediaPlayer(self)
        self.player.setAudioRole(QAudio.MusicRole)

        self.player.durationChanged.connect(self.duration_changed)
        self.player.positionChanged.connect(self.position_changed)
        self.player.mediaStatusChanged.connect(self.status_changed)
        self.player.bufferStatusChanged.connect(self.buffering_progress)
        self.player.stateChanged.connect(self.state_changed)
        self.player.error[QMediaPlayer.Error].connect(
            self.display_error_message)

        self.play_control = PlayControl(layout, parent)
        self.play_control.set_state(self.player.state())
        self.play_control.play.connect(self.player.play)
        self.play_control.pause.connect(self.player.pause)
        self.play_control.stop.connect(self.player.stop)

        self.player.stateChanged.connect(self.play_control.set_state)

        self.song_label = QLabel()
        self.song_label.setText(self.NO_PLAYING_SONG)
        self.song_label.setWordWrap(True)

        song_grid = QGridLayout(parent)
        song_grid.addWidget(self.song_label, 0, 0)

        self.duration_label = QLabel()
        self.duration_label.setText("00:00 / 00:00")
        self.slider = QSlider(Qt.Horizontal, parent)
        self.slider.setRange(0, self.player.duration() / 1000)
        self.slider.sliderMoved.connect(self.seek)
        progress_layout = QHBoxLayout(parent)
        progress_layout.insertWidget(0, self.duration_label)
        progress_layout.insertWidget(0, self.slider)
        song_grid.addLayout(progress_layout, 1, 0)

        self.status_label = QLabel()
        song_grid.addWidget(self.status_label, 2, 0)

        layout.addLayout(song_grid)

        if not self.is_player_available():
            QMessageBox.warning(self, "警告", "QMediaPlayer 对象无可用的服务")
            return

        self.song = None
示例#10
0
 def createCategories(self):
     pass
     #NOTE might want to store the category labels in case they need to change or whatever.
     #
     self.categories = {}
     categoriesLayout = QHBoxLayout()
     for category, clues in triviaInfo.items():
         pass
         theLabel = QLabel(category)
         theLabel.setFrameShape(QFrame.Box)
         theLabel.setLineWidth(2)
         theLabel.setFixedSize(150, 100)
         theLabel.setStyleSheet('background-color : blue; color : yellow')
         categoriesLayout.insertWidget(int(clues[0].getX()), theLabel)
         self.categories[category] = theLabel
     
     self.generalLayout.addLayout(categoriesLayout)
class AnnotationItemWidget(QWidget):
    def __init__(self, label_text, count=0):
        super().__init__()

        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setAlignment(Qt.AlignLeft)

        self.check_box = QCheckBox()
        self.check_box.setMaximumWidth(18)
        self.check_box.setCheckState(Qt.Checked)

        self.color_select = ColorButton()

        self.label = QLabel(label_text)

        self.setLayout(self.layout)

        self.count_label = QLabel(str(count))

        img_path = get_image_path('spreadsheet.svg')
        self.view_btn = QPushButton()
        self.view_btn.setFixedWidth(20)
        self.view_btn.setFixedHeight(20)
        self.view_btn.setStyleSheet("QPushButton{border: 0px;}")
        self.view_btn.setIcon(QIcon(img_path))

        self.layout.addWidget(self.check_box)
        self.layout.addWidget(self.color_select)
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.count_label)
        self.layout.addWidget(self.view_btn)

    def set_count(self, count):
        self.count_label.setText(str(count))

    def start_edit_label(self):
        self.line_edit = QLineEdit(self.label.text())
        self.layout.insertWidget(2, self.line_edit)
        self.line_edit.returnPressed.connect(self.finish_edit_label)

    def finish_edit_label(self):
        self.label.setText(self.line_edit.text())
        self.line_edit.hide()
示例#12
0
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
示例#13
0
文件: game.py 项目: trawl/gamelog
class GamePlayerWidget(QGroupBox):

    def __init__(self, nick, colour=None, parent=None):
        super(GamePlayerWidget, self).__init__(parent)
        self.player = nick
        self.pcolour = colour
        self.initUI()

    def initUI(self):
        #        self.setMinimumWidth(300)
        self.mainLayout = QHBoxLayout(self)
#         self.mainLayout.addStretch()
        self.scoreLCD = QLCDNumber(self)
        self.scoreLCD.setSegmentStyle(QLCDNumber.Flat)
        self.mainLayout.addWidget(self.scoreLCD)
        self.scoreLCD.setNumDigits(3)
        self.scoreLCD.setFixedSize(100, 60)
        self.scoreLCD.display(0)
        css = "QLCDNumber {{ color:rgb({},{},{});}}"
        self.scoreLCD.setStyleSheet(css.format(self.pcolour.red(),
                                               self.pcolour.green(),
                                               self.pcolour.blue()))

        self.nameLabel = QLabel(self)
        self.nameLabel.setText(self.player)
        sh = ("QLabel {{ font-size: 32px; font-weight: "
              "bold; color:rgb({},{},{});}}")
        self.nameLabel.setStyleSheet(sh.format(self.pcolour.red(),
                                               self.pcolour.green(),
                                               self.pcolour.blue()))
        self.mainLayout.addWidget(self.nameLabel)

        self.dealerPixmap = QtGui.QPixmap('icons/cards.png')
        self.nonDealerPixmap = QtGui.QPixmap()
        self.winnerPixmap = QtGui.QPixmap('icons/winner.png')

        self.iconlabel = IconLabel(self)
        self.iconlabel.setFixedSize(50, 50)
        self.iconlabel.setScaledContents(True)
        self.mainLayout.insertWidget(0, self.iconlabel)
#         self.mainLayout.addStretch()
        self.unsetDealer()

    def updateDisplay(self, points):
        if points >= 1000:
            self.scoreLCD.setNumDigits(4)
        self.scoreLCD.display(points)

    def setDealer(self): self.iconlabel.setPixmap(self.dealerPixmap)

    def unsetDealer(self): self.iconlabel.setPixmap(self.nonDealerPixmap)

    def setWinner(self): self.iconlabel.setPixmap(self.winnerPixmap)

    def setColour(self, colour):
        self.pcolour = colour
        css = "QLCDNumber {{ color:rgb({},{},{});}}"
        self.scoreLCD.setStyleSheet(css.format(self.pcolour.red(),
                                               self.pcolour.green(),
                                               self.pcolour.blue()))
        sh = ("QLabel {{ font-size: 32px; font-weight: bold; "
              "color:rgb({},{},{});}}")
        self.nameLabel.setStyleSheet(sh.format(self.pcolour.red(),
                                               self.pcolour.green(),
                                               self.pcolour.blue()))
示例#14
0
class TaskWidget(QWidget):

    on_remove = pyqtSignal(int)
    on_review = pyqtSignal(int)

    def __init__(self, rid, text, actions, icon=None, max_text_width=200, parent=None):
        super().__init__(parent)
        self.rid = rid
        self.actions = actions
        self.setObjectName("TaskWidget")

        self.label = QLabel(text)
        self.label.setWordWrap(True)
        self.label.setFixedWidth(max_text_width)
        if icon:
            self.icon_btn = QToolButton()
            self.icon_btn.setIcon(icon)
            self.icon_btn.setMaximumSize(20, 20)
            self.icon_btn.setAutoRaise(True)
        else:
            self.icon_btn = None

        self.checker = None

        self.layout = QHBoxLayout()
        self.layout.addWidget(self.label)
        if icon:
            self.layout.addWidget(self.icon_btn)
        self.layout.addStretch(0)
        self.setLayout(self.layout)

    def add_checker(self):
        # if checker is present just return
        if self.checker:
            return

        self.checker = QCheckBox()
        self.layout.insertWidget(0, self.checker)

    def remove_checker(self):
        self.layout.removeWidget(self.checker)
        self.checker.deleteLater()
        self.checker = None

    def set_text(self, text):
        self.label.setText(text)

    def mousePressEvent(self, event):
        super().mousePressEvent(event)

        if event.button() != Qt.RightButton:
            return
        action_menu = QMenu()
        action_map = {}
        for action in self.actions:
            if action.icon is None:
                a = action_menu.addAction(action.text)
            else:
                a = action_menu.addAction(action.icon, action.text)
            action_map[a] = action

        chosen_action = action_menu.exec_(self.mapToGlobal(event.pos()))
        if chosen_action is None:
            return
        action_map[chosen_action].signal.emit(self.mapToGlobal(event.pos()))
示例#15
0
class Main(UI_Main):

    resized = QtCore.pyqtSignal()
    total_price: int = 0

    def __init__(self):
        super().__init__()

        self.v = QVBoxLayout()
        self.h = QHBoxLayout()
        self.v.addStretch(1)
        self.h.addStretch(1)

        self.v.addLayout(self.h)

        self.v.addStretch(1)
        self.h.addStretch(1)

        self.modal_container = QFrame(self)
        self.modal_container.setObjectName('modal_container')
        self.modal_container.setLayout(self.v)
        self.resized.connect(self.on_mainWindow_resized)
        self.open_modal(self.login_form)

    def resizeEvent(self, event):
        self.resized.emit()

    def sync_cart(self):
        self.clear_cart()
        layout = self.table.products_layout
        for i in database.get_cart():
            self.update_total_price(i._selling_price * i._count)
            layout.insertWidget(layout.count() - 1, ProductTableItemWidget(i))

    def clear_cart(self):
        self.clear_layout(self.table.products_layout, 1)
        self.total_price = 0
        self.update_payment()

    def add2cart(self):
        product = self.sender().product
        database.add2cart(product.id)
        layout = self.table.products_layout
        product._count = 1
        self.update_total_price(product._selling_price * product._count)
        layout.insertWidget(0, ProductTableItemWidget(product))

    def update_total_price(self, add: int):
        self.total_price += add
        self.update_payment()

    def on_mainWindow_resized(self):
        if self.modal_container:
            self.modal_container.setFixedSize(self.size())

    @QtCore.pyqtSlot()
    def on_plus_clicked(self):
        self.open_modal(self.add_product_form)
        # self.open_add_product()

    @QtCore.pyqtSlot(str)
    def on_search_textEdited(self, filter_str: str = ''):
        self.update_list(filter_str)

    @QtCore.pyqtSlot(int)
    def on_to_pay_value_valueChanged(self, value: int = 0):
        self.update_payment()

    @QtCore.pyqtSlot()
    def on_pay_btn_clicked(self):
        database.sell()
        self.payment.to_pay_value.setValue(0)
        self.clear_cart()

    def update_payment(self):
        self.payment.surrender_value.setText(
            f'{self.payment.to_pay_value.value() - self.total_price}')
        self.payment.total_value.setText(f'{self.total_price}')

    def open_add_product(self):
        self.add_product_form.show()

    def update_list(self, filter_str: str = ''):
        self.clear_list()
        for p in database.get_products(filter_str):
            product = ProductWidget(p)
            product.clicked.connect(self.add2cart)
            self.sidebar.products_layout.insertWidget(
                self.sidebar.products_layout.count() - 1, product)

    def clear_list(self):
        self.clear_layout(self.sidebar.products_layout, 1)

    @staticmethod
    def clear_layout(layout, offset=0):
        for i in range(layout.count() - offset):
            layout.itemAt(i).widget().close()

    def open_modal(self, modal: QWidget):
        self.modal_container.setFixedSize(self.size())
        self.modal_container.show()

        self.h.insertWidget(1, modal, 1)
        self.h.itemAt(1).widget().show()

        shadow = QGraphicsDropShadowEffect()
        shadow.setColor(QColor(0, 0, 0, 100))
        shadow.setBlurRadius(50)
        shadow.setOffset(0, 10)

        modal.setGraphicsEffect(shadow)
        modal.setObjectName("modal")

    def close_modal(self):
        self.modal_container.hide()

    def close_add_product_form(self):
        self.close_modal()
        self.update_list()

    def close_login_form(self):
        self.close_modal()
        self.navbar.shop.setText(database.dbname)
        self.update_data()

    def update_data(self):
        self.update_list()
        self.sync_cart()
示例#16
0
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)
示例#17
0
class E5LineEdit(QLineEdit):
    """
    Class implementing a line edit widget showing some inactive text.
    """
    LeftSide = 0
    RightSide = 1

    def __init__(self, parent=None, inactiveText=""):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @param inactiveText text to be shown on inactivity (string)
        """
        super(E5LineEdit, self).__init__(parent)

        self.setMinimumHeight(22)

        self.setPlaceholderText(inactiveText)

        self.__mainLayout = QHBoxLayout(self)
        self.__mainLayout.setContentsMargins(0, 0, 0, 0)
        self.__mainLayout.setSpacing(0)

        self.__leftMargin = 0
        self.__leftWidget = E5LineEditSideWidget(self)
        self.__leftWidget.resize(0, 0)
        self.__leftLayout = QHBoxLayout(self.__leftWidget)
        self.__leftLayout.setContentsMargins(0, 0, 2, 0)
        if QApplication.isRightToLeft():
            self.__leftLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__leftLayout.setDirection(QBoxLayout.LeftToRight)
        self.__leftLayout.setSizeConstraint(QLayout.SetFixedSize)

        self.__rightWidget = E5LineEditSideWidget(self)
        self.__rightWidget.resize(0, 0)
        self.__rightLayout = QHBoxLayout(self.__rightWidget)
        self.__rightLayout.setContentsMargins(0, 0, 2, 0)
        if self.isRightToLeft():
            self.__rightLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__rightLayout.setDirection(QBoxLayout.LeftToRight)

        horizontalSpacer = QSpacerItem(0, 0, QSizePolicy.Expanding,
                                       QSizePolicy.Minimum)
        self.__mainLayout.addWidget(self.__leftWidget, 0,
                                    Qt.AlignVCenter | Qt.AlignLeft)
        self.__mainLayout.addItem(horizontalSpacer)
        self.__mainLayout.addWidget(self.__rightWidget, 0,
                                    Qt.AlignVCenter | Qt.AlignRight)
        if self.isRightToLeft():
            self.__mainLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__mainLayout.setDirection(QBoxLayout.LeftToRight)

        self.setWidgetSpacing(3)
        self.__leftWidget.sizeHintChanged.connect(self._updateTextMargins)
        self.__rightWidget.sizeHintChanged.connect(self._updateTextMargins)

    def setLeftMargin(self, margin):
        """
        Public method to set the left margin.
        
        @param margin left margin in pixel (integer)
        """
        self.__leftMargin = margin

    def leftMargin(self):
        """
        Public method to get the size of the left margin.
        
        @return left margin in pixel (integer)
        """
        return self.__leftMargin

    def event(self, evt):
        """
        Public method to handle events.
        
        @param evt reference to the event (QEvent)
        @return flag indicating, whether the event was recognized (boolean)
        """
        if evt.type() == QEvent.LayoutDirectionChange:
            if self.isRightToLeft():
                self.__mainLayout.setDirection(QBoxLayout.RightToLeft)
                self.__leftLayout.setDirection(QBoxLayout.RightToLeft)
                self.__rightLayout.setDirection(QBoxLayout.RightToLeft)
            else:
                self.__mainLayout.setDirection(QBoxLayout.LeftToRight)
                self.__leftLayout.setDirection(QBoxLayout.LeftToRight)
                self.__rightLayout.setDirection(QBoxLayout.LeftToRight)
        return QLineEdit.event(self, evt)

    def paintEvent(self, evt):
        """
        Protected method handling a paint event.
        
        @param evt reference to the paint event (QPaintEvent)
        """
        super(E5LineEdit, self).paintEvent(evt)

    def _updateTextMargins(self):
        """
        Protected slot to update the text margins.
        """
        if self.__leftMargin == 0:
            left = self.__leftWidget.sizeHint().width()
        else:
            left = self.__leftMargin
        right = self.__rightWidget.sizeHint().width()
        top = 0
        bottom = 0
        self.setTextMargins(left, top, right, bottom)

    def addWidget(self, widget, position):
        """
        Public method to add a widget to a side.
        
        @param widget reference to the widget to add (QWidget)
        @param position position to add to (E5LineEdit.LeftSide,
            E5LineEdit.RightSide)
        """
        if widget is None:
            return

        if self.isRightToLeft():
            if position == self.LeftSide:
                position = self.RightSide
            else:
                position = self.LeftSide
        if position == self.LeftSide:
            self.__leftLayout.addWidget(widget)
        else:
            self.__rightLayout.insertWidget(1, widget)

    def removeWidget(self, widget):
        """
        Public method to remove a widget from a side.
        
        @param widget reference to the widget to remove (QWidget)
        """
        if widget is None:
            return

        self.__leftLayout.removeWidget(widget)
        self.__rightLayout.removeWidget(widget)
        widget.hide()

    def widgetSpacing(self):
        """
        Public method to get the side widget spacing.
        
        @return side widget spacing (integer)
        """
        return self.__leftLayout.spacing()

    def setWidgetSpacing(self, spacing):
        """
        Public method to set the side widget spacing.
        
        @param spacing side widget spacing (integer)
        """
        self.__leftLayout.setSpacing(spacing)
        self.__rightLayout.setSpacing(spacing)
        self._updateTextMargins()

    def textMargin(self, position):
        """
        Public method to get the text margin for a side.
        
        @param position side to get margin for (E5LineEdit.LeftSide,
            E5LineEdit.RightSide)
        @return text margin (integer)
        """
        spacing = self.__rightLayout.spacing()
        w = 0
        if position == self.LeftSide:
            w = self.__leftWidget.sizeHint().width()
        else:
            w = self.__rightWidget.sizeHint().width()
        if w == 0:
            return 0
        return w + spacing * 2

    def inactiveText(self):
        """
        Public method to get the inactive text.
        
        @return inactive text (string)
        """
        if qVersionTuple() < (4, 7, 0):
            return self.__inactiveText
        else:
            return self.placeholderText()

    def setInactiveText(self, inactiveText):
        """
        Public method to set the inactive text.
        
        @param inactiveText text to be shown on inactivity (string)
        """

        self.setPlaceholderText(inactiveText)
示例#18
0
class SpaceLineEdit(QWidget):
    SPLIT_CHARS = ' ,;'

    def __init__(self, parent=None, validator=None, flags=Qt.WindowFlags()):
        # type: (QWidget, QValidator, Qt.WindowFlags) -> None
        super(SpaceLineEdit, self).__init__(parent, flags)
        self.setFocusPolicy(Qt.StrongFocus)
        self._validator = validator
        self._layout = QHBoxLayout(self)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self._prepareLineEdit(0)

    def setValidator(self, validator):  # type: (QValidator) -> None
        self._validator = validator

    def getValues(self):  # type: () -> List[str]
        values = []
        for index in range(self._layout.count()):
            text = self._layout.itemAt(index).widget().text()
            values.append(text)
        return values

    def setValues(self, values):  # type: (List[str]) -> None
        self._cleanAll()
        if not values:
            values = ['']
        for index, value in enumerate(values):
            self._prepareLineEdit(index, value)

    def _cleanAll(self):
        for index in reversed(range(self._layout.count())):
            widget = self._layout.takeAt(index).widget()  # type: QWidget
            widget.setParent(None)
            widget.deleteLater()

    def _prepareLineEdit(self, position, text=''):  # type: (int, str) -> None
        lineEdit = QLineEdit(str(text), self)
        lineEdit.setValidator(self._validator)
        lineEdit.textChanged.connect(partial(self.onTextChanged, lineEdit))
        lineEdit.installEventFilter(self)
        self._layout.insertWidget(position, lineEdit)

    def focusInEvent(self, event):
        item = self._layout.itemAt(0)
        if item:
            item.widget().setFocus()

    def onTextChanged(self, lineEdit, text):  # type: (QLineEdit, str) -> None
        if not text:
            self._removeLineEdit(lineEdit)

        text = self._convert(text)
        if self._isSplit(text):
            self._addLineEdit(lineEdit, text)

    def _removeLineEdit(self,
                        lineEdit,
                        changeFocus=True):  # type: (QLineEdit, bool) -> None
        if self._layout.count() <= 1:
            return

        if changeFocus:
            if not self._setFocus(lineEdit, offset=-1):
                self._setFocus(lineEdit, offset=1)

        self._layout.removeWidget(lineEdit)
        lineEdit.deleteLater()

    @staticmethod
    def _convert(text):  # type: (str) -> str
        for sch in SpaceLineEdit.SPLIT_CHARS[1:]:
            text = text.replace(sch, ' ')
        return text

    @staticmethod
    def _isSplit(text):  # type: (str) -> bool
        return ' ' in text

    def _addLineEdit(self, lineEdit, text):  # type: (QLineEdit, str) -> None
        texts = text.split()
        if not texts:
            lineEdit.setText('')
            return
        else:
            lineEdit.setText(texts[0])

        if len(texts) == 1:
            texts.append('')

        insertIndex = self._layout.indexOf(lineEdit) + 1
        for index, text in enumerate(texts[1:], insertIndex):
            self._prepareLineEdit(index, text)

        self._setFocus(lineEdit, offset=1)

    def eventFilter(self, watched, event):  # type: (QWidget, QEvent) -> bool
        if isinstance(watched, QLineEdit):
            lineEdit = watched  # type: QLineEdit

            if event.type() == QEvent.FocusOut and not lineEdit.text():
                self._removeLineEdit(lineEdit, changeFocus=False)

            elif event.type() == QEvent.KeyPress and isinstance(
                    event, QKeyEvent):
                if event.key() == Qt.Key_Backspace and not lineEdit.text():
                    self._removeLineEdit(lineEdit)

                if event.key() == Qt.Key_Down:
                    self._setFocus(lineEdit, offset=1)
                elif event.key() == Qt.Key_Up:
                    self._setFocus(lineEdit, offset=-1)

        return super(SpaceLineEdit, self).eventFilter(watched, event)

    def _setFocus(self, lineEdit, offset):  # type: (QLineEdit, int) -> bool
        index = self._layout.indexOf(lineEdit) + offset
        item = self._layout.itemAt(index)
        if item:
            item.widget().setFocus(Qt.OtherFocusReason)
            return True

        return False
示例#19
0
class TitleBar(QWidget):
    def __init__(self, parent=None):
        super(TitleBar, self).__init__(parent)

        self.title = 'no title'

        self._init_ui()
        self.setMouseTracking(True)

        font = QFont('Webdings')

        # close
        self.button_close = TitleButton('r')
        self.button_close.setFont(font)
        self.button_close.setObjectName('ButtonClose')
        self._r_hl.insertWidget(0, self.button_close)
        # max
        self.button_max = TitleButton('1')
        self.button_max.setFont(font)
        self.button_max.setObjectName('ButtonMax')
        self._r_hl.insertWidget(0, self.button_max)
        # min
        self.button_min = TitleButton('0')
        self.button_min.setFont(font)
        self.button_min.setObjectName('ButtonMin')
        self._r_hl.insertWidget(0, self.button_min)

    def _init_ui(self):
        hl = QHBoxLayout(self)
        hl.setContentsMargins(0, 0, 0, 0)
        hl.setSpacing(0)
        l_widget = BaseWidget()
        self._l_hl = QHBoxLayout(l_widget)
        self._l_hl.setContentsMargins(0, 0, 0, 0)
        self._l_hl.setSpacing(0)
        hl.addWidget(l_widget)
        hl.setContentsMargins(0, 0, 0, 0)
        self._label_title = QLabel(self.title)
        self._label_title.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self._label_title.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Minimum)
        hl.addWidget(self._label_title)
        r_widget = BaseWidget()
        self._r_hl = QHBoxLayout(r_widget)
        self._r_hl.setContentsMargins(0, 0, 0, 0)
        self._r_hl.setSpacing(0)
        hl.addWidget(r_widget)

    def set_title(self, title):
        if not isinstance(title, str):
            raise TypeError("'title' requires 'str' type.")
        self.title = title
        self._label_title.setText(self.title)

    def add_widget(self, icon, left=True):
        if not isinstance(icon, str):
            raise TypeError("'icon' requires 'str' type.")
        widget = TitleWidget()
        widget.setIcon(QIcon(icon))
        if left:
            self._l_hl.insertWidget(-1, widget)
        else:
            self._r_hl.insertWidget(0, widget)
示例#20
0
class MenuBar(QTabWidget):
    def __init__(self, parent=None):
        super(MenuBar, self).__init__(parent)

        tabbar = TabBar(parent)
        self.setTabBar(tabbar)
        self._init_ui()
        self.setMinimumHeight(125)
        self.setMouseTracking(True)

    def _set_height(self, height):
        self.setFixedHeight(height)

    _height = pyqtProperty(int, fset=_set_height)

    def _init_ui(self):
        font = QFont('Webdings')

        self._drop = False
        self._corner = CornerButton('6')
        self._corner.setObjectName('BUttonCorner')
        self._corner.setFont(font)
        self.setCornerWidget(self._corner, Qt.BottomRightCorner)
        self._corner.clicked.connect(self._corner_clicked)
        self.currentChanged.connect(self._current_changed)

    def _corner_clicked(self):
        self._ani = QPropertyAnimation(self, b'_height')
        self._ani.setDuration(500)

        if self._drop:
            self._corner.setText('5')
            self._drop = False
            self._ani.setStartValue(30)
            self._ani.setEndValue(125)
        else:
            self._corner.setText('6')
            self._drop = True
            self._ani.setStartValue(125)
            self._ani.setEndValue(30)
        self._ani.start()

    def _current_changed(self, index):
        tab_text = self.tabText(index)
        menu = self.findChild(MenuWidget, tab_text)
        self._ani1 = QPropertyAnimation(menu, b'_height')
        self._ani1.setDuration(500)
        self._ani1.setStartValue(0)
        self._ani1.setEndValue(95)
        self._ani1.start()

    def add_menu(self, p_str):
        p_str = "  {p_str}  ".format(p_str=p_str)
        menu = MenuWidget()
        menu.setObjectName(p_str)
        self.addTab(menu, p_str)
        self._hl = QHBoxLayout(menu)
        self._hl.setObjectName(p_str)
        self._hl.setContentsMargins(0, 0, 0, 0)
        self._hl.setSpacing(0)
        hs = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self._hl.addItem(hs)
        return menu

    def add_group(self, p_str, menu):
        group = GroupWidget(p_str, menu)
        group.setObjectName('group')
        insert_index = len(menu.findChildren(GroupWidget, 'group')) - 1
        self._hl.insertWidget(insert_index, group)
        return group
示例#21
0
class LineEdit(QLineEdit):
    inactiveText = QtDynamicProperty('inactiveText', str)
    widgetSpacing = QtDynamicProperty('widgetSpacing', int)

    def __init__(self, parent=None, contents=""):
        super(LineEdit, self).__init__(contents, parent)
        box_direction = QBoxLayout.RightToLeft if self.isRightToLeft(
        ) else QBoxLayout.LeftToRight
        self.inactiveText = ""
        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 = int(mid_height - self.left_widget.height() / 2)
            left_width = self.left_widget.width()
            if left_width == 0:
                left_height = int(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(
            int(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()
示例#22
0
class DiagramFieldView(QWidget):
    """ A Diagram field where up to three diagrams would be presented
        Attributes:
            main (QWidget): The main widget.
    """
    class Draw(QThread):
        """
        Args:
            QThread
        """
        def __init__(self, diagram: DiagramView):
            super().__init__()
            self.__diagram: DiagramView = diagram

        def run(self) -> NoReturn:
            self.__diagram.update_diagram()

    __diagram_field: 'DiagramFieldView'

    def __init__(self, parent: QWidget):
        """Initialising of a diagram field view
        Args:
            parent(QWidget)
        """
        super().__init__(parent)
        DiagramFieldView.__diagram_field = self

        self.__list: List[DiagramView] = []
        self.__dialog: Dialog = None
        self.__diagram_layout: QVBoxLayout = QVBoxLayout()
        self.__button_layout: QHBoxLayout = QHBoxLayout()
        self.__start_button: StartButtonView = StartButtonView()
        self.__maximize_button: QPushButton = QPushButton()

        self.__diagram_group: QtWidgets.QGroupBox = QtWidgets.QGroupBox(self)
        self.__group_layout: QtWidgets.QVBoxLayout = QtWidgets.QVBoxLayout(
            self.__diagram_group)
        self.__stretch_widget: QtWidgets.QWidget = QtWidgets.QWidget(self)
        self.__diagram_count: int = 0

        self.__start_button.start_signal.connect(self.__clear_diagrams)
        self.__maximize_button.clicked.connect(self.__maximize_on_click)
        ManagerModel.set_diagram_notifier(self)
        self.__init_ui()

    def __init_ui(self):
        """ this method initialises the user interface of the diagram field"""
        self.__maximize_button.setFixedSize(31, 31)
        self.__maximize_button.setIcon(
            QIcon(SystemInfo.RESOURCES + 'images/buttons/maximize.svg'))

        self.__diagram_group.setStyleSheet(
            "QGroupBox { border: 1px solid gray; background: white; }")
        self.__diagram_layout.addWidget(self.__diagram_group)

        self.__button_layout = QHBoxLayout()
        self.__button_layout.addWidget(self.__start_button)
        self.__button_layout.addStretch()
        self.__button_layout.addWidget(self.__maximize_button)

        main_layout = QVBoxLayout()
        main_layout.addLayout(self.__button_layout, 1)
        main_layout.addLayout(self.__diagram_layout, 1)
        main_layout.addStretch(0)

        self.setLayout(main_layout)

    def __clear_diagrams(self) -> NoReturn:
        """Clears the DiagramViews"""
        for diagram in self.__list:
            diagram.clear_diagram()

    def __maximize_on_click(self):
        """if maximize button clicked open dialog that contains diagrams on screen"""
        self.__dialog = Dialog(self.__list, self.__start_button)
        self.__dialog.close_signal.connect(self.__update_diagrams)
        self.__maximize_button.clearFocus()

    def update_view(self):
        threads = [self.Draw(diagram) for diagram in self.__list]
        [thread.start() for thread in threads]
        [thread.wait() for thread in threads]

    @pyqtSlot()
    def __update_diagrams(self):
        """adds diagrams in the diagram field view when maximized window is closed"""
        for diagram in self.__list:
            diagram.resize(280, 350)
            self.__group_layout.addWidget(diagram, 10, Qt.AlignTop)
        if self.__diagram_count == 1:
            self.add_stretch()
        self.__button_layout.insertWidget(0, self.__start_button)

    @staticmethod
    def add_diagram(diagram: DiagramView) -> NoReturn:
        """adds diagram to list of diagrams & add it to the layout """
        if len(DiagramFieldView.__diagram_field.__list) >= 3:
            raise DiagramMaximumReached
        DiagramFieldView.__diagram_field.__list.append(diagram)
        DiagramFieldView.__diagram_field.__diagram_count += 1
        if DiagramFieldView.__diagram_field.__diagram_count == 2:
            DiagramFieldView.__diagram_field.__group_layout.removeWidget(
                DiagramFieldView.__diagram_field.__stretch_widget)
        DiagramFieldView.__diagram_field.__group_layout.addWidget(
            diagram, 10, Qt.AlignTop)
        if DiagramFieldView.__diagram_field.__diagram_count == 1:
            DiagramFieldView.add_stretch()

    @staticmethod
    def delete_diagram(diagram: DiagramView) -> NoReturn:
        """delete diagram from list of diagrams and then removes it from layout"""
        DiagramFieldView.__diagram_field.__list.remove(diagram)
        DiagramFieldView.__diagram_field.__diagram_count -= 1
        DiagramFieldView.__diagram_field.__group_layout.removeWidget(diagram)
        if DiagramFieldView.__diagram_field.__diagram_count == 1:
            DiagramFieldView.add_stretch()
        diagram.close()

    @staticmethod
    def add_stretch() -> NoReturn:
        """adds a stretch to the diagram field"""
        DiagramFieldView.__diagram_field.__group_layout.addWidget(
            DiagramFieldView.__diagram_field.__stretch_widget, 10,
            Qt.AlignBottom)
示例#23
0
class PMessage(PSticky):
    def __init__(self, parent: QWidget, rect: QRect = None):
        super().__init__(parent)

        if rect is None:
            x = parent.x() + parent.width() / 2 - 150
            y = parent.y() + parent.height() / 2 - 50
            window_rect = QRect(x, y, 300, 100)
        else:
            x = rect.x() + rect.width() / 2 - 150
            y = rect.y() + rect.height() / 2 - 100
            window_rect = QRect(x, y, 300, 100)
        self.setGeometry(window_rect)

        self.action = QHBoxLayout()
        self.action.addStretch()
        self.action.addStretch()

        self.text = QLabel()
        self.text.setWordWrap(True)
        self.text.setStyleSheet("color:#666666;")

        layout = QVBoxLayout()
        layout.setSpacing(18)
        layout.addWidget(self.text, alignment=Qt.AlignTop)
        layout.addLayout(self.action)
        layout.setContentsMargins(12, 12, 12, 0)

        widget = QWidget()
        widget.setLayout(layout)
        widget.setContentsMargins(0, 8, 0, 0)
        widget.setObjectName(Parapluie.Object_Raised_Off)
        self.setCentralWidget(widget)

    def addButton(self, text, tpe, action=None, data=None):
        apply = QPushButton()
        apply.setText(text)
        if action:
            if data is None:
                apply.clicked.connect(action)
            else:
                apply.clicked.connect(lambda: action(text, data))

        if tpe == Parapluie.Button_Positive:
            apply.setObjectName(Parapluie.Object_OptimizeButton)
        elif tpe == Parapluie.Button_Negative:
            apply.setObjectName(Parapluie.Object_NegativeButton)

        self.action.insertWidget(1, apply, alignment=Qt.AlignCenter)

    def setMessage(self, message):
        self.text.setText(message)

    def initInformation(self, message, title="Notice"):
        self.setWindowTitle(title.upper())
        self.setMessage(message)
        self.addButton("CLOSE", Parapluie.Button_Neutral,
                       lambda: self.completeDestroy(0))
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

    def initWarning(self, message, title="Warning!!!", negative="CONTINUE"):
        self.setWindowTitle(title.upper())
        self.setMessage(message)
        self.addButton("CLOSE", Parapluie.Button_Neutral,
                       lambda: self.completeDestroy(0))
        self.addButton(negative, Parapluie.Button_Negative,
                       lambda: self.completeDestroy(1))
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

    def initQuestion(self, message, option: list, title="Choose a option..."):
        self.setWindowTitle(title.upper())
        self.setMessage(message)
        for o in option:
            if isinstance(o, str):
                self.addButton(o, Parapluie.Button_Positive,
                               self.onSelectedOption, option.index(o))
            elif isinstance(o, dict):
                if "text" in o and "type" in o:
                    self.addButton(o["text"], o["type"], self.onSelectedOption,
                                   option.index(o))

        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

    def onSelectedOption(self, text, data):
        self.completeDestroy(data)
示例#24
0
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)
示例#25
0
class DualEntry(QDialog):
    """ Just a simple Dialog for retrieving 2 values """
    def __init__(self,
                 entry1,
                 entry2,
                 input_type,
                 text,
                 min,
                 max,
                 parent=None):
        """
        Note: Use the static method
        """
        super().__init__(parent=parent)
        self.initUI(entry1, entry2, input_type, text, min, max)

    def initUI(self, entry1, entry2, input_type, text, min, max):
        self.setObjectName('Dialog')
        self.setStyleSheet("font: 10pt \"Arial\";")
        self.setWindowTitle('Input dialog')

        # Setting the parent makes it the default layout
        self.vlayout = QVBoxLayout(self)
        self.vlayout.setObjectName('vlayout')

        self.main_text = QLabel(self)
        self.main_text.setText(text)
        self.vlayout.insertWidget(0, self.main_text)
        self.hlayout = QHBoxLayout()
        self.hlayout.setObjectName('hlayout')
        self.vlayout.insertLayout(1, self.hlayout)

        if input_type is float:
            self.entry1 = QDoubleSpinBox(self)
            self.entry2 = QDoubleSpinBox(self)
        elif input_type is int:
            self.entry1 = QSpinBox(self)
            self.entry2 = QSpinBox(self)
        self.entry1.setMaximum(max)
        self.entry1.setMinimum(min)
        self.entry2.setMaximum(max)
        self.entry2.setMinimum(min)
        self.entry1.setValue(entry1)
        self.entry2.setValue(entry2)

        self.hlayout.insertWidget(0, self.entry1)
        self.hlayout.insertWidget(1, self.entry2)

        self.bb = QDialogButtonBox(self)
        self.bb.setStandardButtons(QDialogButtonBox.Cancel
                                   | QDialogButtonBox.Ok)
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.vlayout.insertWidget(-1, self.bb)
        self.show()

    @staticmethod
    def getDualEntries(entry1,
                       entry2,
                       input_type=float,
                       text='Enter Values:',
                       min=-10000000000,
                       max=10000000000,
                       parent=None):
        """
        Pops up a dialog box that retrieves 2 values, either float or int

        Parameters
        ----------
        entry1: int or float
            Default value in the left box

        entry2: int or float
            Default value in the right box

        input_type : type
            Either float or int are currently supported

        text : str
            Text describing the values to set

        min : int or float
            Minimum value for entries

        max : int or float
            Maximum value for entries

        parent : object or None
            Parent of the Dialog (QDialog) box.

        Returns
        -------
        (numeric, numeric), int
            Returns (left value, right value), Ok pressed. If Ok was pressed, value == 1, else 0.    

        """
        dialog = DualEntry(entry1, entry2, input_type, text, min, max, parent)
        result = dialog.exec_()
        if result:
            return (dialog.entry1.value(), dialog.entry2.value()), result
        else:
            return (), result
示例#26
0
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()
示例#27
0
class ViewDataWindow(QDialog):
    formSubmitted = pyqtSignal()
    closeSignal = pyqtSignal()

    def __init__(self, LOGIN, parent=None):
        #super(ViewDataWindow, self).__init__(parent)
        super().__init__(parent)
        self.LOGIN = LOGIN
        self.db = db(self.LOGIN, "Brewing")
        self.displayNo = 0
        self.displayedBrewsH = []
        self.displayedBrewsVMash = []
        self.displayedBrewsVBoil = []
        self.displayedBrewsVFerment = []
        self.create_layout_viewData()
        self.setWindowTitle('Past Brew Viewer')
        self.activeColours = []
        self.colours = ["red", "green", "blue", "black",\
                        "cyan", "magenta", "yellow", "gray"]

    # Function to create layout of New Brew window
    def create_layout_viewData(self):

        # Create scroll boxes for filtering data
        filterGroupBox = QGroupBox("Filter by:")

        # Date edit box
        lab_date = QLabel("Date:")
        self.dateEdit = QDateEdit()
        # Set range of possible dates, current date is max date
        self.maxDate = QDate().currentDate()
        self.minDate = QDate(2019, 1, 1)
        self.dateEdit.setDate(self.maxDate)
        self.dateEdit.setDateRange(self.minDate, self.maxDate)
        self.dateEdit.setCalendarPopup(1)
        self.dateEdit.setDisplayFormat("dd/MM/yy")
        #self.dateEdit2 = QLineEdit()
        self.dateEdit.dateChanged.connect(self.filter_date)

        dateHLayout = QHBoxLayout()
        dateHLayout.addWidget(lab_date)
        dateHLayout.addWidget(self.dateEdit)

        ### Text edit filters ###
        # Batch ID search
        lab_batch = QLabel("Batch ID:")
        self.edit_batchID = QLineEdit()
        self.edit_batchID.setPlaceholderText("Enter Batch ID")
        self.but_IDsearch = QPushButton("Go")
        self.but_IDsearch.setAutoDefault(0)
        self.but_IDsearch.clicked.connect(self.filter_batchID)
        self.edit_batchID.returnPressed.connect(self.filter_batchID)
        batchHLayout = QHBoxLayout()
        batchHLayout.addWidget(lab_batch)
        batchHLayout.addWidget(self.edit_batchID)
        batchHLayout.addWidget(self.but_IDsearch)

        # Recipe search
        lab_recipe = QLabel("Recipe:")
        self.lineEdit_recipe = QLineEdit()
        self.lineEdit_recipe.setPlaceholderText("Enter Recipe")
        self.lineEdit_recipe.textChanged.connect(self.filter_recipe)
        self.lineEdit_recipe.returnPressed.connect(self.filter_recipe)
        recipeHLayout = QHBoxLayout()
        recipeHLayout.addWidget(lab_recipe)
        recipeHLayout.addWidget(self.lineEdit_recipe)

        # Clear filters button
        self.but_clearFilter = QPushButton("Clear Filters")
        self.but_clearFilter.setAutoDefault(0)
        clearHLayout = QHBoxLayout()
        clearHLayout.addStretch(1)
        clearHLayout.addWidget(self.but_clearFilter)
        clearHLayout.addStretch(0)
        self.but_clearFilter.clicked.connect(self.clearFilters)

        # Filter groupbox layout

        #recipeHLayout.addWidget(self.recipeEdit)
        #recipeVLayout = QVBoxLayout()
        #recipeVLayout.addLayout(recipeHLayout)
        #recipeVLayout.addWidget(self.lineEdit_recipe)
        filterHLayout = QHBoxLayout()
        filterHLayout.addStretch(1)
        filterHLayout.addWidget(lab_date)
        filterHLayout.addWidget(self.dateEdit)
        filterHLayout.addStretch(1)
        #filterHLayout.addLayout(recipeVLayout)
        filterHLayout.addStretch(1)
        filterHLayout.addWidget(self.edit_batchID)
        filterHLayout.addWidget(self.but_IDsearch)
        filterHLayout.addStretch(1)
        #filterGroupBox.setLayout(filterHLayout)

        # Alternate - Filter vertical layout
        filterVLayout = QVBoxLayout()
        filterVLayout.addLayout(batchHLayout)
        filterVLayout.addLayout(recipeHLayout)
        filterVLayout.addLayout(dateHLayout)
        filterVLayout.addLayout(clearHLayout)
        filterGroupBox.setLayout(filterVLayout)

        # scrollHLayout = QHBoxLayout()
        # scrollHLayout.addWidget(filterGroupBox)
        # scrollHLayout.addStretch(1)

        # Create QTableView of brew data
        header = ['Brew ID', 'Recipe', 'Date']
        self.model = MyTableModel(
            self.db.readFromTable("Brews", "id, Recipe, Date"), header, self)
        self.proxyModel = QSortFilterProxyModel(self)
        self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.proxyModel.setSourceModel(self.model)
        self.dataTable = QTableView()
        self.dataTable.setModel(self.proxyModel)
        self.dataTable.setSortingEnabled(True)
        self.dataTable.setSelectionBehavior(1)

        # Create bottom buttons
        self.but_quit = QPushButton("Close")
        self.but_quit.setAutoDefault(0)
        self.but_view = QPushButton("Display Brew")
        self.but_view.setAutoDefault(0)
        quitHLayout = QHBoxLayout()
        quitHLayout.addStretch(0)
        quitHLayout.addWidget(self.but_quit)
        quitHLayout.addStretch(3)
        quitHLayout.addWidget(self.but_view)
        quitHLayout.addStretch(0)

        # Main vertical layout for left area
        lWidget = QWidget()
        vLayoutL = QVBoxLayout(lWidget)
        vLayoutL.addWidget(filterGroupBox)
        vLayoutL.addWidget(self.dataTable)
        vLayoutL.addLayout(quitHLayout)

        # Widget for displayed brews - Widget allows fixed height to be set
        displayedWidget = QWidget()
        #displayedWidget.setFixedHeight(180)
        # h Layout to add groupboxes of displayed brews - added to in viewButtonClicked slot
        self.hLayoutDisplayed = QHBoxLayout()
        self.hLayoutDisplayed.addStretch(1)
        self.hLayoutDisplayed.setSizeConstraint(5)
        displayedWidget.setLayout(self.hLayoutDisplayed)
        # Scroll area for horizontal displayed brews
        hScrollArea = QScrollArea()
        hScrollArea.setWidget(displayedWidget)
        hScrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        hScrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        #hScrollArea.setFixedHeight(120)
        hScrollArea.setMinimumHeight(20)
        # Main v layout for displayed brews widget
        displayTitle = QLabel("Displayed Brews:")
        displayTitle.setMaximumHeight(20)
        hDisplayedWidget = QWidget()
        self.vLayoutDisplayed = QVBoxLayout(hDisplayedWidget)
        self.vLayoutDisplayed.addWidget(displayTitle)
        self.vLayoutDisplayed.addWidget(hScrollArea)

        # Main vertical layout for right area
        rWidget = QWidget()
        rSplitter = QSplitter()
        rSplitter.setOrientation(Qt.Orientation(2))
        #vLayoutR = QVBoxLayout(rWidget)
        self.tabs = QTabWidget()
        self.tabMash = MashTab()
        self.tabBoil = BoilTab()
        self.tabFerment = FermentTab()
        self.tabs.resize(100, 1000)
        self.tabs.setMinimumSize(400, 100)
        self.tabs.addTab(self.tabMash, "Mash")
        self.tabs.addTab(self.tabBoil, "Boil")
        self.tabs.addTab(self.tabFerment, "Ferment")
        #vLayoutR.addLayout(self.vLayoutDisplayed)
        #vLayoutR.addWidget(self.tabs)
        rSplitter.addWidget(hDisplayedWidget)
        rSplitter.addWidget(self.tabs)

        # Main layout for whole window - splitter so widget sizes are adjustable
        mainLayout = QHBoxLayout()
        mainSplitter = QSplitter()
        mainSplitter.addWidget(lWidget)
        mainSplitter.addWidget(rSplitter)
        mainLayout.addWidget(mainSplitter)
        mainSplitter.setSizes([260, 740])

        self.setLayout(mainLayout)
        #self.showFullScreen()
        self.setGeometry(0, 0, 1500, 1000)

        self.but_view.clicked.connect(self.viewButtonClicked)
        self.but_view.clicked.connect(self.viewButtonClickedTabs)
        self.but_quit.clicked.connect(self.quitButtonClicked)

    def quitButtonClicked(self):

        self.close()
        self.closeSignal.emit()

    # Slot for adding selected brew to widget
    def viewButtonClicked(self):

        self.brewInfo = []  # array to place brew info: ID, Recipe, Date
        self.displayNo = self.displayNo + 1
        # Add brew info to array based on selected row
        index = (self.dataTable.selectionModel().currentIndex())
        for i in range(3):
            self.brewInfo.append(
                QLabel(str(index.sibling(index.row(), i).data())))

        # Choose colour to use for this displayed brew
        self.colourToUse = self.chooseColour()

        # Create group box with all brew info displayed and Remove button
        brewGroupBox = QGroupBox(str(self.displayNo))
        brewGroupBox.setObjectName("ColouredGroupBox")
        brewGroupBox.setStyleSheet(
            "QGroupBox#ColouredGroupBox { border: 2px solid %s;}" %
            self.colourToUse)
        brewForm = QFormLayout()
        brewForm.addRow(QLabel('Brew ID:'), self.brewInfo[0])
        brewForm.addRow(QLabel('Recipe:'), self.brewInfo[1])
        brewForm.addRow(QLabel('Date:'), self.brewInfo[2])
        removeButHLayout = QHBoxLayout()
        removeButHLayout.addStretch(1)
        self.but_Remove = QPushButton("Remove")
        removeButHLayout.addWidget(self.but_Remove)
        brewForm.addRow(removeButHLayout)
        brewGroupBox.setLayout(brewForm)
        # Add group box to layout - use insert so that stretch stays on right side
        self.hLayoutDisplayed.insertWidget(self.displayNo - 1, brewGroupBox)
        self.displayedBrewsH.append(
            brewGroupBox)  # Add groupbox to array of displayed brews
        # Signal to connect remove brew button. Lambda function used to pass argument of specific
        # brew to be removed
        self.but_Remove.clicked.connect(
            lambda: self.removeBrewClickedMash(brewGroupBox))
        self.but_Remove.clicked.connect(
            lambda: self.removeBrewClickedBoil(brewGroupBox))
        self.but_Remove.clicked.connect(
            lambda: self.removeBrewClickedFerment(brewGroupBox))
        self.but_Remove.clicked.connect(
            lambda: self.removeBrewClicked(brewGroupBox))

    # Slot for adding brew info to each of the process tabs
    def viewButtonClickedTabs(self):

        batchID = self.brewInfo[0].text()
        # Query database to get recipe data for the brew selected to view
        # brewInfo[0].text() gives brew ID for selected brew from table
        sql = f"SELECT * FROM Brews WHERE id = '{self.brewInfo[0].text()}'"
        query = self.db.custom(sql)

        self.recipedata = {}
        self.recipedata['batchID'] = query[0][0]
        self.recipedata['recipeName'] = query[0][1]
        self.recipedata['recipeDate'] = query[0][2]
        self.recipedata['mashTemp'] = query[0][3]
        self.recipedata['mashTime'] = query[0][4]
        self.recipedata['boilTemp'] = query[0][5]
        self.recipedata['boilTime'] = query[0][6]
        self.recipedata['hop1'] = (query[0][7], query[0][8])
        self.recipedata['hop2'] = (query[0][9], query[0][10])
        self.recipedata['hop3'] = (query[0][11], query[0][12])
        self.recipedata['hop4'] = (query[0][13], query[0][14])
        self.recipedata['fermenttemp'] = query[0][15]

        # Create groupboxes for each of the process tabs to fill with brew info
        mashGroupBox = QGroupBox(str(self.displayNo))
        mashGroupBox.setObjectName("MashColouredGroupBox")
        mashGroupBox.setStyleSheet(
            "QGroupBox#MashColouredGroupBox { border: 2px solid %s;}" %
            self.colourToUse)
        mashFormLayout = QFormLayout()
        mashFormLayout.addRow(
            QLabel(f"Temp ({DEGREES}C): {self.recipedata['mashTemp']}"))
        mashFormLayout.addRow(
            QLabel(f"Time (mins): {self.recipedata['mashTime']}"))
        mashGroupBox.setLayout(mashFormLayout)
        self.tabMash.mashVLayout.insertWidget(self.displayNo - 1, mashGroupBox)
        self.displayedBrewsVMash.append(mashGroupBox)

        boilGroupBox = QGroupBox(str(self.displayNo))
        boilGroupBox.setObjectName("BoilColouredGroupBox")
        boilGroupBox.setStyleSheet(
            "QGroupBox#BoilColouredGroupBox { border: 2px solid %s;}" %
            self.colourToUse)
        boilFormLayout = QFormLayout()
        boilFormLayout.addRow(
            QLabel(f"Temp ({DEGREES}C):{self.recipedata['boilTemp']}"))
        boilFormLayout.addRow(
            QLabel(f"Time (mins): {self.recipedata['boilTime']}"))
        boilFormLayout.addRow(QLabel(f"Hop 1: {self.recipedata['hop1'][0]}"))
        boilFormLayout.addRow(
            QLabel(f"Time (mins): {self.recipedata['hop1'][1]}"))
        boilFormLayout.addRow(QLabel(f"Hop 2: {self.recipedata['hop2'][0]}"))
        boilFormLayout.addRow(
            QLabel(f"Time (mins): {self.recipedata['hop2'][1]}"))
        boilFormLayout.addRow(QLabel(f"Hop 3: {self.recipedata['hop3'][0]}"))
        boilFormLayout.addRow(
            QLabel(f"Time (mins): {self.recipedata['hop3'][1]}"))
        boilFormLayout.addRow(QLabel(f"Hop 4: {self.recipedata['hop4'][0]}"))
        boilFormLayout.addRow(
            QLabel(f"Time (mins): {self.recipedata['hop4'][1]}"))
        boilGroupBox.setLayout(boilFormLayout)
        self.tabBoil.boilVLayout.insertWidget(self.displayNo - 1, boilGroupBox)
        self.displayedBrewsVBoil.append(boilGroupBox)

        fermentGroupBox = QGroupBox(str(self.displayNo))
        fermentGroupBox.setObjectName("FermentColouredGroupBox")
        fermentGroupBox.setStyleSheet(
            "QGroupBox#FermentColouredGroupBox { border: 2px solid %s;}" %
            self.colourToUse)
        fermentFormLayout = QFormLayout()
        fermentFormLayout.addRow(
            QLabel(f"Temp ({DEGREES}C): {self.recipedata['fermenttemp']}"))
        #fermentFormLayout.addRow(QLabel('Time (mins):'))
        fermentGroupBox.setLayout(fermentFormLayout)
        self.tabFerment.fermentVLayout.insertWidget(self.displayNo - 1,
                                                    fermentGroupBox)
        self.displayedBrewsVFerment.append(fermentGroupBox)

        ### PUTTING DATA ONTO GRAPHS ###
        # Query database to get data to plot on graphs
        sqlMash = f"SELECT TimeStamp, Temp FROM Mash WHERE BatchID = '{batchID}'"
        sqlBoil = f"SELECT TimeStamp, Temp FROM BoilMonitor WHERE BatchID = '{batchID}'"
        sqlFermentTemp = f"SELECT TimeStamp, Temp FROM Ferment WHERE BatchID = '{batchID}'"
        sqlFermentSG = f"SELECT TimeStamp, Sg FROM Ferment WHERE BatchID = '{batchID}'"
        mashDataX, mashDataY = self.createData(sqlMash)
        boilDataX, boilDataY = self.createData(sqlBoil)
        fermentTempDataX, fermentTempDataY = self.createData(sqlFermentTemp)
        fermentSGDataX, fermentSGDataY = self.createData(sqlFermentSG)

        # Create and add curves to each of the plots
        self.tabMash.graph.createCurve(mashDataX, mashDataY, self.colourToUse)
        self.tabBoil.graph.createCurve(boilDataX, boilDataY, self.colourToUse)
        self.tabFerment.tempGraph.createCurve(fermentTempDataX[1:],
                                              fermentTempDataY[1:],
                                              self.colourToUse)
        self.tabFerment.gravGraph.createCurve(fermentSGDataX[1:],
                                              fermentSGDataY[1:],
                                              self.colourToUse)

    # Function to choose first available colour
    def chooseColour(self):

        # Loop through dictionary, checking if self.colours[j] appear
        for colours in self.colours:
            # If it does appear, continue to next colour
            if colours not in self.activeColours:
                # If it doesn't appear, add colour to dictionary and return colour
                self.activeColours.append(colours)
                return colours

    # Get data from database using sql query
    def createData(self, sql):
        timestamps = []
        tempdat = []
        try:
            for data in self.db.custom(sql):
                timestamps.append(data[0])
                tempdat.append(data[1])

            startTime = timestamps[0]
            for i in range(len(timestamps)):
                timestamps[i] = (timestamps[i] - startTime).seconds
        except IndexError:
            return [0], [0]

        return timestamps, tempdat

    # Slot for removing group boxes from horizontal tab
    def removeBrewClicked(self, brewToRemove):

        brewArrayPos = self.displayedBrewsH.index(brewToRemove)

        self.hLayoutDisplayed.removeWidget(
            brewToRemove)  # remove widget from layout
        brewToRemove.setParent(None)  # remove parent so widget dissappears
        self.displayNo = self.displayNo - 1
        self.displayedBrewsH.remove(
            brewToRemove)  # remove brew from array of displayed brews
        i = 0
        # Loop to renumber the remaining displayed groupboxes using the array
        for i in range(len(self.displayedBrewsH)):
            self.displayedBrewsH[i].setTitle(str(i + 1))

        del self.activeColours[brewArrayPos]

    # Slot for removing group boxes of brew info in mash tab
    def removeBrewClickedMash(self, brewToRemove):

        # Obtain position in array of displayed brews of brew to remove
        brewArrayPos = self.displayedBrewsH.index(brewToRemove)
        # Use position to remove widget from layout
        self.tabMash.mashVLayout.takeAt(brewArrayPos)
        # Use position to remove parent
        self.displayedBrewsVMash[brewArrayPos].setParent(None)
        # Use position to delete from vertical array
        del self.displayedBrewsVMash[brewArrayPos]
        # Renumber groupboxes in vertical display
        for i in range(len(self.displayedBrewsVMash)):
            self.displayedBrewsVMash[i].setTitle(str(i + 1))

        # Remove curve from graph
        self.tabMash.graph.removeCurve(brewArrayPos)

    # Slot for removing group boxes of brew info in boil tab
    def removeBrewClickedBoil(self, brewToRemove):

        # Obtain position in array of displayed brews of brew to remove
        brewArrayPos = self.displayedBrewsH.index(brewToRemove)
        # Use position to remove widget from layout
        self.tabBoil.boilVLayout.takeAt(brewArrayPos)
        # Use position to remove parent
        self.displayedBrewsVBoil[brewArrayPos].setParent(None)
        # Use position to delete from vertical array
        del self.displayedBrewsVBoil[brewArrayPos]
        # Renumber groupboxes in vertical display
        for i in range(len(self.displayedBrewsVBoil)):
            self.displayedBrewsVBoil[i].setTitle(str(i + 1))

        self.tabBoil.graph.removeCurve(brewArrayPos)

    # Slot for removing group boxes of brew info in ferment tab
    def removeBrewClickedFerment(self, brewToRemove):

        # Obtain position in array of displayed brews of brew to remove
        brewArrayPos = self.displayedBrewsH.index(brewToRemove)
        # Use position to remove widget from layout
        self.tabFerment.fermentVLayout.takeAt(brewArrayPos)
        # Use position to remove parent
        self.displayedBrewsVFerment[brewArrayPos].setParent(None)
        # Use position to delete from vertical array
        del self.displayedBrewsVFerment[brewArrayPos]
        # Renumber groupboxes in vertical display
        for i in range(len(self.displayedBrewsVFerment)):
            self.displayedBrewsVFerment[i].setTitle(str(i + 1))

        self.tabFerment.tempGraph.removeCurve(brewArrayPos)
        self.tabFerment.gravGraph.removeCurve(brewArrayPos)

    # Slot for filtering by Batch IDdisplayed
    def filter_batchID(self):
        self.lineEdit_recipe.clear()
        self.proxyModel.setFilterRegExp(self.edit_batchID.text())
        self.proxyModel.setFilterKeyColumn(0)

    # Slot for filtering by Recipe
    def filter_recipe(self):
        self.edit_batchID.clear()
        self.proxyModel.setFilterRegExp(self.lineEdit_recipe.text())
        self.proxyModel.setFilterKeyColumn(1)

    # Slot for filtering by date
    def filter_date(self):
        self.lineEdit_recipe.clear()
        self.edit_batchID.clear()
        self.proxyModel.setFilterRegExp(
            self.dateEdit.date().toString("dd/MM/yy"))
        self.proxyModel.setFilterKeyColumn(2)

    # Slot for clearing all filters
    def clearFilters(self):
        self.dateEdit.setDate(self.maxDate)
        self.proxyModel.setFilterRegExp('')
        self.proxyModel.setFilterKeyColumn(0)
        self.proxyModel.setFilterKeyColumn(1)
        self.proxyModel.setFilterKeyColumn(2)
        self.lineEdit_recipe.clear()
        self.edit_batchID.clear()
示例#28
0
class FormWidget(FormPage):
    instanceAssigned = pyqtSignal(str)
    instanceDeleted = pyqtSignal(str)
    instanceSaved = pyqtSignal(str)
    exception = pyqtSignal(str)

    def __init__(self, parent = None, buttons = FormButtons.EditButtons):
        super(FormWidget, self).__init__(parent)
        self.buildButtonBox(buttons)
        self.tabs = None
        self._tabs = {}

    def buildButtonBox(self, buttons):
        buttonWidget = QGroupBox()
        self.buttonbox = QHBoxLayout(buttonWidget)
        if buttons & FormButtons.DeleteButton:
            self.deletebutton = QPushButton("Delete", self)
            self.deletebutton.clicked.connect(self.deleteInstance)
            self.buttonbox.addWidget(self.deletebutton)
        self.buttonbox.addStretch(1)
        if buttons & FormButtons.ResetButton:
            self.resetbutton = QPushButton("Reset", self)
            self.resetbutton.clicked.connect(self.setInstance)
            self.buttonbox.addWidget(self.resetbutton)
        if buttons & FormButtons.SaveButton:
            self.savebutton = QPushButton("Save", self)
            self.savebutton.clicked.connect(self.save)
            self.buttonbox.addWidget(self.savebutton)
        self.layout().addWidget(buttonWidget)

    def addWidgetToButtonBox(self, widget, *args):
        self.buttonbox.insertWidget(0, widget, *args)

    def addTab(self, widget, title):
        if self.tabs is None:
            self.tabs = QTabWidget(self)
            self.tabs.currentChanged[int].connect(self.tabChanged)

            # Remove stretch at the bottom:
            self._removeStretch()
            self.vbox.addWidget(self.tabs, 1)
        if isinstance(widget, FormPage):
            self.form.addSubLayout(widget.form)
        self.tabs.addTab(widget, title)
        self._tabs[title] = widget
        return widget

    def count(self):
        return self.tabs and self.tabs.count()

    def setTab(self, tab):
        if self.tabs and tab <= self.tabs.count():
            self.tabs.setCurrentIndex(tab)

    def tabChanged(self, ix):
        w = self.tabs.currentWidget()
        if hasattr(w, "selected"):
            w.selected()

    def save(self):
        try:
            self.form.retrieve(self.instance())
            if hasattr(self, "retrieve") and callable(self.retrieve):
                self.retrieve(self.instance())
            self.instanceSaved.emit(str(self.instance.key()))
            self.statusMessage.emit("Saved")
        except:
            self.exception.emit("Save failed...")
        self.setInstance()

    def instance(self):
        return self._instance

    def setInstance(self, instance = None):
        if instance:
            self._instance = instance
        self.form.apply(self.instance())
        if hasattr(self, "assign") and callable(self.assign):
            self.assign(self.instance())
        self.instanceAssigned.emit(str(self.instance().key()))

    def confirmDelete(self):
        return QMessageBox.warning(self, "Are you sure?",
                                   "Are you sure you want to delete this?",
                                    QMessageBox.Cancel | QMessageBox.Ok,
                                    QMessageBox.Cancel) == QMessageBox.Ok

    def onDelete(self):
        try:
            with gripe.db.Tx.begin():
                key = str(self.instance().key())
                if grumble.model.delete(self.instance()):
                    self.instanceDeleted.emit(key)
        except:
            traceback.print_exc()
            self.exception.emit("Delete failed...")

    def deleteInstance(self):
        if self.instance() and self.confirmDelete():
            self.onDelete()
示例#29
0
class MainWindow(QMainWindow):

    # signal to be emitted when device/channel is changed
    _sig_entry_form_changed = pyqtSignal(object)

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # aliases
        self._statusbar = self.ui.statusBar
        self._messagelog = self.ui.txtMessageLog
        self._overview = self.ui.fmOverview
        self._plots = self.ui.fmPlots
        self._gbpinnedplot = self.ui.gbPinnedPlot
        self._tabview = self.ui.tabMain
        self._btnload = self.ui.btnLoad
        self._btnsave = self.ui.btnSave
        self._btnsaveas = self.ui.btnSaveAs
        self._btnquit = self.ui.btnQuit
        self._btnabout = self.ui.btnAbout

        # set up pinned plot
        self._pinnedplot = DateTimePlotWidget()
        self._gbpinnedplot.layout().itemAt(0).widget().deleteLater()
        self._gbpinnedplot.layout().insertWidget(0, self._pinnedplot)

        # Icons
        self._icon_path = os.path.join(
            os.path.abspath(os.path.dirname(__file__)), "images/icons")

        self._btnquit.setIcon(
            QIcon(QPixmap(os.path.join(self._icon_path, 'process-stop.png'))))
        self._btnload.setIcon(
            QIcon(QPixmap(os.path.join(self._icon_path, 'document-open.png'))))
        self._btnsave.setIcon(
            QIcon(QPixmap(os.path.join(self._icon_path, 'document-save.png'))))
        self._btnsaveas.setIcon(
            QIcon(QPixmap(os.path.join(self._icon_path, 'document-save.png'))))
        self._btnabout.setIcon(
            QIcon(QPixmap(os.path.join(self._icon_path, 'help-browser.png'))))

        self.ui.btnExpand.setIcon(
            QIcon(QPixmap(os.path.join(self._icon_path, 'list-add.png'))))
        self.ui.btnCollapse.setIcon(
            QIcon(QPixmap(os.path.join(self._icon_path, 'list-remove.png'))))

        # About dialog connection
        self._btnabout.triggered.connect(self.show_AboutDialog)
        self.ui.action_about.triggered.connect(self.show_AboutDialog)

        # Help will open the README.md
        self.ui.action_help.triggered.connect(self.show_readme)

        # tab changes
        self._current_tab = 'main'
        self._tabview.currentChanged.connect(self.tab_changed)

        # set up containers
        self._overview_layout = QHBoxLayout()
        self._overview.setLayout(self._overview_layout)
        self._overview_layout.addStretch()

        self._plot_layout = QGridLayout()
        self._plots.setLayout(self._plot_layout)

        # settings page
        self.ui.treeDevices.setHeaderLabels(['Label', 'Type'])
        self.ui.treeDevices.currentItemChanged.connect(
            self.on_settings_row_changed)
        self.ui.btnExpand.clicked.connect(self.ui.treeDevices.expandAll)
        self.ui.btnCollapse.clicked.connect(self.ui.treeDevices.collapseAll)
        self._devvbox = QVBoxLayout()
        self.ui.fmDeviceSettings.setLayout(self._devvbox)

        # local copies of data
        self._settings_devices = {}

    def apply_settings(self, settings):
        self.resize(settings['window-width'], settings['window-height'])

        self.ui.splitter_horizontal.setSizes(
            [settings['split-main-first'], settings['split-main-second']])

        self.ui.splitSettings.setSizes([
            settings['split-settings-first'], settings['split-settings-second']
        ])

        self.move(settings['window-pos-x'], settings['window-pos-y'])

    def current_settings(self):
        return {
            'split-main-first':
            self.ui.splitter_horizontal.sizes()[0],
            'split-main-second':
            self.ui.splitter_horizontal.sizes()[1],
            'split-settings-first':
            self.ui.splitSettings.sizes()[0],
            'split-settings-second':
            self.ui.splitSettings.sizes()[1],
            'window-maximize-state':
            True if self.windowState == Qt.WindowMaximized else False,
            'window-pos-x':
            self.pos().x(),
            'window-pos-y':
            self.pos().y(),
            'window-width':
            self.frameSize().width(),
            'window-height':
            self.frameSize().height(),
        }

    @property
    def current_tab(self):
        return self._current_tab

    def tab_changed(self):
        tabName = self._tabview.tabText(self._tabview.currentIndex())
        if tabName == 'Overview':
            self._current_tab = 'main'
        elif tabName == 'Devices':
            self._current_tab = 'devices'
        elif tabName == 'Plotting':
            self._current_tab = 'plots'

    # ---- Tab Update Functions ----
    def update_overview(self, devices):
        self.clearLayout(self._overview_layout)
        order_devlist = sorted([x for _, x in devices.items()],
                               key=lambda y: y.overview_order)
        for device in order_devlist:
            if 'overview' in device.pages:
                self._overview_layout.insertWidget(0, device._overview_widget)
        self._overview_layout.addStretch()

    def update_plots(self, devices, plotted_channels):
        """ Draw the plotted channels, as specified by the PlotChooseDialog """
        self.clearLayout(self._plot_layout)
        row = 0
        col = 0
        for device_name, device in devices.items():
            for channel_name, channel in device.channels.items():
                if channel in plotted_channels:
                    self._plot_layout.addWidget(channel._plot_widget, row, col)
                    row += 1
                    if row == 2:
                        row = 0
                        col += 1

    def update_device_settings(self, devices):
        """ Populates the treeview on the devices tab """
        self.clearLayout(self._devvbox)
        self.ui.treeDevices.clear()
        for device_name, device in devices.items():
            devrow = QTreeWidgetItem(self.ui.treeDevices)
            devrow.setText(0, device.label)
            devrow.setText(1, 'Device')
            self._settings_devices[device.name] = {
                'device': device,
                'row': devrow,
                'channels': {}
            }
            for chname, ch in reversed(
                    sorted(device.channels.items(),
                           key=lambda x: x[1].display_order)):
                chrow = QTreeWidgetItem(devrow)
                chrow.setText(0, ch.label)
                chrow.setText(1, 'Channel')
                self._settings_devices[device.name]['channels'][ch.name] = {
                    'channel': ch,
                    'row': chrow
                }
            newchrow = QTreeWidgetItem(devrow)
            newchrow.setText(0, '[Add a new Channel]')
            #newchrow.setText(1, 'Channel')

        newdevrow = QTreeWidgetItem(self.ui.treeDevices)
        newdevrow.setText(0, '[Add a new Device]')
        #newdevrow.setText(1, 'Device')

        self.ui.treeDevices.expandAll()

    def update_procedures(self, procedures):
        # Add procedures to the procedures tab
        self.clearLayout(self.ui.vboxProcedures)
        for procedure_name, procedure in procedures.items():
            self.ui.vboxProcedures.addWidget(procedure.widget)
        self.ui.vboxProcedures.addStretch()

    # ---- Settings page functions ----
    def on_settings_row_changed(self, item):
        if item == None:
            return

        # if clause fixes mysterious floating entry forms....
        if self._devvbox.count():
            widget = self._devvbox.itemAt(0).widget()
            widget.hide()

        self.clearLayout(self._devvbox)

        obj = None
        parent = None

        for device_name, device_data in self._settings_devices.items():
            # are we editing an existing device/channel?
            if device_data['row'] == item:
                obj = device_data['device']
                break
            else:
                for channel_name, channel_data in device_data[
                        'channels'].items():
                    if channel_data['row'] == item:
                        obj = channel_data['channel']
                        parent = device_data['device']
                        break

        if (obj, parent) == (None, None):
            # adding a new device or channel
            if 'channel' in item.text(0).lower():
                for device_name, device_data in self._settings_devices.items():
                    if device_data['row'] == item.parent():
                        parent = device_data['device']
                        break

        if obj is None:
            if parent is None:
                obj = Device(label='New Device')
            else:
                obj = Channel(label='New Channel')
                obj.parent_device = parent

        obj.reset_entry_form()
        self._devvbox.addWidget(obj.entry_form)
        obj.entry_form.show()
        self._sig_entry_form_changed.emit(obj)

    # ---- Misc functions ---
    def show_AboutDialog(self):
        _aboutdialog = AboutDialog()
        _aboutdialog.exec_()

    @staticmethod
    def show_readme():
        readme_url = "https://github.com/DanielWinklehner/pycontrolsystem"
        wb.open(readme_url, new=2)

    def show_ErrorDialog(self, error_message='Error'):
        _errordialog = ErrorDialog(error_message)
        _errordialog.exec_()

    def set_polling_rate(self, text):
        self.ui.lblServPoll.setText('Server polling rate: ' + text + ' Hz')

    def status_message(self, text):
        self._statusbar.showMessage(text)
        self._messagelog.append(
            time.strftime('[%Y-%m-%d %H:%M:%S] ', time.localtime()) + text)

    @staticmethod
    def clearLayout(layout):
        """ Removes all widgets from a QLayout. Does not delete the widget """
        while layout.count():
            child = layout.takeAt(0)
            if child.widget():
                child.widget().setParent(None)
            elif child.layout():
                self.clearLayout(child)
示例#30
0
    def __init__(self, HomeTab):
        super().__init__()

        parent_horizontal_layout = QHBoxLayout(HomeTab)

        menu_vlayout = QVBoxLayout()
        frames_parent_vlayout = QVBoxLayout()

        frames_container = QFrame()
        frames_container.setLayout(frames_parent_vlayout)

        # Create scrollArea widget inside centralwidget
        self.scrollArea = QtWidgets.QScrollArea(frames_container)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.scrollArea.sizePolicy().hasHeightForWidth())

        # Set size policy for scrollArea widget
        self.scrollArea.setSizePolicy(sizePolicy)
        self.scrollArea.setMinimumSize(QtCore.QSize(767, 620))
        self.scrollArea.setMaximumSize(QtCore.QSize(767, 620))
        self.scrollArea.setHorizontalScrollBarPolicy(
            QtCore.Qt.ScrollBarAlwaysOff)
        self.scrollArea.setWidgetResizable(False)
        self.scrollArea.setObjectName("scrollArea")

        # Create scrollAreaWidgetContents widget
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(
            QtCore.QRect(0, -176, 771, 900))

        # Create size policy for scrollAreaWidgetContents
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed,
                                           QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.scrollAreaWidgetContents.sizePolicy().hasHeightForWidth())

        # Set scrollAreaWidgetContents size policy
        self.scrollAreaWidgetContents.setSizePolicy(sizePolicy)
        self.scrollAreaWidgetContents.setMinimumSize(QtCore.QSize(771, 900))
        self.scrollAreaWidgetContents.setMaximumSize(QtCore.QSize(771, 900))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")

        menu_frame = QFrame()
        menu_frame.setFixedWidth(130)

        self.home_btn = QPushButton("Home")
        self.home_btn.setStyleSheet(
            "background: #14ffec; font-family: Verdana, Geneva, sans-serif; font-weight: bold;"
        )
        self.home_btn.clicked.connect(self.showHome)

        self.register_btn = QPushButton("Registration")
        self.register_btn.setStyleSheet(
            "QPushButton { background: #14ffec; font-family: Verdana, Geneva, sans-serif; font-weight: bold; }"
            "QPushButton:pressed { background: #f6c90e; }")

        self.member_profiles_btn = QPushButton("Member Profiles")
        self.member_profiles_btn.setStyleSheet(
            "background: #14ffec; font-family: Verdana, Geneva, sans-serif; font-weight: bold;"
        )
        #self.member_profiles_btn.clicked.connect(self.showMemberProfiles)

        self.units_btn = QPushButton("Units")
        self.units_btn.setStyleSheet(
            "background: #14ffec; font-family: Verdana, Geneva, sans-serif; font-weight: bold;"
        )
        self.units_btn.clicked.connect(self.showUnits)

        self.files_btn = QPushButton("Files")
        self.files_btn.setStyleSheet(
            "background: #14ffec; font-family: Verdana, Geneva, sans-serif; font-weight: bold;"
        )
        self.files_btn.clicked.connect(self.showFiles)

        self.accounts_btn = QPushButton("Accounts")
        self.accounts_btn.setStyleSheet(
            "background: #14ffec; font-family: Verdana, Geneva, sans-serif; font-weight: bold;"
        )
        self.accounts_btn.clicked.connect(self.showAccounts)

        self.share_capital_btn = QPushButton("Share Capital")
        self.share_capital_btn.setStyleSheet(
            "background: #14ffec; font-family: Verdana, Geneva, sans-serif; font-weight: bold;"
        )
        self.share_capital_btn.clicked.connect(self.showShareCapital)

        menu_vlayout.addWidget(self.home_btn)
        menu_vlayout.addWidget(self.units_btn)
        menu_vlayout.addWidget(self.files_btn)
        menu_vlayout.addWidget(self.accounts_btn)
        menu_vlayout.addWidget(self.share_capital_btn)
        menu_vlayout.addWidget(self.register_btn)
        menu_vlayout.addWidget(self.member_profiles_btn)

        v_layout1 = QVBoxLayout()
        toolbar_Hlayout = QHBoxLayout()
        toolbar_Hlayout.setSpacing(0)
        toolbar_Hlayout.addStretch()
        toolbar_Hlayout.setContentsMargins(2, 1, 2, 1)
        sortby_label = QLabel("Sort by:")
        sortby_label.setStyleSheet(
            "background: #e8e4e1; color: #1b1b2f; border: none; font-family: Verdana, Geneva, sans-serif; padding-left: 0.5px;"
        )

        self.comboBox = QComboBox()
        self.comboBox.setStyleSheet(
            "background: white; border: none; font-family: Verdana, Geneva, sans-serif;"
        )
        self.comboBox.addItem("Date")
        self.comboBox.addItem("Name")
        self.comboBox.setFixedWidth(57)

        search_label = QLabel("Search:")
        search_label.setStyleSheet(
            "background: #e8e4e1; color: #1b1b2f; border: none; font-family: Verdana, Geneva, sans-serif; padding-left: 0.5px;"
        )
        self.search_input = QLineEdit()
        self.search_input.setStyleSheet(
            "background: white; border: none; font-family: Verdana, Geneva, sans-serif;"
        )
        self.search_input.setFixedWidth(150)
        toolbar_Hlayout.addWidget(search_label)
        toolbar_Hlayout.addWidget(self.search_input)
        toolbar_Hlayout.insertWidget(0, self.comboBox)
        toolbar_Hlayout.insertWidget(0, sortby_label)

        self.listwidget = QListWidget()
        self.listwidget.setSpacing(1)
        #self.listwidget.setStyleSheet("QListWidget {font-family: Verdana, Geneva, sans-serif;}" "QListWidget.item:hover {color: white; background: blue;}")
        self.listwidget.setFixedHeight(600)

        label_Hlayout = QHBoxLayout()
        label_Hlayout.setSpacing(1)
        label_Hlayout.setContentsMargins(2, 1, 2, 0)
        no_label, name_label, address_label, date_label = QLabel('No'), QLabel(
            'Name'), QLabel('Address'), QLabel('Date')
        no_label.setStyleSheet(
            "background: white; color: blue; font-family: Verdana, Geneva, sans-serif; font-weight: bold; padding-top: 3px; padding-bottom: 3px; border-left: none; border-right: none; border-top: none;"
        )
        name_label.setStyleSheet(
            "background: white; color: blue; font-family: Verdana, Geneva, sans-serif; font-weight: bold; padding-left: 65px; padding-right: 60px; padding-top: 3px; padding-bottom: 3px; border-left: none; border-right: none; border-top: none;"
        )
        address_label.setStyleSheet(
            "background: white; color: blue; font-family: Verdana, Geneva, sans-serif; font-weight: bold; padding-left: 145px; padding-right: 150px; padding-top: 3px; padding-bottom: 3px; border-left: none; border-right: none; border-top: none;"
        )
        date_label.setStyleSheet(
            "background: white; color: blue; font-family: Verdana, Geneva, sans-serif; font-weight: bold; border-left: none; padding-top: 3px; padding-bottom: 3px; padding-right: 15px;  padding-left: 11px; border-right: none; border-top: none;"
        )
        label_Hlayout.addWidget(no_label, 0)
        label_Hlayout.addWidget(name_label, 1)
        label_Hlayout.addWidget(address_label, 2)
        label_Hlayout.addWidget(date_label, 0)
        v_layout1.addLayout(toolbar_Hlayout)
        v_layout1.addLayout(label_Hlayout)
        v_layout1.addWidget(self.listwidget)
        v_layout1.setContentsMargins(0, 0, 0, 0)
        v_layout1.setSpacing(0)

        v_layout2 = QVBoxLayout()
        units_label = QLabel("Units")
        units_label.setStyleSheet("border: 1px solid red;")
        v_layout2.addWidget(units_label)

        v_layout3 = QVBoxLayout()
        files_label = QLabel("Files")
        files_label.setStyleSheet("border: 1px solid red;")
        v_layout3.addWidget(files_label)

        v_layout4 = QVBoxLayout()
        accounts_label = QLabel("Accounts")
        accounts_label.setStyleSheet("border: 1px solid red;")
        v_layout4.addWidget(accounts_label)

        v_layout5 = QVBoxLayout()
        share_capitals_label = QLabel("Share Capitals")
        share_capitals_label.setStyleSheet("border: 1px solid red;")
        v_layout5.addWidget(share_capitals_label)

        self.frame1 = QFrame()
        self.frame1.setLayout(v_layout1)
        self.frame2 = QFrame()
        self.frame2.setStyleSheet("border: 1px solid blue;")
        self.frame2.setLayout(v_layout2)
        self.frame3 = QFrame()
        self.frame3.setStyleSheet("border: 1px solid blue;")
        self.frame3.setLayout(v_layout3)
        self.frame4 = QFrame()
        self.frame4.setStyleSheet("border: 1px solid blue;")
        self.frame4.setLayout(v_layout4)
        self.frame5 = QFrame()
        self.frame5.setStyleSheet("border: 1px solid blue;")
        self.frame5.setLayout(v_layout5)

        frames_parent_vlayout.addWidget(self.frame1)
        frames_parent_vlayout.addWidget(self.frame2)
        frames_parent_vlayout.addWidget(self.frame3)
        frames_parent_vlayout.addWidget(self.frame4)
        frames_parent_vlayout.addWidget(self.frame5)

        self.showHome()

        menu_frame.setLayout(menu_vlayout)
        menu_frame.setStyleSheet("background: #252525;")
        menu_vlayout.setContentsMargins(5, 5, 5, 0)
        parent_horizontal_layout.addWidget(menu_frame)
        parent_horizontal_layout.addWidget(frames_container)
        frames_parent_vlayout.addStretch()
        frames_parent_vlayout.setContentsMargins(0, 0, 0, 0)
        frames_container.setContentsMargins(0, 0, 0, 0)
        frames_container.setStyleSheet("background: #978d58;")
        menu_vlayout.addStretch()
        parent_horizontal_layout.setContentsMargins(0, 0, 0, 0)
        parent_horizontal_layout.setSpacing(0)
        #parent_horizontal_layout.addStretch()

        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
示例#31
0
class Invite(QWidget):
    """
    An invite is a small notification being displayed in the bottom right
    corner of the window. It fades in and out, and is used to invite an user
    to jump to a certain location. Some other uses might be added later.
    """
    def __init__(self, plugin, parent=None):
        super(Invite, self).__init__(parent)
        self._plugin = plugin
        self._time = 0

        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool
                            | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_ShowWithoutActivating)
        self.setAttribute(Qt.WA_TranslucentBackground)

        self._icon = QLabel()
        self._icon.setAutoFillBackground(False)
        self._icon.setAttribute(Qt.WA_TranslucentBackground)

        self._text = QLabel()
        self._text.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self._layout = QHBoxLayout()
        self._layout.addWidget(self._text)
        self.setLayout(self._layout)

        # Fade in and out animation
        self._popup_opacity = 0.0
        self._animation = QPropertyAnimation()
        self._animation.setTargetObject(self)
        self._animation.setPropertyName("popup_opacity")
        self._animation.finished.connect(self.hide)

        # Timer used to auto-close the window
        self._timer = QTimer()
        self._timer.timeout.connect(self.hide_animation)
        self._callback = None
        self._triggered = False

    @property
    def time(self):
        return self._time

    @time.setter
    def time(self, time):
        self._time = time

    @property
    def text(self):
        return self._text.text()

    @text.setter
    def text(self, text):
        self._text.setText(text)
        self.adjustSize()

    @property
    def icon(self):
        return self._icon.pixmap()

    @icon.setter
    def icon(self, pixmap):
        # Resize the given pixmap
        pixmap_height = self._text.sizeHint().height()
        self._icon.setPixmap(
            pixmap.scaled(
                pixmap_height,
                pixmap_height,
                Qt.KeepAspectRatio,
                Qt.SmoothTransformation,
            ))
        self._layout.insertWidget(0, self._icon)

    @property
    def callback(self):
        return self._callback

    @callback.setter
    def callback(self, callback):
        self._callback = callback

    @property
    def triggered(self):
        return self._triggered

    @triggered.setter
    def triggered(self, triggered):
        self._triggered = triggered

    def paintEvent(self, event):  # noqa: N802
        """We override the painting event to draw the invite ourselves."""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        rect = QRect(self.rect())

        # Draw the border
        painter.setBrush(QBrush(QColor(122, 122, 122)))
        painter.setPen(Qt.NoPen)
        painter.drawRect(rect)

        rect.setX(rect.x() + 1)
        rect.setY(rect.y() + 1)
        rect.setWidth(rect.width() - 1)
        rect.setHeight(rect.height() - 1)

        # Draw the background
        painter.setBrush(QBrush(QColor(255, 255, 225)))
        painter.setPen(Qt.NoPen)
        painter.drawRect(rect)

    def mouseReleaseEvent(self, event):  # noqa: N802
        """
        This function is called when the user clicks the invite. It triggers
        the callback function is it has been specified, and hides the invite.
        """
        if self._callback:
            self._callback()
        self._triggered = True
        self._popup_opacity = 0.0
        self.hide()

    def show(self):
        """Shows the invite to user. It triggers a fade in effect."""
        self._plugin.logger.debug("Showing invite %s" % self.text)
        self.setWindowOpacity(0.0)

        self._animation.setDuration(500)
        self._animation.setStartValue(0.0)
        self._animation.setEndValue(1.0)

        # Map the notification to the bottom right corner
        pos = QPoint(self.parent().width() - 25, self.parent().height() - 50)
        pos = self.parent().mapToGlobal(pos)

        self.setGeometry(
            pos.x() - self.width(),
            pos.y() - self.height(),
            self.width(),
            self.height(),
        )
        super(Invite, self).show()

        self._animation.start()
        self._timer.start(3500)

    def hide(self):
        """Hides the invite only if it is fully transparent."""
        if self._popup_opacity == 0.0:
            self._plugin.interface.widget.refresh()
            super(Invite, self).hide()

    def hide_animation(self):
        """Hides the invite. It triggers the fade out animation."""
        self._timer.stop()
        self._animation.setDuration(500)
        self._animation.setStartValue(1.0)
        self._animation.setEndValue(0.0)
        self._animation.start()

    @pyqtProperty(float)
    def popup_opacity(self):
        return self._popup_opacity

    @popup_opacity.setter
    def popup_opacity(self, opacity):
        self._popup_opacity = opacity
        self.setWindowOpacity(opacity)
示例#32
0
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()
示例#33
0
class E5LineEdit(QLineEdit):
    """
    Class implementing a line edit widget showing some inactive text.
    """
    LeftSide = 0
    RightSide = 1
    
    def __init__(self, parent=None, inactiveText=""):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @param inactiveText text to be shown on inactivity (string)
        """
        super(E5LineEdit, self).__init__(parent)
        
        self.setMinimumHeight(22)
        
        if qVersion() < "4.7.0":
            self.__inactiveText = inactiveText
        else:
            self.setPlaceholderText(inactiveText)
        
        self.__mainLayout = QHBoxLayout(self)
        self.__mainLayout.setContentsMargins(0, 0, 0, 0)
        self.__mainLayout.setSpacing(0)
        
        self.__leftMargin = 0
        self.__leftWidget = E5LineEditSideWidget(self)
        self.__leftWidget.resize(0, 0)
        self.__leftLayout = QHBoxLayout(self.__leftWidget)
        self.__leftLayout.setContentsMargins(0, 0, 2, 0)
        if QApplication.isRightToLeft():
            self.__leftLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__leftLayout.setDirection(QBoxLayout.LeftToRight)
        self.__leftLayout.setSizeConstraint(QLayout.SetFixedSize)
        
        self.__rightWidget = E5LineEditSideWidget(self)
        self.__rightWidget.resize(0, 0)
        self.__rightLayout = QHBoxLayout(self.__rightWidget)
        self.__rightLayout.setContentsMargins(0, 0, 2, 0)
        if self.isRightToLeft():
            self.__rightLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__rightLayout.setDirection(QBoxLayout.LeftToRight)
        
        horizontalSpacer = QSpacerItem(
            0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.__mainLayout.addWidget(
            self.__leftWidget, 0, Qt.AlignVCenter | Qt.AlignLeft)
        self.__mainLayout.addItem(horizontalSpacer)
        self.__mainLayout.addWidget(
            self.__rightWidget, 0, Qt.AlignVCenter | Qt.AlignRight)
        if self.isRightToLeft():
            self.__mainLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__mainLayout.setDirection(QBoxLayout.LeftToRight)
        
        self.setWidgetSpacing(3)
        self.__leftWidget.sizeHintChanged.connect(self._updateTextMargins)
        self.__rightWidget.sizeHintChanged.connect(self._updateTextMargins)
    
    def setLeftMargin(self, margin):
        """
        Public method to set the left margin.
        
        @param margin left margin in pixel (integer)
        """
        self.__leftMargin = margin
    
    def leftMargin(self):
        """
        Public method to get the size of the left margin.
        
        @return left margin in pixel (integer)
        """
        return self.__leftMargin
    
    def event(self, evt):
        """
        Public method to handle events.
        
        @param evt reference to the event (QEvent)
        @return flag indicating, whether the event was recognized (boolean)
        """
        if evt.type() == QEvent.LayoutDirectionChange:
            if self.isRightToLeft():
                self.__mainLayout.setDirection(QBoxLayout.RightToLeft)
                self.__leftLayout.setDirection(QBoxLayout.RightToLeft)
                self.__rightLayout.setDirection(QBoxLayout.RightToLeft)
            else:
                self.__mainLayout.setDirection(QBoxLayout.LeftToRight)
                self.__leftLayout.setDirection(QBoxLayout.LeftToRight)
                self.__rightLayout.setDirection(QBoxLayout.LeftToRight)
        return QLineEdit.event(self, evt)
    
    def paintEvent(self, evt):
        """
        Protected method handling a paint event.
        
        @param evt reference to the paint event (QPaintEvent)
        """
        super(E5LineEdit, self).paintEvent(evt)
        
        if qVersion() < "4.7.0":
            if not self.text() and \
               self.__inactiveText and \
               not self.hasFocus():
                panel = QStyleOptionFrame()
                self.initStyleOption(panel)
                textRect = self.style().subElementRect(
                    QStyle.SE_LineEditContents, panel, self)
                textRect.adjust(2, 0, 0, 0)
                left = self.textMargin(self.LeftSide)
                right = self.textMargin(self.RightSide)
                textRect.adjust(left, 0, -right, 0)
                painter = QPainter(self)
                painter.setPen(self.palette().brush(
                    QPalette.Disabled, QPalette.Text).color())
                painter.drawText(
                    textRect, Qt.AlignLeft | Qt.AlignVCenter,
                    self.__inactiveText)
    
    def _updateTextMargins(self):
        """
        Protected slot to update the text margins.
        """
        if self.__leftMargin == 0:
            left = self.__leftWidget.sizeHint().width()
        else:
            left = self.__leftMargin
        right = self.__rightWidget.sizeHint().width()
        top = 0
        bottom = 0
        self.setTextMargins(left, top, right, bottom)
    
    def addWidget(self, widget, position):
        """
        Public method to add a widget to a side.
        
        @param widget reference to the widget to add (QWidget)
        @param position position to add to (E5LineEdit.LeftSide,
            E5LineEdit.RightSide)
        """
        if widget is None:
            return
        
        if self.isRightToLeft():
            if position == self.LeftSide:
                position = self.RightSide
            else:
                position = self.LeftSide
        if position == self.LeftSide:
            self.__leftLayout.addWidget(widget)
        else:
            self.__rightLayout.insertWidget(1, widget)
    
    def removeWidget(self, widget):
        """
        Public method to remove a widget from a side.
        
        @param widget reference to the widget to remove (QWidget)
        """
        if widget is None:
            return
        
        self.__leftLayout.removeWidget(widget)
        self.__rightLayout.removeWidget(widget)
        widget.hide()
    
    def widgetSpacing(self):
        """
        Public method to get the side widget spacing.
        
        @return side widget spacing (integer)
        """
        return self.__leftLayout.spacing()
    
    def setWidgetSpacing(self, spacing):
        """
        Public method to set the side widget spacing.
        
        @param spacing side widget spacing (integer)
        """
        self.__leftLayout.setSpacing(spacing)
        self.__rightLayout.setSpacing(spacing)
        self._updateTextMargins()
    
    def textMargin(self, position):
        """
        Public method to get the text margin for a side.
        
        @param position side to get margin for (E5LineEdit.LeftSide,
            E5LineEdit.RightSide)
        @return text margin (integer)
        """
        spacing = self.__rightLayout.spacing()
        w = 0
        if position == self.LeftSide:
            w = self.__leftWidget.sizeHint().width()
        else:
            w = self.__rightWidget.sizeHint().width()
        if w == 0:
            return 0
        return w + spacing * 2
    
    def inactiveText(self):
        """
        Public method to get the inactive text.
        
        @return inactive text (string)
        """
        if qVersion() < "4.7.0":
            return self.__inactiveText
        else:
            return self.placeholderText()
    
    def setInactiveText(self, inactiveText):
        """
        Public method to set the inactive text.
        
        @param inactiveText text to be shown on inactivity (string)
        """
        if qVersion() < "4.7.0":
            self.__inactiveText = inactiveText
            self.update()
        else:
            self.setPlaceholderText(inactiveText)
示例#34
0
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")