Exemplo n.º 1
0
class IPFSHashExplorerStack(GalacteekTab):
    """
    Organizes IPFSHashExplorerWidgets with a QStackedWidget
    """
    def __init__(self, gWindow, hashRef, maxItems=16, parent=None):
        super(IPFSHashExplorerStack, self).__init__(gWindow)

        self.rootHash = hashRef
        self.maxItems = maxItems

        self.stack = QStackedWidget(self)
        self.exLayout = QVBoxLayout()
        self.exLayout.addWidget(self.stack)

        self.vLayout.addLayout(self.exLayout)
        if self.rootHash:
            self.viewHash(self.rootHash)

    def tabDestroyedPost(self):
        self.stack.setParent(None)
        self.stack.deleteLater()

    @property
    def itemsCount(self):
        return self.stack.count()

    def viewHash(self,
                 hashRef,
                 addClose=False,
                 autoOpenFolders=False,
                 parentView=None):
        view = IPFSHashExplorerWidget(hashRef,
                                      parent=self,
                                      addClose=addClose,
                                      showCidLabel=True,
                                      autoOpenFolders=autoOpenFolders,
                                      parentView=parentView)
        view.closeRequest.connect(partialEnsure(self.remove, view))
        view.directoryOpenRequest.connect(lambda nView, cid: self.viewHash(
            cid, addClose=True, parentView=nView))

        self.stack.insertWidget(self.stack.count(), view)
        self.stack.setCurrentWidget(view)
        view.reFocus()
        return True

    async def remove(self, view):
        try:
            view.cancelTasks()
            self.stack.removeWidget(view)
        except:
            pass

    async def onClose(self):
        for idx in range(self.stack.count()):
            widget = self.stack.widget(idx)
            await widget.cleanup()

        return True
Exemplo n.º 2
0
class PyMultiPageWidget(QWidget):
    def __init__(self, parent=None):
        super(PyMultiPageWidget, self).__init__(parent)
        self.value = 0

        self.bt = QPushButton('switch', self)
        self.bt.clicked.connect(self.switch)

        self.stackWidget = QStackedWidget()
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.bt)
        self.layout.addWidget(self.stackWidget)
        self.setLayout(self.layout)
        self.resize(500, 400)

    def switch(self):
        self.value = 1 - self.value
        bt = QPushButton('new')
        if self.value:
            self.addPage(bt)
        else:
            self.removePage(0)

    @pyqtSlot(QWidget)
    def addPage(self, page):
        index = self.stackWidget.count()
        page.setParent(self.stackWidget)
        self.stackWidget.insertWidget(index, page)

    @pyqtSlot(int)
    def removePage(self, index):
        widget = self.stackWidget.widget(index)
        self.stackWidget.removeWidget(widget)
Exemplo n.º 3
0
class DynaFrame(QFrame):
    """ The DynaFrame class wraps one custom widget which can be replaced with another during runtime. """

    def __init__(self, parent, widget: QWidget=None):
        super(__class__, self).__init__(parent)
        layout = QHBoxLayout()
        self._stk_widget = QStackedWidget()
        if widget is not None:
            self.setWidget(widget)
        layout.addWidget(self._stk_widget)
        self.setLayout(layout)

    def setWidget(self, widget: QWidget):
        """ Replaces the currently present widget (if any) with the specified one. """
        self._clear_stack_widget()
        self._stk_widget.addWidget(widget)

    def getWidget(self) -> QWidget:
        """ Returns the widget wrapped in the frame. """
        return self._stk_widget.widget(0)

    def _clear_stack_widget(self):
        """ Clears all widgets which were added to the stack. """
        count = self._stk_widget.count()
        assert 0 <= count <= 1
        for index in range(0, count):
            widget = self._stk_widget.widget(index)
            self._stk_widget.removeWidget(widget)
Exemplo n.º 4
0
class IPFSHashExplorerStack(GalacteekTab):
    """
    Organizes IPFSHashExplorerWidgets with a QStackedWidget
    """
    def __init__(self, gWindow, hashRef, maxItems=16, parent=None):
        super(IPFSHashExplorerStack, self).__init__(gWindow)

        self.rootHash = hashRef
        self.maxItems = maxItems

        self.stack = QStackedWidget(self)
        self.exLayout = QVBoxLayout()
        self.exLayout.addWidget(self.stack)

        self.vLayout.addLayout(self.exLayout)
        if self.rootHash:
            self.viewHash(self.rootHash)

    @property
    def itemsCount(self):
        return self.stack.count()

    def viewHash(self, hashRef, addClose=False, autoOpenFolders=False):
        view = IPFSHashExplorerWidget(hashRef,
                                      parent=self,
                                      addClose=addClose,
                                      showCidLabel=True,
                                      autoOpenFolders=autoOpenFolders)
        view.closeRequest.connect(functools.partial(self.remove, view))
        view.directoryOpenRequest.connect(
            lambda cid: self.viewHash(cid, addClose=True))

        self.stack.insertWidget(self.stack.count(), view)
        self.stack.setCurrentWidget(view)
        view.reFocus()
        return True

    def remove(self, view):
        try:
            self.stack.removeWidget(view)
        except:
            pass
Exemplo n.º 5
0
class StackedPage(QWidget):
    def __init__(self, parent=None):
        super(StackedPage, self).__init__(parent)
        layout = QHBoxLayout(self)
        leftpane = QVBoxLayout()
        self.buttongroup = QButtonGroup(self)
        self.buttongroup.setExclusive(True)
        self.groupbox = QGroupBox(self)
        self.groupbox.setMinimumWidth(200)
        QVBoxLayout(self.groupbox)
        leftpane.addWidget(self.groupbox)
        leftpane.addStretch(1)
        layout.addLayout(leftpane)
        self.rightpane = QStackedWidget(self)
        layout.addWidget(self.rightpane)
        self.buttongroup.buttonClicked[int].connect(
            self.rightpane.setCurrentIndex)
        self.rightpane.currentChanged[int].connect(self.activate)

    def addPage(self, buttontext, widget):
        button = QPushButton(buttontext)
        button.setCheckable(True)
        button.setChecked(self.rightpane.count() == 0)
        self.buttongroup.addButton(button, self.rightpane.count())
        self.groupbox.layout().addWidget(button)
        self.rightpane.addWidget(widget)

    def pages(self):
        return [
            self.rightpane.widget(i) for i in range(self.rightpane.count())
        ]

    def activate(self, ix):
        page = self.rightpane.currentWidget()
        if hasattr(page, "activate") and callable(page.activate):
            page.activate()

    def showEvent(self, *args, **kwargs):
        self.activate(0)
        return QWidget.showEvent(self, *args, **kwargs)
Exemplo n.º 6
0
class MainView(QMainWindow):
    def __init__(self):
        super(MainView, self).__init__()
        self.setWindowTitle('Jira Helper')

        self.window = QStackedWidget()  # Create the main widget for the page

        main_window_widget = QWidget()
        main_window_layout = QGridLayout()
        main_window_widget.setLayout(main_window_layout)
        menu_widget = QWidget()
        menu_layout = QHBoxLayout()
        menu_widget.setLayout(menu_layout)
        main_window_layout.addWidget(menu_widget, 0, 0)
        main_window_layout.addWidget(self.window, 1, 0)
        self.setCentralWidget(main_window_widget)

        # Create a settings button
        self.settings_submit_button = QPushButton()
        self.settings_submit_button.setText("Settings")
        menu_layout.addWidget(self.settings_submit_button)

        # Create date time
        datetime_font = QFont("Times", 30)
        self.date = QLabel()
        self.date.setFont(datetime_font)
        self.time = QLabel()
        self.time.setFont(datetime_font)
        menu_layout.addWidget(self.date)
        menu_layout.addWidget(self.time)

        # Create a clean queue button
        self.clean_queue_button = QPushButton()
        self.clean_queue_button.setText("Clean Queue")
        self.clean_queue_button.setCheckable(True)
        menu_layout.addWidget(self.clean_queue_button)

    def update_datetime(self):
        '''Connected to a Qtimer to periodically update the date and time'''
        Qdate = QDate.currentDate()
        Qtime = QTime.currentTime()
        self.date.setText(Qdate.toString(Qt.DefaultLocaleLongDate))
        self.time.setText(Qtime.toString(Qt.DefaultLocaleLongDate))

    def transition_page(self):
        '''Connected to a Qtimer to periodically transition through the widgets stacked on self.window'''
        index_Id = self.window.currentIndex()
        if index_Id < self.window.count() - 1:
            self.window.setCurrentIndex(index_Id + 1)
        else:
            self.window.setCurrentIndex(0)
Exemplo n.º 7
0
class Timer(Base):

    def __init__(self, observer: Observer, name: str, foreground_color="#ffffff", font_name=""):
        super().__init__(name, foreground_color, font_name)

        self.main_layout = QGridLayout()
        self.central_widget = QStackedWidget()
        self.central_widget.insertWidget(0, TimerOverview(observer, "TimerOverview", self, foreground_color))
        self.central_widget.insertWidget(1, AddTimer(observer, "AddTimer", self, foreground_color))
        self.central_widget.setCurrentIndex(0)

        self.main_layout.addWidget(self.central_widget)
        self.setLayout(self.main_layout)

    def pass_timer(self, timer_config: dict):
        self.central_widget.widget(0).add_timer(timer_config)

    def switch_central_widget(self):
        max_value = self.central_widget.count()
        index = self.central_widget.currentIndex()
        self.central_widget.setCurrentIndex((index + 1) % max_value)
Exemplo n.º 8
0
class MainWindow(QMainWindow):
    def __init__(self, app, dtos):
        super(MainWindow, self).__init__()
        self.app = app # pass app just in case need to do some events
        self.twitter_ids = list(dtos.keys())
        dbgp("twitter_ids:{} => {}".format(dtos.keys(), self.twitter_ids))
        self.stack_widget = QStackedWidget()
        for twitter_id in dtos:
            profile_elem, activities = dtos[twitter_id]
            self.stack_widget.addWidget(MainWidget(twitter_id, activities))
        self.stack_widget.keyPressEvent = self.changePage
        self.init_ui()

    def init_ui(self):
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.stack_widget)
        self.setCentralWidget(self.stack_widget)
        self.setWindowTitle(window_title)

    def changePage(self, event):
        if event.key() == ord(next_page_key):
            next_index = (self.stack_widget.currentIndex() + 1) % self.stack_widget.count()
            dbgp("Changing to show the content of twitter_id :{}:".format(self.twitter_ids[next_index]))
            self.stack_widget.setCurrentIndex((next_index))
Exemplo n.º 9
0
class TableWidget(QWidget):

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

        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)

        self.relations = {}

        # Stack
        self.stacked = QStackedWidget()
        vbox.addWidget(self.stacked)

    def count(self):
        return self.stacked.count()

    def remove_table(self, index):
        widget = self.stacked.widget(index)
        self.stacked.removeWidget(widget)
        del widget

    def current_table(self):
        return self.stacked.currentWidget()

    def remove_relation(self, name):
        del self.relations[name]

    def add_relation(self, name, rela):
        if self.relations.get(name, None) is None:
            self.relations[name] = rela
            return True
        return False

    def update_table(self, data):
        current_table = self.current_table()
        model = current_table.model()
        # Clear content
        model.clear()
        # Add new header and content
        model.setHorizontalHeaderLabels(data.header)

        for row_count, row in enumerate(data.content):
            for col_count, data in enumerate(row):
                item = QStandardItem(data)
                item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                # item.setSelectable(False)
                model.setItem(row_count, col_count, item)

    def add_table(self, rela, name):
        """ Add new table from New Relation Dialog """

        # Create table
        table = self.create_table(rela)
        self.add_relation(name, rela)
        self.stacked.addWidget(table)

    def create_table(self, rela):
        table = custom_table.Table()
        model = QStandardItemModel()
        table.setModel(model)
        model.setHorizontalHeaderLabels(rela.header)

        for row_count, row in enumerate(rela.content):
            for col_count, data in enumerate(row):
                item = QStandardItem(data)
                item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                model.setItem(row_count, col_count, item)

        return table
Exemplo n.º 10
0
class TabBarWindow(TabWindow):
    """Implementation which uses a separate QTabBar and QStackedWidget.
    The Tab bar is placed next to the menu bar to save real estate."""
    def __init__(self, app, **kwargs):
        super().__init__(app, **kwargs)

    def _setupUi(self):
        self.setWindowTitle(self.app.NAME)
        self.resize(640, 480)
        self.tabBar = QTabBar()
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self._setupActions()
        self._setupMenu()

        self.centralWidget = QWidget(self)
        self.setCentralWidget(self.centralWidget)
        self.stackedWidget = QStackedWidget()
        self.centralWidget.setLayout(self.verticalLayout)
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.addWidget(self.menubar, 0, Qt.AlignTop)
        self.horizontalLayout.addWidget(self.tabBar, 0, Qt.AlignTop)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.verticalLayout.addWidget(self.stackedWidget)

        self.tabBar.currentChanged.connect(self.showWidget)
        self.tabBar.tabCloseRequested.connect(self.onTabCloseRequested)

        self.stackedWidget.currentChanged.connect(self.updateMenuBar)
        self.stackedWidget.widgetRemoved.connect(self.onRemovedWidget)

        self.tabBar.setTabsClosable(True)
        self.restoreGeometry()

    def addTab(self, page, title, switch=True):
        stack_index = self.stackedWidget.insertWidget(-1, page)
        tab_index = self.tabBar.addTab(title)

        if isinstance(page, DirectoriesDialog):
            self.tabBar.setTabButton(tab_index, QTabBar.RightSide, None)
        if switch:  # switch to the added tab immediately upon creation
            self.setTabIndex(tab_index)
            self.stackedWidget.setCurrentWidget(page)
        return stack_index

    @pyqtSlot(int)
    def showWidget(self, index):
        if index >= 0 and index <= self.stackedWidget.count() - 1:
            self.stackedWidget.setCurrentIndex(index)
            # if not self.tabBar.isTabVisible(index):
            self.setTabVisible(index, True)

    def indexOfWidget(self, widget):
        # Warning: this may return -1 if widget is not a child of stackedwidget
        return self.stackedWidget.indexOf(widget)

    def setCurrentIndex(self, tab_index):
        # The signal will handle switching the stackwidget's widget
        self.setTabIndex(tab_index)
        # self.stackedWidget.setCurrentWidget(self.stackedWidget.widget(tab_index))

    @pyqtSlot(int)
    def setTabIndex(self, index):
        if index is None:
            return
        self.tabBar.setCurrentIndex(index)

    def setTabVisible(self, index, value):
        return self.tabBar.setTabVisible(index, value)

    @pyqtSlot(int)
    def onRemovedWidget(self, index):
        self.removeTab(index)

    @pyqtSlot(int)
    def removeTab(self, index):
        # No need to remove the widget here:
        # self.stackedWidget.removeWidget(self.stackedWidget.widget(index))
        return self.tabBar.removeTab(index)

    @pyqtSlot(int)
    def removeWidget(self, widget):
        return self.stackedWidget.removeWidget(widget)

    def isTabVisible(self, index):
        return self.tabBar.isTabVisible(index)

    def getCurrentIndex(self):
        return self.stackedWidget.currentIndex()

    def getWidgetAtIndex(self, index):
        return self.stackedWidget.widget(index)

    def getCount(self):
        return self.stackedWidget.count()

    @pyqtSlot()
    def toggleTabBar(self):
        value = self.sender().isChecked()
        self.actionToggleTabs.setChecked(value)
        self.tabBar.setVisible(value)

    @pyqtSlot(int)
    def onTabCloseRequested(self, index):
        current_widget = self.getWidgetAtIndex(index)
        if isinstance(current_widget, DirectoriesDialog):
            # On MacOS, the tab has a close button even though we explicitely
            # set it to None in order to hide it. This should prevent
            # the "Directories" tab from closing by mistake.
            return
        current_widget.close()
        self.stackedWidget.removeWidget(current_widget)
Exemplo n.º 11
0
class ExecTA(ElementMaster):

    pixmap_path = 'images/ExecTA.png'
    child_pos = (True, False)

    def __init__(self, row, column):

        self.row = row
        self.column = column

        ta_str = 'MA'
        ta_index = 0
        ta_config = (3, )
        log_state = False

        self.config = (ta_str, ta_index, ta_config, log_state)

        super().__init__(self.row, self.column, QPixmap(self.pixmap_path), True, self.config)
        super().edit_sig.connect(self.edit)
        logging.debug('ExecTA called at row {}, column {}'.format(row, column))
        self.addFunction(TAFunction)

    def __setstate__(self, state):
        logging.debug('__setstate__() called ExecTA')
        self.row, self.column, self.config = state
        super().__init__(self.row, self.column, QPixmap(self.pixmap_path), True, self.config)
        super().edit_sig.connect(self.edit)
        self.addFunction(TAFunction)

    def __getstate__(self):
        logging.debug('__getstate__() called ExecTA')
        return (self.row, self.column, self.config)

    def openEditor(self):
        logging.debug('openEditor() called ExecTA')

    def edit(self):

        logging.debug('edit() called ExecTA')

        ta_str, ta_index, ta_config, log_state = self.config

        self.basic_ta_layout = QVBoxLayout()
        self.confirm_button = QPushButton(QC.translate('', 'Ok'))

        self.interval_txt = QLabel()
        self.interval_txt.setText(QC.translate('', 'Choose technical analysis function'))

        # https://github.com/sammchardy/python-binance/blob/master/binance/client.py
        self.selectTA = QComboBox()
        self.selectTA.addItem(QC.translate('', 'Moving Average'), QVariant('MA'))
        self.selectTA.addItem(QC.translate('', 'Exponential Moving Average'), QVariant('EMA'))
        self.selectTA.addItem(QC.translate('', 'Stochastic Oscillator %K'), QVariant('STOK'))
        self.selectTA.addItem(QC.translate('', 'Stochastic Oscillator %D'), QVariant('STO'))
        self.selectTA.addItem(QC.translate('', 'Relative Strenght Index'), QVariant('RSI'))
        """
        self.selectTA.addItem(QC.translate('', 'Momentum'), QVariant('MOM'))
        self.selectTA.addItem(QC.translate('', 'Rate of Change'), QVariant('ROC'))
        self.selectTA.addItem(QC.translate('', 'Average True Range'), QVariant('ATR'))
        self.selectTA.addItem(QC.translate('', 'Bollinger Bands'), QVariant('BBANDS'))
        self.selectTA.addItem(QC.translate('', 'Pivot Points, Support and Resitances'), QVariant('PPSR'))
        self.selectTA.addItem(QC.translate('', 'Trix'), QVariant('TRIX'))
        self.selectTA.addItem(QC.translate('', 'Average Directional Movement Index'), QVariant('ADX'))
        self.selectTA.addItem(QC.translate('', 'MACD, MACD Signal and MACD diffrence'), QVariant('MACD'))
        self.selectTA.addItem(QC.translate('', 'Mass Index'), QVariant('MI'))
        self.selectTA.addItem(QC.translate('', 'Vortex Indikator'), QVariant('VORTEX'))
        self.selectTA.addItem(QC.translate('', 'KST Oscillator'), QVariant('KST'))
        self.selectTA.addItem(QC.translate('', 'True Strenght Index'), QVariant('TSI'))
        self.selectTA.addItem(QC.translate('', 'Accumulation/Distribution'), QVariant('ACCDIST'))
        self.selectTA.addItem(QC.translate('', 'Chaikin Oscillator'), QVariant('CHAI'))
        self.selectTA.addItem(QC.translate('', 'Money Flow Index and Ratio'), QVariant('MFI'))
        self.selectTA.addItem(QC.translate('', 'On Balance Volume'), QVariant('OBV'))
        self.selectTA.addItem(QC.translate('', 'Force Index'), QVariant('FI'))
        self.selectTA.addItem(QC.translate('', 'Ease of Movement'), QVariant('EOM'))
        self.selectTA.addItem(QC.translate('', 'Commodity Channel Index'), QVariant('CCI'))
        """
        self.selectTA.setCurrentIndex(ta_index)


        self.variable_box = QStackedWidget()
        self.maInput()
        self.emaInput()
        self.stokInput()
        self.stoInput()
        self.rsiInput()
        self.loadLastConfig()

        logging.debug('edit() - {} elements in QStackedWidget'.format(self.variable_box.count()))

        self.link_line = QWidget()
        self.link_line_layout = QHBoxLayout(self.link_line)

        self.link_txt = QLabel()
        self.link_txt.setText(QC.translate('', 'Find information about technical analysis on'))

        self.link = QLabel()
        self.link.setText('<a href="https://www.investopedia.com/walkthrough/forex/">Investopedia</a>')
        self.link.setTextFormat(Qt.RichText)
        self.link.setTextInteractionFlags(Qt.TextBrowserInteraction)
        self.link.setOpenExternalLinks(True)

        self.link_line_layout.addWidget(self.link_txt)
        self.link_line_layout.addWidget(self.link)
        self.link_line_layout.addStretch(1)

        # hier logging option einfügen
        self.log_line = QWidget()
        self.ask_for_logging = QLabel()
        self.ask_for_logging.setText(QC.translate('', 'Log output?'))
        self.log_checkbox = QCheckBox()
        self.log_line_layout = QHBoxLayout(self.log_line)
        self.log_line_layout.addWidget(self.ask_for_logging)
        self.log_line_layout.addWidget(self.log_checkbox)
        self.log_line_layout.addStretch(1)

        if log_state:
            self.log_checkbox.setChecked(True)


        self.basic_ta_edit = ElementEditor(self)
        self.basic_ta_edit.setWindowTitle(QC.translate('', 'Edit TA function'))

        # signals and slots
        self.confirm_button.clicked.connect(self.basic_ta_edit.closeEvent)
        self.basic_ta_edit.window_closed.connect(self.edit_done)
        self.selectTA.currentIndexChanged.connect(self.indexChanged)

        self.basic_ta_layout.addWidget(self.interval_txt)
        self.basic_ta_layout.addWidget(self.selectTA)
        self.basic_ta_layout.addWidget(self.variable_box)
        self.basic_ta_layout.addStretch(1)
        self.basic_ta_layout.addWidget(self.log_line)
        self.basic_ta_layout.addWidget(self.link_line)
        self.basic_ta_layout.addWidget(self.confirm_button)
        self.basic_ta_edit.setLayout(self.basic_ta_layout)
        self.basic_ta_edit.show()
        
    def loadLastConfig(self):

        ta_str, ta_index, ta_config, log_state = self.config

        logging.debug('loadLastConfig() called with ta_str = {}'.format(ta_str))

        self.variable_box.setCurrentIndex(ta_index)

        if ta_str == 'MA':
            self.ma_range_input.setText(str(ta_config[0]))
        elif ta_str == 'EMA':
            self.ema_range_input.setText(str(ta_config[0]))
        elif ta_str == 'STO':
            self.sto_range_input.setText(str(ta_config[0]))
        elif ta_str == 'RSI':
            self.rsi_range_input.setText(str(ta_config[0]))

    def maInput(self):

        self.ma_input = QWidget()
        self.ma_layout = QHBoxLayout(self.ma_input)

        self.ma_range_txt = QLabel()
        self.ma_range_txt.setText(QC.translate('', 'Enter time range MA'))

        self.ma_range_input = QLineEdit()
        self.ma_range_input.setValidator(QIntValidator(1, 999))
        self.ma_range_input.setPlaceholderText(QC.translate('', 'Default value: 3'))

        self.ma_layout.addWidget(self.ma_range_txt)
        self.ma_layout.addWidget(self.ma_range_input)


        self.variable_box.addWidget(self.ma_input)

    def emaInput(self):

        self.ema_input = QWidget()
        self.ema_layout = QHBoxLayout(self.ema_input)

        self.ema_range_txt = QLabel()
        self.ema_range_txt.setText(QC.translate('', 'Enter time range EMA'))

        self.ema_range_input = QLineEdit()
        self.ema_range_input.setValidator(QIntValidator(1, 999))
        self.ema_range_input.setPlaceholderText(QC.translate('', 'Default value: 3'))

        self.ema_layout.addWidget(self.ema_range_txt)
        self.ema_layout.addWidget(self.ema_range_input)

        self.variable_box.addWidget(self.ema_input)

    def stokInput(self):

        self.stok_input = QWidget()
        self.stok_layout = QHBoxLayout(self.stok_input)

        self.variable_box.addWidget(self.stok_input)

    def stoInput(self):

        self.sto_input = QWidget()
        self.sto_layout = QHBoxLayout(self.sto_input)

        self.sto_range_txt = QLabel()
        self.sto_range_txt.setText(QC.translate('', 'Enter MA period'))

        self.sto_range_input = QLineEdit()
        self.sto_range_input.setValidator(QIntValidator(1, 999))
        self.sto_range_input.setPlaceholderText(QC.translate('', 'Default value: 3'))

        self.sto_layout.addWidget(self.sto_range_txt)
        self.sto_layout.addWidget(self.sto_range_input)

        self.variable_box.addWidget(self.sto_input)

    def rsiInput(self):

        self.rsi_input = QWidget()
        self.rsi_layout = QHBoxLayout(self.rsi_input)

        self.rsi_range_txt = QLabel()
        self.rsi_range_txt.setText(QC.translate('', 'Enter periods'))

        self.rsi_range_input = QLineEdit()
        self.rsi_range_input.setValidator(QIntValidator(1, 999))
        self.rsi_range_input.setPlaceholderText(QC.translate('', 'Default value: 3'))

        self.rsi_layout.addWidget(self.rsi_range_txt)
        self.rsi_layout.addWidget(self.rsi_range_input)

        self.variable_box.addWidget(self.rsi_input)


    def indexChanged(self, event):

        current_index = event
        logging.debug('indexChanged() called {}'.format(current_index))
        self.variable_box.setCurrentIndex(current_index)

        if current_index == 0:

            logging.debug('Moving Average selected - {}'.format(self.selectTA.currentData()))

        elif current_index == 1:

            logging.debug('Exponential Moving Average selected')

    def edit_done(self):

        logging.debug('edit_done() called ExecTA')
        if self.selectTA.currentData() == 'MA':

            period = self.ma_range_input.text()

            if period == '':
                ta_config = (3, )
            else:
                ta_config = (int(period), )

            logging.debug('edit_done() - Moving Average selected - {}'.format(ta_config))

        elif self.selectTA.currentData() == 'EMA':

            period = self.ema_range_input.text()

            if period == '':
                ta_config = (3, )
            else:
                ta_config = (int(period), )

            logging.debug('edit_done() - Exponential Moving Average selected - {}'.format(ta_config))

        elif self.selectTA.currentData() == 'STO':

            period = self.sto_range_input.text()

            if period == '':
                ta_config = (3, )
            else:
                ta_config = (int(period), )

            logging.debug('edit_done() - Stochastic Oscillator %D or EMA or RSI selected - {}'.format(ta_config))

        elif self.selectTA.currentData() == 'RSI':

            period = self.rsi_range_input.text()

            if period == '':
                ta_config = (3, )
            else:
                ta_config = (int(period), )

            logging.debug('edit_done() - Relative Strenght Index selected - {}'.format(ta_config))


        else:
            ta_config = None


        ta_str    = self.selectTA.currentData()
        ta_index  = self.selectTA.currentIndex()
        log_state = self.log_checkbox.isChecked()

        self.config = (ta_str, ta_index, ta_config, log_state)
        self.addFunction(TAFunction)
Exemplo n.º 12
0
class FontWindow(BaseWindow):
    def __init__(self, font, parent=None):
        super().__init__(parent)
        self._font = None

        self._infoWindow = None
        self._featuresWindow = None
        self._groupsWindow = None
        self._kerningWindow = None
        self._metricsWindow = None

        self.toolBar = ToolBar(self)
        self.toolBar.setTools(t() for t in QApplication.instance().drawingTools())

        self.glyphCellView = GlyphCellView(self)
        self.glyphCellView.glyphActivated.connect(self.openGlyphTab)
        self.glyphCellView.glyphsDropped.connect(self._orderChanged)
        self.glyphCellView.selectionChanged.connect(self._selectionChanged)
        self.glyphCellView.setAcceptDrops(True)
        self.glyphCellView.setCellRepresentationName("TruFont.GlyphCell")
        self.glyphCellView.setFrameShape(self.glyphCellView.NoFrame)
        self.glyphCellView.setFocus()

        self.tabWidget = TabWidget(self)
        self.tabWidget.setAutoHide(True)
        self.tabWidget.setHeroFirstTab(True)
        self.tabWidget.addTab(self.tr("Font"))

        self.stackWidget = QStackedWidget(self)
        self.stackWidget.addWidget(self.glyphCellView)
        self.tabWidget.currentTabChanged.connect(self._tabChanged)
        self.tabWidget.tabRemoved.connect(
            lambda index: self.stackWidget.removeWidget(self.stackWidget.widget(index))
        )
        self.stackWidget.currentChanged.connect(self._widgetChanged)

        self.propertiesView = PropertiesView(font, self)
        self.propertiesView.hide()

        self.statusBar = StatusBar(self)
        self.statusBar.setMinimumSize(32)
        self.statusBar.setMaximumSize(128)
        self.statusBar.sizeChanged.connect(self._sizeChanged)

        self.setFont_(font)

        app = QApplication.instance()
        app.dispatcher.addObserver(
            self, "_drawingToolRegistered", "drawingToolRegistered"
        )
        app.dispatcher.addObserver(
            self, "_drawingToolUnregistered", "drawingToolUnregistered"
        )
        app.dispatcher.addObserver(
            self, "_glyphViewGlyphsChanged", "glyphViewGlyphsChanged"
        )

        layout = QHBoxLayout(self)
        layout.addWidget(self.toolBar)
        vLayout = QVBoxLayout()
        vLayout.addWidget(self.tabWidget)
        pageWidget = PageWidget()
        pageWidget.addWidget(self.stackWidget)
        pageWidget.addWidget(self.statusBar)
        vLayout.addWidget(pageWidget)
        layout.addLayout(vLayout)
        layout.addWidget(self.propertiesView)
        layout.setContentsMargins(0, 2, 0, 0)
        layout.setSpacing(2)

        elements = [
            ("Ctrl+D", self.deselect),
            (platformSpecific.closeKeySequence(), self.closeGlyphTab),
            # XXX: does this really not warrant widget focus?
            (QKeySequence.Delete, self.delete),
            ("Shift+" + QKeySequence(QKeySequence.Delete).toString(), self.delete),
            ("Z", lambda: self.zoom(1)),
            ("X", lambda: self.zoom(-1)),
        ]
        e = platformSpecific.altDeleteSequence()
        if e is not None:
            elements.append((e, self.delete))
        e = platformSpecific.altRedoSequence()
        if e is not None:
            elements.append((e, self.redo))
        for keys, callback in elements:
            shortcut = QShortcut(QKeySequence(keys), self)
            shortcut.activated.connect(callback)

        self.installEventFilter(PreviewEventFilter(self))

        self.readSettings()
        self.propertiesView.activeLayerModified.connect(self._activeLayerModified)
        self.statusBar.sizeChanged.connect(self.writeSettings)

    def readSettings(self):
        geometry = settings.fontWindowGeometry()
        if geometry:
            self.restoreGeometry(geometry)
        cellSize = settings.glyphCellSize()
        self.statusBar.setSize(cellSize)
        hidden = settings.propertiesHidden()
        if not hidden:
            self.properties()

    def writeSettings(self):
        settings.setFontWindowGeometry(self.saveGeometry())
        settings.setGlyphCellSize(self.glyphCellView.cellSize()[0])
        settings.setPropertiesHidden(self.propertiesView.isHidden())

    def menuBar(self):
        return self.layout().menuBar()

    def setMenuBar(self, menuBar):
        self.layout().setMenuBar(menuBar)

    def setupMenu(self, menuBar):
        app = QApplication.instance()

        fileMenu = menuBar.fetchMenu(Entries.File)
        fileMenu.fetchAction(Entries.File_New)
        fileMenu.fetchAction(Entries.File_Open)
        fileMenu.fetchMenu(Entries.File_Open_Recent)
        if not platformSpecific.mergeOpenAndImport():
            fileMenu.fetchAction(Entries.File_Import)
        fileMenu.addSeparator()
        fileMenu.fetchAction(Entries.File_Save, self.saveFile)
        fileMenu.fetchAction(Entries.File_Save_As, self.saveFileAs)
        fileMenu.fetchAction(Entries.File_Save_All)
        fileMenu.fetchAction(Entries.File_Reload, self.reloadFile)
        fileMenu.addSeparator()
        fileMenu.fetchAction(Entries.File_Export, self.exportFile)
        fileMenu.fetchAction(Entries.File_Exit)

        editMenu = menuBar.fetchMenu(Entries.Edit)
        self._undoAction = editMenu.fetchAction(Entries.Edit_Undo, self.undo)
        self._redoAction = editMenu.fetchAction(Entries.Edit_Redo, self.redo)
        editMenu.addSeparator()
        cut = editMenu.fetchAction(Entries.Edit_Cut, self.cut)
        copy = editMenu.fetchAction(Entries.Edit_Copy, self.copy)
        copyComponent = editMenu.fetchAction(
            Entries.Edit_Copy_As_Component, self.copyAsComponent
        )
        paste = editMenu.fetchAction(Entries.Edit_Paste, self.paste)
        self._clipboardActions = (cut, copy, copyComponent, paste)
        editMenu.fetchAction(Entries.Edit_Select_All, self.selectAll)
        # editMenu.fetchAction(Entries.Edit_Deselect, self.deselect)
        editMenu.fetchAction(Entries.Edit_Find, self.findGlyph)
        editMenu.addSeparator()
        editMenu.fetchAction(Entries.Edit_Settings)

        viewMenu = menuBar.fetchMenu(Entries.View)
        viewMenu.fetchAction(Entries.View_Zoom_In, lambda: self.zoom(1))
        viewMenu.fetchAction(Entries.View_Zoom_Out, lambda: self.zoom(-1))
        viewMenu.fetchAction(Entries.View_Reset_Zoom, self.resetZoom)
        viewMenu.addSeparator()
        viewMenu.fetchAction(Entries.View_Next_Tab, lambda: self.tabOffset(1))
        viewMenu.fetchAction(Entries.View_Previous_Tab, lambda: self.tabOffset(-1))
        viewMenu.fetchAction(Entries.View_Next_Glyph, lambda: self.glyphOffset(1))
        viewMenu.fetchAction(Entries.View_Previous_Glyph, lambda: self.glyphOffset(-1))
        viewMenu.fetchAction(Entries.View_Layer_Up, lambda: self.layerOffset(-1))
        viewMenu.fetchAction(Entries.View_Layer_Down, lambda: self.layerOffset(1))
        viewMenu.addSeparator()
        viewMenu.fetchAction(Entries.View_Show_Points)
        viewMenu.fetchAction(Entries.View_Show_Metrics)
        viewMenu.fetchAction(Entries.View_Show_Images)
        viewMenu.fetchAction(Entries.View_Show_Guidelines)

        fontMenu = menuBar.fetchMenu(Entries.Font)
        fontMenu.fetchAction(Entries.Font_Font_Info, self.fontInfo)
        fontMenu.fetchAction(Entries.Font_Font_Features, self.fontFeatures)
        fontMenu.addSeparator()
        fontMenu.fetchAction(Entries.Font_Add_Glyphs, self.addGlyphs)
        fontMenu.fetchAction(Entries.Font_Sort, self.sortGlyphs)

        # glyphMenu = menuBar.fetchMenu(self.tr("&Glyph"))
        # self._layerAction = glyphMenu.fetchAction(
        #     self.tr("&Layer Actions…"), self.layerActions, "L")

        menuBar.fetchMenu(Entries.Scripts)

        windowMenu = menuBar.fetchMenu(Entries.Window)
        windowMenu.fetchAction(Entries.Window_Groups, self.groups)
        windowMenu.fetchAction(Entries.Window_Kerning, self.kerning)
        windowMenu.fetchAction(Entries.Window_Metrics, self.metrics)
        windowMenu.fetchAction(Entries.Window_Scripting)
        windowMenu.fetchAction(Entries.Window_Properties, self.properties)
        windowMenu.addSeparator()
        action = windowMenu.fetchAction(Entries.Window_Output)
        action.setEnabled(app.outputWindow is not None)

        helpMenu = menuBar.fetchMenu(Entries.Help)
        helpMenu.fetchAction(Entries.Help_Documentation)
        helpMenu.fetchAction(Entries.Help_Report_An_Issue)
        helpMenu.addSeparator()
        helpMenu.fetchAction(Entries.Help_About)

        self._updateGlyphActions()

    # --------------
    # Custom methods
    # --------------

    def font_(self):
        return self._font

    def setFont_(self, font):
        if self._font is not None:
            self._font.removeObserver(self, "Font.Changed")
            self._font.removeObserver(self, "Font.GlyphOrderChanged")
            self._font.removeObserver(self, "Font.SortDescriptorChanged")
        self._font = font
        self.setWindowTitle(self.fontTitle())
        if font is None:
            return
        self._updateGlyphsFromGlyphOrder()
        font.addObserver(self, "_fontChanged", "Font.Changed")
        font.addObserver(self, "_glyphOrderChanged", "Font.GlyphOrderChanged")
        font.addObserver(self, "_sortDescriptorChanged", "Font.SortDescriptorChanged")

    def fontTitle(self):
        if self._font is None:
            return None
        path = self._font.path or self._font.binaryPath
        if path is not None:
            return os.path.basename(path.rstrip(os.sep))
        return self.tr("Untitled")

    def isGlyphTab(self):
        return bool(self.stackWidget.currentIndex())

    def openGlyphTab(self, glyph):
        # if a tab with this glyph exists already, switch to it
        for index in range(self.stackWidget.count()):
            if not index:
                continue
            view = self.stackWidget.widget(index)
            if list(view.glyphs()) == [glyph]:
                self.tabWidget.setCurrentTab(index)
                return
        # spawn
        widget = GlyphCanvasView(self)
        widget.setInputNames([glyph.name])
        widget.activeGlyphChanged.connect(self._selectionChanged)
        widget.glyphNamesChanged.connect(self._namesChanged)
        widget.pointSizeModified.connect(self.statusBar.setSize)
        widget.toolModified.connect(self.toolBar.setCurrentTool)
        # add
        self.tabWidget.addTab(_textForGlyphs([glyph]))
        self.stackWidget.addWidget(widget)
        # activate
        self.tabWidget.setCurrentTab(-1)

    def closeGlyphTab(self):
        index = self.stackWidget.currentIndex()
        if index:
            self.tabWidget.removeTab(index)

    def maybeSaveBeforeExit(self):
        if self._font.dirty:
            ret = CloseMessageBox.getCloseDocument(self, self.fontTitle())
            if ret == QMessageBox.Save:
                self.saveFile()
                return True
            elif ret == QMessageBox.Discard:
                return True
            return False
        return True

    # -------------
    # Notifications
    # -------------

    # app

    def _drawingToolRegistered(self, notification):
        toolClass = notification.data["tool"]
        index = self.stackWidget.currentIndex()
        parent = self.stackWidget.currentWidget() if index else None
        self.toolBar.addTool(toolClass(parent=parent))

    def _drawingToolUnregistered(self, notification):
        toolClass = notification.data["tool"]
        for tool in self.toolBar.tools():
            if isinstance(tool, toolClass):
                self.toolBar.removeTool(tool)
                return
        raise ValueError(f"couldn't find tool to unregister: {toolClass}")

    def _glyphViewGlyphsChanged(self, notification):
        self._updateGlyphActions()

    # widgets

    def _activeLayerModified(self):
        if self.isGlyphTab():
            widget = self.stackWidget.currentWidget()
            index = self.sender().currentIndex().row()
            layers = self._font.layers
            layer = layers[layers.layerOrder[index]]
            currentGlyph = widget.activeGlyph()
            # XXX: adjust TLayer.get and use it
            if currentGlyph.name in layer:
                glyph = layer[currentGlyph.name]
            else:
                glyph = layer.newGlyph(currentGlyph.name)
            widget.setActiveGlyph(glyph)

    def _namesChanged(self):
        sender = self.sender()
        index = self.stackWidget.indexOf(sender)
        self.tabWidget.setTabName(index, _textForGlyphs(sender.glyphs()))

    def _sizeChanged(self):
        size = self.statusBar.size()
        if self.isGlyphTab():
            widget = self.stackWidget.currentWidget()
            widget.setPointSize(size)
        else:
            self.glyphCellView.setCellSize(size)

    def _tabChanged(self, index):
        self.statusBar.setShouldPropagateSize(not index)
        # we need to hide, then setParent, then show
        self.stackWidget.currentWidget().hide()
        newWidget = self.stackWidget.widget(index)
        if index:
            for tool in self.toolBar.tools():
                tool.setParent(newWidget)
        self.stackWidget.setCurrentIndex(index)
        newWidget.setFocus(Qt.OtherFocusReason)

    def _toolChanged(self, tool):
        widget = self.stackWidget.currentWidget()
        ok = widget.setCurrentTool(tool)
        # the glyph view NAKed the change (in mouseDown)
        # set back the current tool in the toolbar
        if not ok:
            self.toolBar.setCurrentTool(widget.currentTool())

    def _widgetChanged(self, index):
        # update current glyph
        self._updateCurrentGlyph()
        # update undo/redo
        self._updateGlyphActions()
        # update slider
        if self.isGlyphTab():
            lo, hi, unit = 0, 900000, " pt"
            widget = self.stackWidget.currentWidget()
            size = widget.pointSize()
        else:
            lo, hi, unit = 32, 128, None
            size = self.glyphCellView.cellSize()[0]
        self.statusBar.setMinimumSize(lo)
        self.statusBar.setMaximumSize(hi)
        self.statusBar.setSize(size)
        self.statusBar.setUnit(unit)
        self.statusBar.setTextVisible(not self.isGlyphTab())
        # update and connect setCurrentTool
        try:
            self.toolBar.currentToolChanged.disconnect()
        except TypeError:
            pass
        if not index:
            return
        widget = self.stackWidget.currentWidget()
        widget.setCurrentTool(self.toolBar.currentTool())
        self.toolBar.currentToolChanged.connect(self._toolChanged)

    def _orderChanged(self):
        # TODO: reimplement when we start showing glyph subsets
        glyphs = self.glyphCellView.glyphs()
        self._font.glyphOrder = [glyph.name for glyph in glyphs]

    def _selectionChanged(self):
        if self.isGlyphTab():
            activeGlyph = self.stackWidget.currentWidget().activeGlyph()
        else:
            activeGlyph = self.glyphCellView.lastSelectedGlyph()
            # selection text
            # TODO: this should probably be internal to the label
            selection = self.glyphCellView.selection()
            if selection is not None:
                count = len(selection)
                if count == 1:
                    glyph = self.glyphCellView.glyphsForIndexes(selection)[0]
                    text = "%s " % glyph.name
                else:
                    text = ""
                if count:
                    text = self.tr(f"{text}(%n selected)", n=count)
            else:
                text = ""
            self.statusBar.setText(text)
        # currentGlyph
        app = QApplication.instance()
        app.setCurrentGlyph(activeGlyph)
        # actions
        self._updateGlyphActions()

    # defcon

    def _fontChanged(self, notification):
        font = notification.object
        self.setWindowModified(font.dirty)

    def _glyphOrderChanged(self, notification):
        self._updateGlyphsFromGlyphOrder()

    def _updateGlyphsFromGlyphOrder(self):
        font = self._font
        glyphOrder = font.glyphOrder
        if glyphOrder:
            glyphCount = 0
            glyphs = []
            for glyphName in glyphOrder:
                if glyphName in font:
                    glyph = font[glyphName]
                    glyphCount += 1
                else:
                    glyph = font.get(glyphName, asTemplate=True)
                glyphs.append(glyph)
            if glyphCount < len(font):
                # if some glyphs in the font are not present in the glyph
                # order, loop again to add them at the end
                for glyph in font:
                    if glyph not in glyphs:
                        glyphs.append(glyph)
                font.disableNotifications(observer=self)
                font.glyphOrder = [glyph.name for glyph in glyphs]
                font.enableNotifications(observer=self)
        else:
            glyphs = list(font)
            font.disableNotifications(observer=self)
            font.glyphOrder = [glyph.name for glyph in glyphs]
            font.enableNotifications(observer=self)
        self.glyphCellView.setGlyphs(glyphs)

    def _sortDescriptorChanged(self, notification):
        font = notification.object
        descriptors = notification.data["newValue"]
        if descriptors is None:
            return
        if descriptors[0]["type"] == "glyphSet":
            glyphNames = descriptors[0]["glyphs"]
        else:
            glyphNames = font.unicodeData.sortGlyphNames(font.keys(), descriptors)
        font.glyphOrder = glyphNames

    # ------------
    # Menu methods
    # ------------

    # File

    def saveFile(self, path=None, ufoFormatVersion=3):
        if path is None and self._font.path is None:
            self.saveFileAs()
        else:
            if path is None:
                path = self._font.path
            self._font.save(path, ufoFormatVersion)

    def saveFileAs(self):
        fileFormats = OrderedDict(
            [
                (self.tr("UFO Font version 3 {}").format("(*.ufo)"), 3),
                (self.tr("UFO Font version 2 {}").format("(*.ufo)"), 2),
            ]
        )
        state = settings.saveFileDialogState()
        path = self._font.path or self._font.binaryPath
        if path:
            directory = os.path.dirname(path)
        else:
            directory = (
                None
                if state
                else QStandardPaths.standardLocations(QStandardPaths.DocumentsLocation)[
                    0
                ]
            )
        # TODO: switch to directory dlg on platforms that need it
        dialog = QFileDialog(
            self, self.tr("Save File"), directory, ";;".join(fileFormats.keys())
        )
        if state:
            dialog.restoreState(state)
        dialog.setAcceptMode(QFileDialog.AcceptSave)
        if directory:
            dialog.setDirectory(directory)
        ok = dialog.exec_()
        settings.setSaveFileDialogState(dialog.saveState())
        if ok:
            nameFilter = dialog.selectedNameFilter()
            path = dialog.selectedFiles()[0]
            if not os.path.basename(path).endswith(".ufo"):
                path += ".ufo"
            self.saveFile(path, fileFormats[nameFilter])
            app = QApplication.instance()
            app.setCurrentFile(self._font.path)
            self.setWindowTitle(self.fontTitle())
        # return ok

    def reloadFile(self):
        font = self._font
        path = font.path or font.binaryPath
        if not font.dirty or path is None:
            return
        if not ReloadMessageBox.getReloadDocument(self, self.fontTitle()):
            return
        if font.path is not None:
            font.reloadInfo()
            font.reloadKerning()
            font.reloadGroups()
            font.reloadFeatures()
            font.reloadLib()
            font.reloadGlyphs(font.keys())
            font.dirty = False
        else:
            # TODO: we should do this in-place
            font_ = font.__class__().new()
            font_.extract(font.binaryPath)
            self.setFont_(font_)

    def exportFile(self):
        params, ok = ExportDialog.getExportParameters(self, self._font)
        if not ok:
            return
        baseName = params["baseName"]
        directory = params["exportDirectory"]
        compression = set(map(str.lower, params["compression"]))
        for format in map(str.lower, params["formats"]):
            fileName = f"{baseName}.{format}"
            path = os.path.join(directory, fileName)
            try:
                self._font.export(path, format, compression=compression)
            except Exception as e:
                msg = (
                    self.tr("This font’s feature file contains an error.")
                    if isinstance(e, FeatureLibError)
                    else None
                )
                errorReports.showCriticalException(e, message=msg)

    # Edit

    def undo(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
        else:
            glyph = widget.lastSelectedGlyph()
        glyph.undo()

    def redo(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
        else:
            glyph = widget.lastSelectedGlyph()
        glyph.redo()

    def cut(self):
        self.copy()
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            deleteUISelection(glyph)
        else:
            glyphs = widget.glyphs()
            for index in widget.selection():
                glyph = glyphs[index]
                glyph.clear()

    def copy(self):
        font = self._font
        widget = self.stackWidget.currentWidget()
        clipboard = QApplication.clipboard()
        mimeData = QMimeData()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            copyGlyph = glyph.getRepresentation("TruFont.FilterSelection")
            packGlyphs = (copyGlyph,)
        else:
            glyphs = self.glyphCellView.glyphs()
            packGlyphs = (
                glyphs[index] for index in sorted(self.glyphCellView.selection())
            )

        svgGlyphs = []
        pickled = []
        for i, glyph in enumerate(packGlyphs):
            pickled.append(glyph.serialize(blacklist=("name", "unicodes")))

            pen = SVGPathPen(font)
            glyph.draw(pen)
            col = i % 5
            row = i // 5
            g = '<g transform="matrix(1,0,0,-1,{:f},{:f})"><path d="{}"/></g>'.format(
                font.info.unitsPerEm * col,
                font.info.unitsPerEm * row,
                pen.getCommands(),
            )
            svgGlyphs.append(g)

        mimeData.setData("application/x-trufont-glyph-data", pickle.dumps(pickled))

        svg = """\
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg">
%s
</svg>
""" % "\n".join(
            svgGlyphs
        )
        mimeData.setData("image/svg+xml", svg.encode("utf-8"))

        clipboard.setMimeData(mimeData)

    def copyAsComponent(self):
        if self.isGlyphTab():
            pass
        else:
            glyphs = self.glyphCellView.glyphs()
            pickled = []
            for index in self.glyphCellView.selection():
                glyph = glyphs[index]
                componentGlyph = glyph.__class__()
                componentGlyph.width = glyph.width
                component = componentGlyph.instantiateComponent()
                component.baseGlyph = glyph.name
                pickled.append(componentGlyph.serialize())
            clipboard = QApplication.clipboard()
            mimeData = QMimeData()
            mimeData.setData("application/x-trufont-glyph-data", pickle.dumps(pickled))
            clipboard.setMimeData(mimeData)

    def paste(self):
        isGlyphTab = self.isGlyphTab()
        widget = self.stackWidget.currentWidget()
        if isGlyphTab:
            glyphs = (widget.activeGlyph(),)
        else:
            selection = self.glyphCellView.selection()
            glyphs = widget.glyphsForIndexes(selection)
        clipboard = QApplication.clipboard()
        mimeData = clipboard.mimeData()
        if mimeData.hasFormat("application/x-trufont-glyph-data"):
            data = pickle.loads(mimeData.data("application/x-trufont-glyph-data"))
            if len(data) == len(glyphs):
                for pickled, glyph in zip(data, glyphs):
                    if isGlyphTab:
                        pasteGlyph = glyph.__class__()
                        pasteGlyph.deserialize(pickled)
                        # TODO: if we serialize selected state, we don't need
                        # to do this
                        pasteGlyph.selected = True
                        if (
                            len(pasteGlyph)
                            or len(pasteGlyph.components)
                            or len(pasteGlyph.anchors)
                        ):
                            glyph.beginUndoGroup()
                            glyph.holdNotifications()
                            count = len(glyph)
                            pen = glyph.getPointPen()
                            # contours, components
                            pasteGlyph.drawPoints(pen)
                            for contour in glyph[count:]:
                                contour.selected = True
                            # anchors
                            for anchor in pasteGlyph.anchors:
                                glyph.appendAnchor(dict(anchor))
                            # guidelines
                            for guideline in pasteGlyph.guidelines:
                                glyph.appendGuideline(dict(guideline))
                            glyph.releaseHeldNotifications()
                            glyph.endUndoGroup()
                    else:
                        glyph.deserialize(pickled)
            return
        if mimeData.hasFormat("image/svg+xml"):
            if len(glyphs) == 1:
                glyph = glyphs[0]
                try:
                    svgPath = SVGPath.fromstring(mimeData.data("image/svg+xml"))
                except Exception:
                    pass
                else:
                    glyph.beginUndoGroup()
                    if not isGlyphTab:
                        glyph.clear()
                    svgPath.draw(glyph.getPen())
                    glyph.endUndoGroup()
                    return
        if mimeData.hasText():
            if len(glyphs) == 1:
                glyph = glyphs[0]
                otherGlyph = glyph.__class__()
                text = mimeData.text()
                try:
                    readGlyphFromString(text, otherGlyph, otherGlyph.getPointPen())
                except Exception:
                    try:
                        svgPath = SVGPath.fromstring(text)
                        svgPath.draw(otherGlyph.getPen())
                    except Exception:
                        return
                glyph.beginUndoGroup()
                if not isGlyphTab:
                    glyph.clear()
                otherGlyph.drawPoints(glyph.getPointPen())
                glyph.endUndoGroup()

    def selectAll(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            if glyph.selected:
                for anchor in glyph.anchors:
                    anchor.selected = True
                for component in glyph.components:
                    component.selected = True
            else:
                glyph.selected = True
        else:
            widget.selectAll()

    def deselect(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            for anchor in glyph.anchors:
                anchor.selected = False
            for component in glyph.components:
                component.selected = False
            glyph.selected = False
        else:
            widget.setSelection(set())

    def delete(self):
        modifiers = QApplication.keyboardModifiers()
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            # TODO: fuse more the two methods, they're similar and delete is
            # Cut except not putting in the clipboard
            if modifiers & Qt.AltModifier:
                deleteUISelection(glyph)
            else:
                preserveShape = not modifiers & Qt.ShiftModifier
                removeUIGlyphElements(glyph, preserveShape)
        else:
            erase = modifiers & Qt.ShiftModifier
            if self._proceedWithDeletion(erase):
                glyphs = widget.glyphsForIndexes(widget.selection())
                for glyph in glyphs:
                    font = glyph.font
                    for layer in font.layers:
                        if glyph.name in layer:
                            defaultLayer = layer[glyph.name] == glyph
                            if defaultLayer and not erase:
                                # TODO: clear in glyph.template setter?
                                glyph.clear()
                                glyph.template = True
                            else:
                                del layer[glyph.name]

    def findGlyph(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
            newGlyph, ok = FindDialog.getNewGlyph(self, glyph)
            if ok and newGlyph is not None:
                widget.setActiveGlyph(newGlyph)
        else:
            pass  # XXX

    # View

    def zoom(self, step):
        if self.isGlyphTab():
            widget = self.stackWidget.currentWidget()
            newScale = widget.scale() * pow(1.2, step)
            widget.zoom(newScale)
            self.statusBar.setSize(widget.pointSize())
        else:
            value = self.statusBar.size()
            newValue = value + 10 * step
            self.statusBar.setSize(newValue)

    def resetZoom(self):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            widget.fitScaleBBox()
        else:
            settings.removeGlyphCellSize()
            cellSize = settings.glyphCellSize()
            self.statusBar.setSize(cellSize)

    def tabOffset(self, value):
        tab = self.tabWidget.currentTab()
        newTab = (tab + value) % len(self.tabWidget.tabs())
        self.tabWidget.setCurrentTab(newTab)

    def glyphOffset(self, value):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            currentGlyph = widget.activeGlyph()
            font = currentGlyph.font
            glyphOrder = font.glyphOrder
            # should be enforced in fontView already
            if not (glyphOrder and len(glyphOrder)):
                return
            index = glyphOrder.index(currentGlyph.name)
            newIndex = (index + value) % len(glyphOrder)
            glyph = font[glyphOrder[newIndex]]
            widget.setActiveGlyph(glyph)
        else:
            lastSelectedCell = widget.lastSelectedCell()
            if lastSelectedCell is None:
                return
            newIndex = lastSelectedCell + value
            if newIndex < 0 or newIndex >= len(widget.glyphs()):
                return
            widget.setSelection({newIndex})

    def layerOffset(self, value):
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            currentGlyph = widget.activeGlyph()
            layerSet, layer = currentGlyph.layerSet, currentGlyph.layer
            if None in (layerSet, layer):
                return
            index = layerSet.layerOrder.index(layer.name)
            newIndex = (index + value) % len(layerSet)
            layer_ = layerSet[layerSet.layerOrder[newIndex]]
            if layer_ == layer:
                return
            # XXX: fix get
            # glyph = layer_.get(currentGlyph.name)
            if currentGlyph.name in layer_:
                glyph = layer_[currentGlyph.name]
            else:
                glyph = layer_.newGlyph(currentGlyph.name)
            widget.setActiveGlyph(glyph)

    # Font

    def fontInfo(self):
        # If a window is already opened, bring it to the front, else spawn one.
        # TODO: see about using widget.setAttribute(Qt.WA_DeleteOnClose)
        # otherwise it seems we're just leaking memory after each close...
        # (both raise_ and show allocate memory instead of using the hidden
        # widget it seems)
        if self._infoWindow is not None and self._infoWindow.isVisible():
            self._infoWindow.raise_()
        else:
            self._infoWindow = FontInfoWindow(self._font, self)
            self._infoWindow.show()

    def fontFeatures(self):
        # TODO: see up here
        if self._featuresWindow is not None and self._featuresWindow.isVisible():
            self._featuresWindow.raise_()
        else:
            self._featuresWindow = FontFeaturesWindow(self._font, self)
            self._featuresWindow.show()

    def addGlyphs(self):
        glyphs = self.glyphCellView.glyphs()
        newGlyphNames, params, ok = AddGlyphsDialog.getNewGlyphNames(self, glyphs)
        if ok:
            sortFont = params.pop("sortFont")
            for name in newGlyphNames:
                glyph = self._font.get(name, **params)
                if glyph is not None:
                    glyphs.append(glyph)
            self.glyphCellView.setGlyphs(glyphs)
            if sortFont:
                # TODO: when the user add chars from a glyphSet and no others,
                # should we try to sort according to that glyphSet?
                # The above would probably warrant some rearchitecturing.
                # kick-in the sort mechanism
                self._font.sortDescriptor = self._font.sortDescriptor

    def sortGlyphs(self):
        sortDescriptor, ok = SortDialog.getDescriptor(self, self._font.sortDescriptor)
        if ok:
            self._font.sortDescriptor = sortDescriptor

    # Window

    def groups(self):
        # TODO: see up here
        if self._groupsWindow is not None and self._groupsWindow.isVisible():
            self._groupsWindow.raise_()
        else:
            self._groupsWindow = GroupsWindow(self._font, self)
            self._groupsWindow.show()

    def kerning(self):
        # TODO: see up here
        if self._kerningWindow is not None and self._kerningWindow.isVisible():
            self._kerningWindow.raise_()
        else:
            self._kerningWindow = KerningWindow(self._font, self)
            self._kerningWindow.show()

    def metrics(self):
        # TODO: see up here
        if self._metricsWindow is not None and self._metricsWindow.isVisible():
            self._metricsWindow.raise_()
        else:
            self._metricsWindow = MetricsWindow(self._font)
            # XXX: need proper, fast windowForFont API!
            self._metricsWindow._fontWindow = self
            self.destroyed.connect(self._metricsWindow.close)
            self._metricsWindow.show()
        # TODO: default string kicks-in on the window before this. Figure out
        # how to make a clean interface
        selection = self.glyphCellView.selection()
        if selection:
            glyphs = self.glyphCellView.glyphsForIndexes(selection)
            self._metricsWindow.setGlyphs(glyphs)

    def properties(self):
        shouldBeVisible = self.propertiesView.isHidden()
        self.propertiesView.setVisible(shouldBeVisible)
        self.writeSettings()

    # update methods

    def _setGlyphPreview(self, value):
        index = self.stackWidget.currentIndex()
        if index:
            widget = self.stackWidget.currentWidget()
            widget.setPreviewEnabled(value)

    def _updateCurrentGlyph(self):
        # TODO: refactor this pattern...
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            glyph = widget.activeGlyph()
        else:
            glyph = widget.lastSelectedGlyph()
        if glyph is not None:
            app = QApplication.instance()
            app.setCurrentGlyph(glyph)

    def _updateGlyphActions(self):
        if not hasattr(self, "_undoAction"):
            return
        widget = self.stackWidget.currentWidget()
        if self.isGlyphTab():
            currentGlyph = widget.activeGlyph()
        else:
            currentGlyph = widget.lastSelectedGlyph()
        # disconnect eventual signal of previous glyph
        objects = ((self._undoAction, self.undo), (self._redoAction, self.redo))
        for action, slot in objects:
            try:
                action.disconnect()
            except TypeError:
                pass
            action.triggered.connect(slot)
        # now update status
        if currentGlyph is None:
            self._undoAction.setEnabled(False)
            self._redoAction.setEnabled(False)
        else:
            undoManager = currentGlyph.undoManager
            self._undoAction.setEnabled(currentGlyph.canUndo())
            undoManager.canUndoChanged.connect(self._undoAction.setEnabled)
            self._redoAction.setEnabled(currentGlyph.canRedo())
            undoManager.canRedoChanged.connect(self._redoAction.setEnabled)
        # and other actions
        for action in self._clipboardActions:
            action.setEnabled(currentGlyph is not None)

    # helper

    def _proceedWithDeletion(self, erase=False):
        if not self.glyphCellView.selection():
            return
        tr = self.tr("Delete") if erase else self.tr("Clear")
        text = self.tr("Do you want to %s selected glyphs?") % tr.lower()
        closeDialog = QMessageBox(
            QMessageBox.Question,
            "",
            self.tr("%s glyphs") % tr,
            QMessageBox.Yes | QMessageBox.No,
            self,
        )
        closeDialog.setInformativeText(text)
        closeDialog.setModal(True)
        ret = closeDialog.exec_()
        if ret == QMessageBox.Yes:
            return True
        return False

    # ----------
    # Qt methods
    # ----------

    def setWindowTitle(self, title):
        if platformSpecific.appNameInTitle():
            title += " – TruFont"
        super().setWindowTitle(f"[*]{title}")

    def sizeHint(self):
        return QSize(1270, 800)

    def moveEvent(self, event):
        self.writeSettings()

    resizeEvent = moveEvent

    def showEvent(self, event):
        app = QApplication.instance()
        data = dict(font=self._font, window=self)
        app.postNotification("fontWindowWillOpen", data)
        super().showEvent(event)
        app.postNotification("fontWindowOpened", data)

    def closeEvent(self, event):
        ok = self.maybeSaveBeforeExit()
        if ok:
            app = QApplication.instance()
            data = dict(font=self._font, window=self)
            app.postNotification("fontWindowWillClose", data)
            self._font.removeObserver(self, "Font.Changed")
            app = QApplication.instance()
            app.dispatcher.removeObserver(self, "drawingToolRegistered")
            app.dispatcher.removeObserver(self, "drawingToolUnregistered")
            app.dispatcher.removeObserver(self, "glyphViewGlyphsChanged")
            event.accept()
        else:
            event.ignore()

    def event(self, event):
        if event.type() == QEvent.WindowActivate:
            app = QApplication.instance()
            app.setCurrentFontWindow(self)
            self._updateCurrentGlyph()
        return super().event(event)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(event.rect(), QColor(212, 212, 212))
Exemplo n.º 13
0
class ParamsByType(QWidget, MooseWidget):
    """
    Has a QComboBox for the different allowed types.
    On switching type a new ParamsByGroup is shown.
    """
    needBlockList = pyqtSignal(list)
    blockRenamed = pyqtSignal(object, str)
    changed = pyqtSignal()

    def __init__(self, block, **kwds):
        """
        Constructor.
        Input:
            block[BlockInfo]: The block to show.
        """
        super(ParamsByType, self).__init__(**kwds)
        self.block = block
        self.combo = QComboBox()
        self.types = []
        self.type_params_map = {}
        self.table_stack = QStackedWidget()
        self.type_table_map = {}

        for t in sorted(self.block.types.keys()):
            self.types.append(t)
            params_list = []
            for p in self.block.parameters_list:
                params_list.append(self.block.parameters[p])
            t_block = self.block.types[t]
            for p in t_block.parameters_list:
                params_list.append(t_block.parameters[p])
            self.type_params_map[t] = params_list

        self.combo.addItems(sorted(self.block.types.keys()))
        self.combo.currentTextChanged.connect(self.setBlockType)

        self.top_layout = WidgetUtils.addLayout(vertical=True)
        self.top_layout.addWidget(self.combo)
        self.top_layout.addWidget(self.table_stack)
        self.setLayout(self.top_layout)
        self.user_params = []
        self.setDefaultBlockType()

        self.setup()

    def _syncUserParams(self, current, to):
        """
        Sync user added parameters that are on the main block into
        each type ParamsByGroup.
        Input:
            current[ParamsByGroup]: The current group parameter table
            to[ParamsByGroup]: The new group parameter table
        """
        ct = current.findTable("Main")
        tot = to.findTable("Main")
        if not ct or not tot or ct == tot:
            return
        # first remove user params in tot
        tot.removeUserParams()
        params = ct.getUserParams()
        tot.addUserParams(params)
        idx = ct.findRow("Name")
        if idx >= 0:
            name = ct.item(idx, 1).text()
            idx = tot.findRow("Name")
            if idx >= 0:
                tot.item(idx, 1).setText(name)

    def currentType(self):
        return self.combo.currentText()

    def save(self):
        """
        Look at the user params in self.block.parameters.
        update the type tables
        Save type on block
        """
        t = self.getTable()
        if t:
            t.save()
            self.block.setBlockType(self.combo.currentText())

    def reset(self):
        t = self.getTable()
        t.reset()

    def getOrCreateTypeTable(self, type_name):
        """
        Gets the table for the type name or create it if it doesn't exist.
        Input:
            type_name[str]: Name of the type
        Return:
            ParamsByGroup: The parameters corresponding to the type
        """
        t = self.type_table_map.get(type_name)
        if t:
            return t
        t = ParamsByGroup(self.block, self.type_params_map.get(type_name, self.block.orderedParameters()))
        t.needBlockList.connect(self.needBlockList)
        t.blockRenamed.connect(self.blockRenamed)
        t.changed.connect(self.changed)
        self.type_table_map[type_name] = t
        self.table_stack.addWidget(t)
        return t

    def setDefaultBlockType(self):
        param = self.block.getParamInfo("type")
        if param and param.value:
            self.setBlockType(param.value)
        elif self.block.types:
            self.setBlockType(sorted(self.block.types.keys())[0])

    def setBlockType(self, type_name):
        if type_name not in self.block.types:
            return
        t = self.getOrCreateTypeTable(type_name)
        t.updateWatchers()
        self.combo.blockSignals(True)
        self.combo.setCurrentText(type_name)
        self.combo.blockSignals(False)
        t.updateType(type_name)
        current = self.table_stack.currentWidget()
        self._syncUserParams(current, t)
        self.table_stack.setCurrentWidget(t)
        self.changed.emit()

    def addUserParam(self, param):
        t = self.table_stack.currentWidget()
        t.addUserParam(param)

    def setWatchedBlockList(self, path, children):
        for i in range(self.table_stack.count()):
            t = self.table_stack.widget(i)
            t.setWatchedBlockList(path, children)

    def updateWatchers(self):
        for i in range(self.table_stack.count()):
            t = self.table_stack.widget(i)
            t.updateWatchers()

    def getTable(self):
        return self.table_stack.currentWidget()

    def paramValue(self, name):
        for i in range(self.table_stack.count()):
            t = self.table_stack.widget(i)
            if t.paramValue(name):
                return t.paramValue(name)
Exemplo n.º 14
0
class GstMediaSettings(SettingsSection):

    Name = 'Media Settings'

    def __init__(self, size, cue=None, parent=None):
        super().__init__(size, cue=cue, parent=parent)
        self._pipe = ''
        self._conf = {}
        self._check = False

        self.glayout = QGridLayout(self)

        self.listWidget = QListWidget(self)
        self.glayout.addWidget(self.listWidget, 0, 0)

        self.pipeButton = QPushButton('Change Pipe', self)
        self.glayout.addWidget(self.pipeButton, 1, 0)

        self.elements = QStackedWidget(self)
        self.glayout.addWidget(self.elements, 0, 1, 2, 1)

        self.glayout.setColumnStretch(0, 2)
        self.glayout.setColumnStretch(1, 5)

        self.listWidget.currentItemChanged.connect(self.__change_page)
        self.pipeButton.clicked.connect(self.__edit_pipe)

    def set_configuration(self, conf):
        # Get the media section of the cue configuration
        if conf is not None:
            conf = conf.get('media', {})

            # Activate the layout, so we can get the right widgets size
            self.glayout.activate()

            # Create a local copy of the configuration
            self._conf = deepcopy(conf)

            # Create the widgets
            sections = sections_by_element_name()
            for element in conf.get('pipe', '').split('!'):
                widget = sections.get(element)

                if widget is not None:
                    widget = widget(self.elements.size(), element, self)
                    widget.set_configuration(self._conf['elements'])
                    self.elements.addWidget(widget)

                    item = QListWidgetItem(widget.NAME)
                    self.listWidget.addItem(item)

            self.listWidget.setCurrentRow(0)

    def get_configuration(self):
        conf = {'elements': {}}

        for el in self.elements.children():
            if isinstance(el, SettingsSection):
                conf['elements'].update(el.get_configuration())

        # If in check mode the pipeline is not returned
        if not self._check:
            conf['pipe'] = self._conf['pipe']

        return {'media': conf}

    def enable_check(self, enable):
        self._check = enable
        for element in self.elements.children():
            if isinstance(element, SettingsSection):
                element.enable_check(enable)

    def __change_page(self, current, previous):
        if not current:
            current = previous

        self.elements.setCurrentIndex(self.listWidget.row(current))

    def __edit_pipe(self):
        # Backup the settings
        self._conf.update(self.get_configuration()['media'])

        # Show the dialog
        dialog = GstPipeEdit(self._conf.get('pipe', ''), parent=self)

        if dialog.exec_() == dialog.Accepted:
            # Reset the view
            for _ in range(self.elements.count()):
                self.elements.removeWidget(self.elements.widget(0))
            self.listWidget.clear()

            # Reload with the new pipeline
            self._conf['pipe'] = dialog.get_pipe()

            self.set_configuration({'media': self._conf})
            self.enable_check(self._check)
Exemplo n.º 15
0
class TableWidget(QSplitter):

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

        # vbox = QVBoxLayout(self)
        # vbox.setContentsMargins(0, 0, 0, 0)

        self._tabs = QTabWidget()
        self._tabs.setAutoFillBackground(True)
        p = self._tabs.palette()
        p.setColor(p.Window, QColor("white"))
        self._tabs.setPalette(p)
        self._other_tab = QTabWidget()
        self._other_tab.setAutoFillBackground(True)
        self._other_tab.setPalette(p)
        self.addWidget(self._tabs)
        self.addWidget(self._other_tab)
        self.setSizes([1, 1])
        self._other_tab.hide()

        self.relations = {}

        # Stack
        self.stacked = QStackedWidget()
        self._tabs.addTab(self.stacked, "Workspace")
        self.stacked_result = QStackedWidget()
        self._tabs.addTab(self.stacked_result, self.tr("Resultados"))

        btn_split = QToolButton()
        btn_split.setToolTip(self.tr("Click para dividir la pantalla"))
        btn_split.setAutoRaise(True)
        btn_split.setIcon(QIcon(":img/split"))
        self._tabs.setCornerWidget(btn_split)
        btn_split.clicked.connect(self._split)
        btn_split = QToolButton()
        btn_split.setToolTip(self.tr("Click para juntar las pantallas"))
        btn_split.setAutoRaise(True)
        btn_split.setIcon(QIcon(":img/split"))
        btn_split.clicked.connect(self._unsplit)
        self._other_tab.setCornerWidget(btn_split)
        # self.setContextMenuPolicy(Qt.CustomContextMenu)
        # self.customContextMenuRequested.connect(self._show_menu)

        lateral_widget = Pireal.get_service("lateral_widget")
        lateral_widget.resultClicked.connect(self._on_result_list_clicked)
        lateral_widget.resultSelectionChanged.connect(
            lambda index: self.stacked_result.setCurrentIndex(index))
        # lateral_widget.newRowsRequested.connect(self._insert_rows)

    def insert_rows(self, tuplas):
        current_view = self.current_table()
        if current_view is not None:
            model = current_view.model()
            for tupla in tuplas:
                model.insertRow(model.rowCount(), tupla)
        current_view.adjust_columns()

    def _on_result_list_clicked(self, index):
        self.stacked_result.setCurrentIndex(index)
        if not self._other_tab.isVisible():
            self._tabs.setCurrentIndex(1)

    def _unsplit(self):
        self._other_tab.hide()
        result_widget = self._other_tab.widget(0)
        self._tabs.addTab(result_widget, self.tr("Resultados"))
        self._tabs.cornerWidget().show()

    def _split(self):
        result_widget = self._tabs.widget(1)
        self._other_tab.addTab(result_widget, self.tr("Resultados"))
        self._other_tab.show()
        self.setSizes([1, 1])
        self._tabs.cornerWidget().hide()
        self.setOrientation(Qt.Horizontal)

    def _show_menu(self, position):
        menu = QMenu(self)

        if self.count() > 0:
            add_tuple_action = menu.addAction(self.tr("Agregar Tupla"))
            add_col_action = menu.addAction(self.tr("Add Column"))

            add_tuple_action.triggered.connect(self.add_tuple)
            add_col_action.triggered.connect(self.add_column)
            menu.addSeparator()

        add_relation_action = menu.addAction(self.tr("Create new Relation"))
        add_relation_action.triggered.connect(self.__new_relation)

        menu.exec_(self.mapToGlobal(position))

    def __new_relation(self):
        central_service = Pireal.get_service("central")
        central_service.create_new_relation()

    def count(self):
        return self.stacked.count()

    def remove_table(self, index):
        widget = self.stacked.widget(index)
        self.stacked.removeWidget(widget)
        del widget

    def current_table(self):
        return self.stacked.currentWidget()

    def remove_relation(self, name):
        del self.relations[name]

    def add_relation(self, name, rela):
        if self.relations.get(name, None) is None:
            self.relations[name] = rela
            return True
        return False

    def add_table(self, rela, name, table):
        """ Add new table from New Relation Dialog """

        self.add_relation(name, rela)
        self.stacked.addWidget(table)

    def add_tuple(self):
        current_view = self.current_table()
        if current_view is not None:
            model = current_view.model()
            model.insertRow(model.rowCount())

    def add_column(self):
        current_view = self.current_table()
        if current_view is not None:
            model = current_view.model()
            model.insertColumn(model.columnCount())

    def delete_tuple(self):
        current_view = self.current_table()
        if current_view is not None:
            model = current_view.model()
            selection = current_view.selectionModel()
            if selection.hasSelection():
                selection = selection.selection()
                rows = set([index.row() for index in selection.indexes()])
                rows = sorted(list(rows))
                previous = -1
                i = len(rows) - 1
                while i >= 0:
                    current = rows[i]
                    if current != previous:
                        model.removeRow(current)
                    i -= 1

    def delete_column(self):
        """ Elimina la/las columnas seleccionadas """

        current_view = self.current_table()
        if current_view is not None:
            model = current_view.model()
            selection = current_view.selectionModel()
            if selection.hasSelection():
                selection = selection.selection()
                columns = set(
                    [index.column() for index in selection.indexes()])
                columns = sorted(list(columns))
                previous = -1
                i = len(columns) - 1
                while i >= 0:
                    current = columns[i]
                    if current != previous:
                        model.removeColumn(current)
                    i -= 1

    def create_table(self, rela, editable=True):
        """ Se crea la vista y el modelo """

        _view = view.View()
        _model = model.Model(rela)
        if not editable:
            _model.editable = False
        _view.setModel(_model)
        _view.setItemDelegate(delegate.Delegate())
        _view.setHorizontalHeader(view.Header())
        return _view
Exemplo n.º 16
0
class CentralWidget(QWidget):
    # This signals is used by notificator
    databaseSaved = pyqtSignal('QString')
    querySaved = pyqtSignal('QString')
    databaseConected = pyqtSignal('QString')

    def __init__(self):
        QWidget.__init__(self)
        box = QVBoxLayout(self)
        box.setContentsMargins(0, 0, 0, 0)
        box.setSpacing(0)

        self.stacked = QStackedWidget()
        box.addWidget(self.stacked)

        self.created = False
        # Acá cacheo la última carpeta accedida
        self.__last_open_folder = None
        if CONFIG.get("lastOpenFolder") is not None:
            self.__last_open_folder = CONFIG.get("lastOpenFolder")
        self.__recent_dbs = []
        if CONFIG.get("recentFiles"):
            self.__recent_dbs = CONFIG.get("recentFiles")

        Pireal.load_service("central", self)

        esc_short = QShortcut(QKeySequence(Qt.Key_Escape), self)
        esc_short.activated.connect(self._hide_search)

    def _hide_search(self):
        query_container = self.get_active_db().query_container
        if query_container is not None:
            query_container.set_editor_focus()

    @property
    def recent_databases(self):
        return self.__recent_dbs

    @recent_databases.setter
    def recent_databases(self, database_file):
        recent_files = CONFIG.get("recentFiles")
        if database_file in recent_files:
            recent_files.remove(database_file)
        recent_files.insert(0, database_file)
        self.__recent_dbs = recent_files

    @property
    def last_open_folder(self):
        return self.__last_open_folder

    def rdb_to_pdb(self):
        from src.gui import rdb_pdb_tool
        dialog = rdb_pdb_tool.RDBPDBTool(self)
        dialog.exec_()

    def create_database(self):
        """Show a wizard widget to create a new database,
        only have one database open at time."""

        if self.created:
            return self.__say_about_one_db_at_time()
        dialog = new_database_dialog.NewDatabaseDialog(self)
        dialog.created.connect(self.__on_wizard_finished)
        dialog.show()

    def __on_wizard_finished(self, *data):
        """This slot execute when wizard to create a database is finished"""

        pireal = Pireal.get_service("pireal")
        if data:
            db_name, location, fname = data
            # Create a new data base container
            db_container = database_container.DatabaseContainer()
            # Associate the file name with the PFile object
            pfile_object = pfile.File(fname)
            # Associate PFile object with data base container
            # and add widget to stacked
            db_container.pfile = pfile_object
            self.add_widget(db_container)
            # Set window title
            pireal.change_title(file_manager.get_basename(fname))
            # Enable db actions
            pireal.set_enabled_db_actions(True)
            pireal.set_enabled_relation_actions(True)
            self.created = True
            logger.debug("La base de datos ha sido creada con éxito")

    def __say_about_one_db_at_time(self):
        logger.info("Una base de datos a la vez")
        QMessageBox.information(
            self, self.tr("Información"),
            self.tr("Una base de datos a la vez por "
                    "favor."))

    def open_database(self, filename='', remember=True):
        """ This function opens a database and set this on the UI """

        if self.created:
            return self.__say_about_one_db_at_time()

        # If not filename provide, then open dialog to select
        if not filename:
            if self.__last_open_folder is None:
                directory = os.path.expanduser("~")
            else:
                directory = self.__last_open_folder
            filter_ = settings.SUPPORTED_FILES.split(';;')[0]
            filename, _ = QFileDialog.getOpenFileName(
                self, self.tr("Abrir Base de "
                              "Datos"), directory, filter_)
            # If is canceled, return
            if not filename:
                return

        # If filename provide
        try:
            logger.debug("Intentando abrir el archivo {}".format(filename))
            # Read pdb file
            pfile_object = pfile.File(filename)
            db_data = pfile_object.read()
            # Create a dict to manipulate data more easy
            db_data = self.__sanitize_data(db_data)
        except Exception as reason:
            QMessageBox.information(self,
                                    self.tr("El archivo no se puede abrir"),
                                    reason.__str__())
            logger.debug("Error al abrir el archivo {0}: '{1}'".format(
                filename, reason.__str__()))
            return

        # Create a database container widget
        db_container = database_container.DatabaseContainer()

        try:
            db_container.create_database(db_data)
        except Exception as reason:
            QMessageBox.information(self, self.tr("Error"), str(reason))
            logger.debug("Error al crear la base de datos: {}".format(
                reason.__str__()))
            return

        # Set the PFile object to the new database
        db_container.pfile = pfile_object
        # Add data base container to stacked
        self.add_widget(db_container)
        # Database name
        db_name = file_manager.get_basename(filename)
        # Update title with the new database name, and enable some actions
        pireal = Pireal.get_service("pireal")
        self.databaseConected.emit(self.tr("Conectado a: {}".format(db_name)))
        pireal.set_enabled_db_actions(True)
        pireal.set_enabled_relation_actions(True)
        if remember:
            # Add to recent databases
            self.recent_databases = filename
            # Remember the folder
            self.__last_open_folder = file_manager.get_path(filename)
        self.created = True

    def open_query(self, filename='', remember=True):
        if not filename:
            if self.__last_open_folder is None:
                directory = os.path.expanduser("~")
            else:
                directory = self.__last_open_folder
            filter_ = settings.SUPPORTED_FILES.split(';;')[1]
            filename, _ = QFileDialog.getOpenFileName(
                self, self.tr("Abrir Consulta"), directory, filter_)
            if not filename:
                return
        # Si @filename no es False
        # Cacheo la carpeta accedida
        if remember:
            self.__last_open_folder = file_manager.get_path(filename)
        # FIXME: mejorar éste y new_query
        self.new_query(filename)

    def save_query(self, editor=None):
        db = self.get_active_db()
        fname = db.save_query(editor)
        if fname:
            self.querySaved.emit(self.tr(
                "Consulta guardada: {}".format(fname)))

    def save_query_as(self):
        pass

    def __sanitize_data(self, data):
        """
        Este método convierte el contenido de la base de datos a un
        diccionario para un mejor manejo despues
        """

        # FIXME: controlar cuando al final de la línea hay una coma
        from collections import defaultdict
        data_dict = defaultdict(list)
        for line_count, line in enumerate(data.splitlines()):
            # Ignore blank lines
            if not line.strip():
                continue
            if line.startswith("@"):
                # Header de una relación
                tpoint = line.find(":")
                if tpoint == -1:
                    raise Exception(
                        "Error de sintáxis en la línea {}".format(line_count +
                                                                  1))
                table_name, line = line.split(":")
                table_name = table_name[1:].strip()
                table_dict = {}
                table_dict["name"] = table_name
                table_dict["header"] = list(map(str.strip, line.split(",")))
                table_dict["tuples"] = set()
            else:
                # Tuplas de la relación
                for l in csv.reader([line]):
                    tupla = tuple(map(str.strip, l))
                    table_dict["tuples"].add(tupla)
            if not table_dict["tuples"]:
                data_dict["tables"].append(table_dict)
        return data_dict

    def remove_last_widget(self):
        """ Remove last widget from stacked """

        widget = self.stacked.widget(self.stacked.count() - 1)
        self.stacked.removeWidget(widget)

    def close_database(self):
        """ Close the database and return to the main widget """

        db = self.get_active_db()
        query_container = db.query_container

        if db.modified:
            msgbox = QMessageBox(self)
            msgbox.setIcon(QMessageBox.Question)
            msgbox.setWindowTitle(self.tr("Guardar cambios?"))
            msgbox.setText(
                self.tr("La base de datos <b>{}</b> ha sido "
                        "modificada.<br>Quiere guardar los "
                        "cambios?".format(db.dbname())))
            cancel_btn = msgbox.addButton(self.tr("Cancelar"),
                                          QMessageBox.RejectRole)
            msgbox.addButton(self.tr("No"), QMessageBox.NoRole)
            yes_btn = msgbox.addButton(self.tr("Si"), QMessageBox.YesRole)
            msgbox.exec_()
            r = msgbox.clickedButton()
            if r == cancel_btn:
                return
            if r == yes_btn:
                self.save_database()

        # Check if editor is modified
        query_widget = query_container.currentWidget()
        if query_widget is not None:
            weditor = query_widget.get_editor()
            if weditor is not None:
                # TODO: duplicate code, see tab widget
                if weditor.modified:
                    msgbox = QMessageBox(self)
                    msgbox.setIcon(QMessageBox.Question)
                    msgbox.setWindowTitle(self.tr("Archivo modificado"))
                    msgbox.setText(
                        self.tr("El archivo <b>{}</b> tiene cambios"
                                " no guardados. Quiere "
                                "mantenerlos?".format(weditor.name)))
                    cancel_btn = msgbox.addButton(self.tr("Cancelar"),
                                                  QMessageBox.RejectRole)
                    msgbox.addButton(self.tr("No"), QMessageBox.NoRole)
                    yes_btn = msgbox.addButton(self.tr("Si"),
                                               QMessageBox.YesRole)
                    msgbox.exec_()
                    r = msgbox.clickedButton()
                    if r == cancel_btn:
                        return
                    if r == yes_btn:
                        self.save_query(weditor)

        self.stacked.removeWidget(db)

        pireal = Pireal.get_service("pireal")
        pireal.set_enabled_db_actions(False)
        pireal.set_enabled_relation_actions(False)
        pireal.set_enabled_query_actions(False)
        pireal.set_enabled_editor_actions(False)
        pireal.change_title()  # Título en la ventana principal 'Pireal'
        self.created = False
        del db

    def new_query(self, filename=''):
        pireal = Pireal.get_service("pireal")
        db_container = self.get_active_db()
        db_container.new_query(filename)
        # Enable editor actions
        # FIXME: refactoring
        pireal.set_enabled_query_actions(True)
        zoom_in_action = Pireal.get_action("zoom_in")
        zoom_in_action.setEnabled(True)
        zoom_out_action = Pireal.get_action("zoom_out")
        zoom_out_action.setEnabled(True)
        paste_action = Pireal.get_action("paste_action")
        paste_action.setEnabled(True)
        comment_action = Pireal.get_action("comment")
        comment_action.setEnabled(True)
        uncomment_action = Pireal.get_action("uncomment")
        uncomment_action.setEnabled(True)
        search_action = Pireal.get_action("search")
        search_action.setEnabled(True)

    def execute_queries(self):
        db_container = self.get_active_db()
        db_container.execute_queries()

    def execute_selection(self):
        db_container = self.get_active_db()
        db_container.execute_selection()

    def save_database(self):

        db = self.get_active_db()
        if not db.modified:
            return

        # Get relations dict
        relations = db.table_widget.relations
        # Generate content
        content = file_manager.generate_database(relations)
        db.pfile.save(data=content)
        filename = db.pfile.filename
        # Emit signal
        self.databaseSaved.emit(
            self.tr("Base de datos guardada: {}".format(filename)))

        db.modified = False

    def save_database_as(self):
        filter = settings.SUPPORTED_FILES.split(';;')[0]
        filename, _ = QFileDialog.getSaveFileName(
            self, self.tr("Guardar Base de "
                          "Datos como..."), settings.PIREAL_DATABASES, filter)
        if not filename:
            return
        db = self.get_active_db()
        # Get relations
        relations = db.table_widget.relations
        # Content
        content = file_manager.generate_database(relations)
        # Si no se provee la extensión, le agrego
        if not os.path.splitext(filename)[1]:
            filename += '.pdb'
        db.pfile.save(content, filename)
        self.databaseSaved.emit(
            self.tr("Base de datos guardada: {}".format(db.pfile.filename)))

        db.modified = False

    def remove_relation(self):
        db = self.get_active_db()
        if db.delete_relation():
            db.modified = True

    def create_new_relation(self):
        def create_relation(relation, relation_name):
            db = self.get_active_db()
            lateral = Pireal.get_service("lateral_widget")
            table = db.create_table(relation, relation_name)
            db.table_widget.add_table(relation, relation_name, table)
            lateral.relation_list.add_item(relation_name,
                                           relation.cardinality(),
                                           relation.degree())
            db.modified = True

        dialog = new_relation_dialog.NewRelationDialog(self)
        dialog.created.connect(create_relation)
        dialog.show()

    def load_relation(self, filename=''):
        """ Load Relation file """

        if not filename:
            if self.__last_open_folder is None:
                directory = os.path.expanduser("~")
            else:
                directory = self.__last_open_folder

            msg = self.tr("Abrir Relación")
            filter_ = settings.SUPPORTED_FILES.split(';;')[-1]
            filenames = QFileDialog.getOpenFileNames(self, msg, directory,
                                                     filter_)[0]

            if not filenames:
                return

        # Save folder
        self.__last_open_folder = file_manager.get_path(filenames[0])
        db_container = self.get_active_db()
        if db_container.load_relation(filenames):
            db_container.modified = True

    def add_start_page(self):
        """ This function adds the Start Page to the stacked widget """

        sp = start_page.StartPage()
        self.add_widget(sp)

    def show_settings(self):
        """ Show settings dialog on stacked """

        # preferences_dialog = preferences.Preferences(self)

        # if isinstance(self.widget(1), preferences.Preferences):
        #     self.widget(1).close()
        # else:
        #     self.stacked.insertWidget(1, preferences_dialog)
        #     self.stacked.setCurrentIndex(1)

        # # Connect the closed signal
        # preferences_dialog.settingsClosed.connect(self._settings_closed)
        # TODO: para la próxima versión
        pass

    def widget(self, index):
        """ Returns the widget at the given index """

        return self.stacked.widget(index)

    def add_widget(self, widget):
        """ Appends and show the given widget to the Stacked """

        index = self.stacked.addWidget(widget)
        self.stacked.setCurrentIndex(index)

    def _settings_closed(self):
        self.stacked.removeWidget(self.widget(1))
        self.stacked.setCurrentWidget(self.stacked.currentWidget())

    def get_active_db(self):
        """ Return an instance of DatabaseContainer widget if the
        stacked contains an DatabaseContainer in last index or None if it's
        not an instance of DatabaseContainer """

        index = self.stacked.count() - 1
        widget = self.widget(index)
        if isinstance(widget, database_container.DatabaseContainer):
            return widget
        return None

    def get_unsaved_queries(self):
        query_container = self.get_active_db().query_container
        return query_container.get_unsaved_queries()

    def undo_action(self):
        query_container = self.get_active_db().query_container
        query_container.undo()

    def redo_action(self):
        query_container = self.get_active_db().query_container
        query_container.redo()

    def cut_action(self):
        query_container = self.get_active_db().query_container
        query_container.cut()

    def copy_action(self):
        query_container = self.get_active_db().query_container
        query_container.copy()

    def paste_action(self):
        query_container = self.get_active_db().query_container
        query_container.paste()

    def zoom_in(self):
        query_container = self.get_active_db().query_container
        query_container.zoom_in()

    def zoom_out(self):
        query_container = self.get_active_db().query_container
        query_container.zoom_out()

    def comment(self):
        query_container = self.get_active_db().query_container
        query_container.comment()

    def uncomment(self):
        query_container = self.get_active_db().query_container
        query_container.uncomment()

    def add_tuple(self):
        lateral = Pireal.get_service("lateral_widget")
        if lateral.relation_list.has_item() == 0:
            return
        # rname = lateral.relation_list.item_text(lateral.relation_list.row())
        rname = lateral.relation_list.current_text()
        from src.gui.dialogs.edit_relation_dialog import EditRelationDialog
        dialog = EditRelationDialog(rname, self)
        tw = self.get_active_db().table_widget
        dialog.sendData.connect(tw.insert_rows)
        dialog.show()

    def add_column(self):
        tw = self.get_active_db().table_widget
        tw.add_column()

    def delete_tuple(self):
        lateral = Pireal.get_service("lateral_widget")
        if lateral.relation_list.has_item() == 0:
            return
        r = QMessageBox.question(
            self, self.tr("Eliminar tupla/s"),
            self.tr("Seguro que quiere eliminar las tuplas seleccionadas?"),
            QMessageBox.Yes | QMessageBox.Cancel)
        if r == QMessageBox.Cancel:
            return
        tw = self.get_active_db().table_widget
        tw.delete_tuple()

    def delete_column(self):
        tw = self.get_active_db().table_widget
        tw.delete_column()

    def search(self):
        query_container = self.get_active_db().query_container
        query_container.search()
Exemplo n.º 17
0
class QWatson(QWidget, QWatsonImportMixin, QWatsonProjectMixin,
              QWatsonActivityMixin):
    def __init__(self, config_dir=None, parent=None):
        super(QWatson, self).__init__(parent)
        self.setWindowIcon(icons.get_icon('master'))
        self.setWindowTitle(__namever__)
        self.setMinimumWidth(300)
        self.setWindowFlags(Qt.Window | Qt.WindowMinimizeButtonHint
                            | Qt.WindowCloseButtonHint)

        if platform.system() == 'Windows':
            import ctypes
            myappid = __namever__
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                myappid)

        config_dir = (config_dir or os.environ.get('QWATSON_DIR')
                      or click.get_app_dir('QWatson'))

        self.client = Watson(config_dir=config_dir)
        self.model = WatsonTableModel(self.client)

        self.setup_activity_overview()
        self.setup()

        if self.client.is_started:
            self.add_new_project(self.client.current['project'])
            self.stop_watson(tags=['error'],
                             message="last session not closed correctly.")
        self.set_settings_from_index(-1)

    # ---- Setup layout

    def setup(self):
        """Setup the main widget."""

        # Setup the stack widget.

        self.stackwidget = QStackedWidget()

        self.setup_activity_tracker()
        self.setup_datetime_input_dialog()
        self.setup_close_dialog()
        self.setup_del_project_dialog()
        self.setup_merge_project_dialog()
        self.setup_import_dialog()

        # Setup the main layout of the widget

        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.stackwidget)

    def setup_close_dialog(self):
        """
        Setup a dialog that is shown when closing QWatson while and activity
        is being tracked.
        """
        self.close_dial = CloseDialog(parent=self)
        self.close_dial.register_dialog_to(self)

    def setup_datetime_input_dialog(self):
        """
        Setup the dialog to ask the user to enter a datetime value for
        the starting time of the activity.
        """
        self.datetime_input_dial = DateTimeInputDialog(parent=self)
        self.datetime_input_dial.register_dialog_to(self)

    # ---- Main interface

    def setup_activity_tracker(self):
        """Setup the widget used to start, track, and stop new activity."""
        stopwatch = self.setup_stopwatch()
        managers = self.setup_watson_managers()
        statusbar = self.setup_statusbar()

        tracker = QWidget()
        layout = QVBoxLayout(tracker)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(stopwatch)
        layout.addWidget(managers)
        layout.addWidget(statusbar)
        layout.setStretch(1, 100)

        self.stackwidget.addWidget(tracker)

    # ---- Project, Tags and Comment

    def setup_watson_managers(self):
        """
        Setup the embedded dialog to setup the current activity parameters.
        """
        project_manager = self.setup_project_manager()

        self.tag_manager = TagLineEdit()
        self.tag_manager.setPlaceholderText("Tags (comma separated)")

        self.comment_manager = QLineEdit()
        self.comment_manager.setPlaceholderText("Comment")

        # ---- Setup the layout

        managers = ColoredFrame('light')

        layout = QGridLayout(managers)
        layout.setContentsMargins(5, 5, 5, 5)

        layout.addWidget(project_manager, 0, 1)
        layout.addWidget(self.tag_manager, 1, 1)
        layout.addWidget(self.comment_manager, 2, 1)

        layout.addWidget(QLabel('project :'), 0, 0)
        layout.addWidget(QLabel('tags :'), 1, 0)
        layout.addWidget(QLabel('comment :'), 2, 0)

        return managers

    def set_settings_from_index(self, index):
        """
        Load the settings in the manager from the data of the frame saved
        at index.
        """
        if index is not None:
            try:
                frame = self.client.frames[index]
                self.project_manager.blockSignals(True)
                self.project_manager.setCurrentProject(frame.project)
                self.project_manager.blockSignals(False)

                self.tag_manager.blockSignals(True)
                self.tag_manager.set_tags(frame.tags)
                self.tag_manager.blockSignals(False)

                self.comment_manager.blockSignals(True)
                self.comment_manager.setText(frame.message)
                self.comment_manager.blockSignals(False)
            except IndexError:
                print("IndexError: list index out of range")

    # ---- Bottom Toolbar

    def setup_statusbar(self):
        """Setup the toolbar located at the bottom of the main widget."""
        self.btn_report = QToolButtonSmall('note')
        self.btn_report.clicked.connect(self.overview_widg.show)
        self.btn_report.setToolTip("<b>Activity Overview</b><br><br>"
                                   "Open the activity overview window.")

        self.round_time_btn = DropDownToolButton(style='text_only')
        self.round_time_btn.addItems(list(ROUNDMIN.keys()))
        self.round_time_btn.setCurrentIndex(1)
        self.round_time_btn.setToolTip(
            "<b>Round Start and Stop</b><br><br>"
            "Round start and stop times to the nearest"
            " multiple of the selected factor.")

        self.btn_startfrom = DropDownToolButton(style='text_only')
        self.btn_startfrom.addItems(
            ['start from now', 'start from last', 'start from other'])
        self.btn_startfrom.setCurrentIndex(0)
        self.btn_startfrom.setToolTip(
            "<b>Start From</b><br><br>"
            "Set whether the current activity starts"
            " from the current time (now),"
            " from the stop time of the last logged activity (last),"
            " or from a user defined time (other).")

        # Setup the layout of the statusbar

        statusbar = ToolBarWidget('window')
        statusbar.setSpacing(0)
        statusbar.addWidget(self.round_time_btn)
        statusbar.addWidget(self.btn_startfrom)
        statusbar.addStretch(100)
        statusbar.addWidget(self.btn_report)
        statusbar.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred))

        return statusbar

    def roundTo(self):
        """
        Return the start and stop rounding time factor, in minutes, that
        corresponds to the option selected in the round_time_btn.
        """
        return ROUNDMIN[self.round_time_btn.text()]

    def startFrom(self):
        """
        Return the mode to use to determine at what reference time the activity
        must refer to calculate its elapsed time.
        """
        return STARTFROM[self.btn_startfrom.text()]

    # ---- Stackwidget handlers

    def addWidget(self, widget):
        """
        Add a widget to the stackwidget and return the index where the
        widget was added.
        """
        self.stackwidget.addWidget(widget)
        return self.stackwidget.count() - 1

    def removeWidget(self, widget):
        """Remove a widget from the stackwidget."""
        self.stackwidget.removeWidget(widget)

    def currentIndex(self):
        """Return the current index of the stackwidget."""
        return self.stackwidget.currentIndex()

    def setCurrentIndex(self, index):
        """Set the current index of the stackwidget."""
        self.stackwidget.setCurrentIndex(index)

    # ---- Stop, Start, and Cancel

    def setup_stopwatch(self):
        """
        Setup the widget that contains a button to start/stop Watson and a
        digital clock that shows the elapsed amount of time since Watson
        was started.
        """
        self.stopwatch = StopWatchWidget()
        self.stopwatch.sig_btn_start_clicked.connect(self.start_watson)
        self.stopwatch.sig_btn_stop_clicked.connect(self.stop_watson)
        self.stopwatch.sig_btn_cancel_clicked.connect(self.cancel_watson)

        return self.stopwatch

    def start_watson(self, start_time=None):
        """Start monitoring a new activity with the Watson client."""
        if isinstance(start_time, arrow.Arrow):
            self.btn_startfrom.setEnabled(False)
            self.stopwatch.start(start_time)
            self.client.start(self.currentProject())
            self.client._current['start'] = start_time
        else:
            frames = self.client.frames
            if self.startFrom() == 'now':
                self.start_watson(arrow.now())
            elif self.startFrom() == 'last' and len(frames) > 0:
                self.start_watson(min(frames[-1].stop, arrow.now()))
            else:
                self.datetime_input_dial.show()

    def cancel_watson(self):
        """Cancel the Watson client if it is running and reset the UI."""
        self.btn_startfrom.setEnabled(True)
        self.stopwatch.cancel()
        if self.client.is_started:
            self.client.cancel()

    def stop_watson(self,
                    message=None,
                    project=None,
                    tags=None,
                    round_to=None):
        """Stop Watson and update the table model."""
        self.btn_startfrom.setEnabled(True)
        self.stopwatch.stop()

        self.client._current['message'] = \
            self.comment_manager.text() if message is None else message
        self.client._current['project'] = \
            self.currentProject() if project is None else project
        self.client._current['tags'] = \
            self.tag_manager.tags if tags is None else tags

        self.model.beginInsertRows(QModelIndex(), len(self.client.frames),
                                   len(self.client.frames))
        self.client.stop()

        # Round the start and stop times of the last added frame.
        round_frame_at(self.client, -1,
                       self.roundTo() if round_to is None else round_to)

        self.client.save()
        self.model.endInsertRows()

    def closeEvent(self, event):
        """Qt method override."""
        if self.client.is_started:
            self.close_dial.show()
            event.ignore()
        else:
            self.overview_widg.close()
            self.client.save()
            event.accept()
            print("QWatson is closed.\n")
Exemplo n.º 18
0
class MainWindow(QMainWindow):
    def __init__(self, resolution: QRect, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setWindowTitle("")
        self.screens_config = []
        self.current_screen = 0
        self.sub_screens = {}
        self.button = {}
        self.subscreen_stacked_widget = QStackedWidget()
        self.number_of_subs = 0
        self.main_stack_widget = QStackedWidget()
        self.main_widget = QWidget()
        self.main_layout = QGridLayout()
        self.gui_element_builder = GuiElementsBuilder()
        self.gui_button_builder = GuiButtonBuilder()
        self.gui_subscreen_builder = GuiSubscreenBuilder()
        self.resolution = resolution
        self.alarm_observer = AlarmObserver(self)
        self.alarm_widget = Alarm(self.alarm_observer, "Alarm_Widget",
                                  "#550055", self)

    def set_current_subscreen(self):
        source_button = self.sender()
        for i in range(0, self.number_of_subs):
            # print(source_button.text(), self.subscreen_stacked_widget.widget(i).get_name())
            if self.subscreen_stacked_widget.widget(
                    i).get_name() == source_button.text():
                self.subscreen_stacked_widget.setCurrentIndex(i)

    def toggle_main_widget(self, index: int):
        max_value = self.main_stack_widget.count()
        self.main_stack_widget.setCurrentIndex(index % max_value)

    def close_main_window(self):
        self.close()

    def update_from_subscreen(self, msg: dict) -> None:
        print(msg)
        self.alarm_widget.set_alarm_text(msg)
        self.toggle_main_widget(1)

    def init_with_config(self, config: dict):
        self.screens_config = config

        # Set Title
        title = str(config['main']['name'])
        self.setWindowTitle(title)
        # Set window flag
        flags = Qt.CustomizeWindowHint  # Small Frame
        # flags = Qt.FramelessWindowHint # No Frame
        self.setWindowFlags(flags)

        # Set Resolution ######################################################
        # via config
        window_width = config['main']["resolution"][0]
        window_height = config['main']["resolution"][1]
        # via given screen geometry
        # window_width = self.resolution.width()
        # window_height = self.resolution.height()
        # Set Resolution End ##################################################

        button_width = config['main']["button-size"][0]
        button_height = config['main']["button-size"][1]

        front_color = config['main']["front_color"]
        background_color = config['main']["background_color"]
        font = config['main']["font"]

        self.number_of_subs = len(config['sub'])
        self.gui_element_builder.set_font(font)

        self.setFixedSize(window_width, window_height)

        self.main_widget.setLayout(self.main_layout)
        self.main_widget.setStyleSheet("background-color:" + background_color)
        vbox_menu = QVBoxLayout()
        vbox_menu.setSizeConstraint(QLayout.SetFixedSize)

        vbox_menu.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.TOP_LEFT_SHORT,
                                                    100, 191, front_color))
        button_list_widget = QListWidget()
        vbox_menu.addWidget(button_list_widget)
        vbox_menu.addWidget(
            self.gui_element_builder.get_svg_widget(
                Gui_Element.BOTTOM_LEFT_SHORT, 100, 191, front_color))

        # Header #################################################################
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.BUTTON, 33,
                                                    712, front_color), 0, 1, 1,
            1, Qt.AlignTop)
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.BUTTON, 33, 52,
                                                    front_color), 0, 3,
            Qt.AlignTop)
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.END_RIGHT, 33,
                                                    33, front_color), 0, 4,
            Qt.AlignTop)
        # Header - END ###########################################################

        # Menu
        self.main_layout.addLayout(vbox_menu, 0, 0, 4, 1)
        # Central Window
        self.main_layout.addWidget(self.subscreen_stacked_widget, 1, 1, 2, 4)
        # Footer #################################################################
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.BUTTON, 33,
                                                    712, front_color), 3, 1,
            Qt.AlignBottom)
        # Add Exit Button
        exit_button = QPushButton("EXIT")
        exit_button.setFont(QFont(font, 20, QFont.Bold))
        exit_button.setFixedSize(52, 33)
        exit_button.setStyleSheet("background:#ff0000; border:1px solid " +
                                  front_color + ";")
        exit_button.clicked.connect(lambda state: self.close())

        self.main_layout.addWidget(exit_button, 3, 3, Qt.AlignBottom)
        self.main_layout.addWidget(
            self.gui_element_builder.get_svg_widget(Gui_Element.END_RIGHT, 33,
                                                    33, front_color, font), 3,
            4, Qt.AlignBottom)
        # Footer - END ###########################################################

        # button_ListWidget.setVerticalScrolllayout(QAbstractItemView.ScrollMode.ScrollPerItem)
        button_list_widget.setStyleSheet("QListWidget{background:" +
                                         background_color +
                                         ";  border: 0px solid " +
                                         front_color + ";}")

        # Erstellen der linken Button-Leiste ##############
        button_width = button_width * window_width / 100
        button_height = button_height * window_height / 100
        button_size = QSize(button_width, button_height)
        for i in range(0, self.number_of_subs):
            sub_button_list_item = QListWidgetItem(button_list_widget)
            placeholder_list_item = QListWidgetItem(button_list_widget)
            placeholder_list_item.setSizeHint(QSize(button_width, 4))
            placeholder_list_item.setBackground(QColor(background_color))

            flag = placeholder_list_item.flags() & Qt.ItemIsUserCheckable
            placeholder_list_item.setFlags(flag)
            # Widgets ##################################################################################################
            self.subscreen_stacked_widget.insertWidget(
                i,
                self.gui_subscreen_builder.init_with_config(
                    self.screens_config['sub'][i], self.alarm_observer))

            # Buttons ##################################################################################################
            self.gui_button_builder.set_color(
                self.screens_config['sub'][i]["Background"])
            self.gui_button_builder.set_size(button_height, button_width)
            self.button[i] = self.gui_button_builder.create_button(
                self.screens_config["sub"][i]["name"], Gui_Element.BUTTON_TEXT)

            sub_button_list_item.setSizeHint(button_size)
            button_list_widget.addItem(placeholder_list_item)
            button_list_widget.addItem(sub_button_list_item)
            button_list_widget.setItemWidget(sub_button_list_item,
                                             self.button[i])
            # signals ##################################################################################################
            self.button[i].clicked.connect(
                lambda widget=self.subscreen_stacked_widget.widget(
                    i): self.set_current_subscreen())

        # button_list_widget.setMaximumWidth(1000)
        button_list_widget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        button_list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        button_list_widget.setMaximumWidth(
            button_list_widget.sizeHintForColumn(0))

        #############################################
        self.subscreen_stacked_widget.setCurrentIndex(0)
        self.main_stack_widget.insertWidget(0, self.main_widget)
        self.main_stack_widget.insertWidget(1, self.alarm_widget)
        self.main_stack_widget.setCurrentIndex(0)
        self.setCentralWidget(self.main_stack_widget)
Exemplo n.º 19
0
class LabelStack(QWidget):
    """Manager of the label stack

    Attributes
    ----------
    children : list
        Widgets created directly under in the 'hierarchy'
    mode_teste : bool
        Control of testing mode
    stack : QStackedWidget
        Stack of widgets of each type of label

    Methods
    -------
    display(self, i)
        Changes currently displayed label widget
    """
    def __init__(self, mode_teste):
        super().__init__()
        self.children = []
        self.mode_teste = mode_teste

        main_layout = QVBoxLayout()

        # ----------

        label = QLabel()
        label.setText("Choisir le type d'etiquette")
        label.setAlignment(Qt.AlignCenter)

        stack_options = QListWidget()
        stack_options.insertItem(0, 'Mondial Relay')
        stack_options.insertItem(1, 'Lettre suivie')
        stack_options.insertItem(2, 'Colissimo')

        vbox = QVBoxLayout()
        vbox.addWidget(label)
        vbox.addWidget(stack_options)

        # ----------

        stack_mondial = MondialRelay(self.mode_teste)
        stack_lettre = LettreSuivie(self.mode_teste)
        stack_colissimo = Colissimo(self.mode_teste)

        self.stack = QStackedWidget()
        self.stack.addWidget(stack_mondial)
        self.stack.addWidget(stack_lettre)
        self.stack.addWidget(stack_colissimo)

        for i in range(self.stack.count()):
            self.children.append(self.stack.widget(i))

        # ----------

        hbox = QHBoxLayout()
        hbox.addLayout(vbox)
        hbox.addWidget(self.stack)
        main_layout.addLayout(hbox)

        # ----------

        self.setLayout(main_layout)
        stack_options.currentRowChanged.connect(self.display)

    # ----------- Actions ---------------------

    def display(self, i):
        self.stack.setCurrentIndex(i)
Exemplo n.º 20
0
class QueryWidget(QWidget):
    editorModified = pyqtSignal(bool)

    def __init__(self):
        super(QueryWidget, self).__init__()
        box = QVBoxLayout(self)
        box.setContentsMargins(0, 0, 0, 0)

        self._vsplitter = QSplitter(Qt.Vertical)
        self._hsplitter = QSplitter(Qt.Horizontal)

        self._result_list = lateral_widget.LateralWidget()
        self._result_list.header().hide()
        self._hsplitter.addWidget(self._result_list)

        self._stack_tables = QStackedWidget()
        self._hsplitter.addWidget(self._stack_tables)

        self.relations = {}

        self._query_editor = editor.Editor()
        # Editor connections
        self._query_editor.customContextMenuRequested.connect(
            self.__show_context_menu)
        self._query_editor.modificationChanged[bool].connect(
            self.__editor_modified)
        self._query_editor.undoAvailable[bool].connect(
            self.__on_undo_available)
        self._query_editor.redoAvailable[bool].connect(
            self.__on_redo_available)
        self._query_editor.copyAvailable[bool].connect(
            self.__on_copy_available)
        self._vsplitter.addWidget(self._query_editor)

        self._vsplitter.addWidget(self._hsplitter)
        box.addWidget(self._vsplitter)

        # Connections
        self._result_list.itemClicked.connect(
            lambda index: self._stack_tables.setCurrentIndex(
                self._result_list.row()))
        self._result_list.itemDoubleClicked.connect(
            self.show_relation)

    def __show_context_menu(self, point):
        popup_menu = self._query_editor.createStandardContextMenu()

        undock_editor = QAction(self.tr("Undock"), self)
        popup_menu.insertAction(popup_menu.actions()[0],
                                undock_editor)
        popup_menu.insertSeparator(popup_menu.actions()[1])
        undock_editor.triggered.connect(self.__undock_editor)

        popup_menu.exec_(self.mapToGlobal(point))

    def __undock_editor(self):
        new_editor = editor.Editor()
        actual_doc = self._query_editor.document()
        new_editor.setDocument(actual_doc)
        new_editor.resize(900, 400)
        # Set text cursor
        tc = self._query_editor.textCursor()
        new_editor.setTextCursor(tc)
        # Set title
        db = Pireal.get_service("central").get_active_db()
        qc = db.query_container
        new_editor.setWindowTitle(qc.tab_text(qc.current_index()))
        new_editor.show()

    def __on_undo_available(self, value):
        """ Change state of undo action """

        pireal = Pireal.get_service("pireal")
        action = pireal.get_action("undo_action")
        action.setEnabled(value)

    def __on_redo_available(self, value):
        """ Change state of redo action """

        pireal = Pireal.get_service("pireal")
        action = pireal.get_action("redo_action")
        action.setEnabled(value)

    def __on_copy_available(self, value):
        """ Change states of cut and copy action """

        cut_action = Pireal.get_action("cut_action")
        cut_action.setEnabled(value)
        copy_action = Pireal.get_action("copy_action")
        copy_action.setEnabled(value)

    def show_relation(self, item):
        central_widget = Pireal.get_service("central")
        table_widget = central_widget.get_active_db().table_widget
        rela = self.relations[item.name]
        dialog = QDialog(self)
        dialog.resize(700, 500)
        dialog.setWindowTitle(item.name)
        box = QVBoxLayout(dialog)
        box.setContentsMargins(5, 5, 5, 5)
        table = table_widget.create_table(rela)
        box.addWidget(table)
        hbox = QHBoxLayout()
        btn = QPushButton(self.tr("Ok"))
        btn.clicked.connect(dialog.close)
        hbox.addStretch()
        hbox.addWidget(btn)
        box.addLayout(hbox)
        dialog.show()

    def save_sizes(self):
        """ Save sizes of Splitters """

        qsettings = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat)
        qsettings.setValue('hsplitter_query_sizes',
                           self._hsplitter.saveState())
        qsettings.setValue('vsplitter_query_sizes',
                           self._vsplitter.saveState())

    def get_editor(self):
        return self._query_editor

    def __editor_modified(self, modified):
        self.editorModified.emit(modified)

    def showEvent(self, event):
        super(QueryWidget, self).showEvent(event)
        self._hsplitter.setSizes([1, self.width() / 3])

    def clear_results(self):
        self._result_list.clear_items()
        i = self._stack_tables.count()
        while i >= 0:
            widget = self._stack_tables.widget(i)
            self._stack_tables.removeWidget(widget)
            if widget is not None:
                widget.deleteLater()
            i -= 1

    def add_table(self, rela, rname):
        wtable = custom_table.Table()
        # Model
        model = QStandardItemModel()
        wtable.setModel(model)
        model.setHorizontalHeaderLabels(rela.header)

        for data in rela.content:
            nrow = model.rowCount()
            # wtable.insertRow(nrow)
            for col, text in enumerate(data):
                item = QStandardItem(text)
                item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                model.setItem(nrow, col, item)

        index = self._stack_tables.addWidget(wtable)
        self._stack_tables.setCurrentIndex(index)

        self._result_list.add_item(rname, rela.count())
Exemplo n.º 21
0
class PageManagerView(QFrame):

    def __init__(self, parent=None):
        super().__init__(parent)

        self.switch = QStackedWidget(self)
        label_view_page = self.add_page(LabelView())
        send_email_page = self.add_page(SendEmailPageView())
        contacts_page = self.add_page(ContactsPageView())
        settings_page = self.add_page(OptionsPageView())
        email_viewer_page = self.add_page(EmailViewerPageView())

        self.sidebar = Sidebar(self)

        personal_wid = self.sidebar.add_item('Personal', ':images/personal_image.png')
        social_wid = self.sidebar.add_item('Social', ':images/social_image.png')
        updates_wid = self.sidebar.add_item('Updates', ':images/updates_image.png')
        promotions_wid = self.sidebar.add_item('Promotions', ':images/promotions_image.png')
        forums_wid = self.sidebar.add_item('Forums', ':images/forums_image.png')
        categories_label_ids = [
            GMAIL_LABEL_PERSONAL, GMAIL_LABEL_SOCIAL, GMAIL_LABEL_UPDATES,
            GMAIL_LABEL_PROMOTIONS, GMAIL_LABEL_FORUMS
        ]

        group_id = self.sidebar.add_group('Other Labels')
        sent_wid = self.sidebar.add_item_to_group(group_id, 'Sent', ':images/sent_image.png')
        unread_wid = self.sidebar.add_item_to_group(group_id, 'Unread', ':images/unread_image.png')
        important_wid = self.sidebar.add_item_to_group(group_id, 'Important', ':images/important_image.png')
        starred_wid = self.sidebar.add_item_to_group(group_id, 'Starred', ':images/starred_image.png')
        trash_wid = self.sidebar.add_item_to_group(group_id, 'Trash', ':images/trash_image.png')
        spam_wid = self.sidebar.add_item_to_group(group_id, 'Spam', ':images/spam_image.png')
        other_labels_label_ids = [
            GMAIL_LABEL_SENT, GMAIL_LABEL_UNREAD, GMAIL_LABEL_IMPORTANT,
            GMAIL_LABEL_STARRED, GMAIL_LABEL_TRASH, GMAIL_LABEL_SPAM
        ]
        self.item_id_to_label_id = {}
        self.label_id_to_item_id = {}

        send_email_wid = self.sidebar.add_item('Send Email', ':images/send_email_image.png')
        contacts_wid = self.sidebar.add_item('Contacts', ':images/contacts_image.png')
        self.sidebar.add_stretch()
        settings_wid = self.sidebar.add_item('Settings', ':images/options_button.png')

        def item_pressed_handler(widget_id, switch, item_id_to_label_id):
            if widget_id > settings_wid:
                # User defined label
                switch.setCurrentIndex(label_view_page)
                EmailEventChannel.publish(
                    'show_label', label_id=item_id_to_label_id[widget_id])
            elif personal_wid <= widget_id <= forums_wid:
                switch.setCurrentIndex(label_view_page)
                EmailEventChannel.publish('show_label', label_id=categories_label_ids[widget_id])
            elif sent_wid <= widget_id <= spam_wid:
                switch.setCurrentIndex(label_view_page)
                EmailEventChannel.publish(
                    'show_label', label_id=other_labels_label_ids[widget_id - group_id - 1])
            elif widget_id == send_email_wid:
                switch.setCurrentIndex(send_email_page)
            elif widget_id == contacts_wid:
                switch.setCurrentIndex(contacts_page)
            elif widget_id == settings_wid:
                switch.setCurrentIndex(settings_page)

        self.item_pressed_handler = item_pressed_handler
        self.sidebar.on_item_pressed.connect(self.handle_item_pressed)

        def index_changed_handler(page_index, sidebar):
            if page_index == send_email_page:
                sidebar.select_item(send_email_wid)
            elif page_index == contacts_page:
                sidebar.select_item(contacts_wid)
            elif page_index == settings_page:
                sidebar.select_item(settings_wid)
            elif page_index == email_viewer_page:
                sidebar.select_item(None)
            else:
                assert False

        self.index_changed_handler = index_changed_handler

        main_layout = QHBoxLayout()
        main_layout.addWidget(self.sidebar)
        main_layout.addWidget(self.switch)
        main_layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(main_layout)

        EmailEventChannel.subscribe(
            'email_response', lambda **kwargs: self.change_to_index(email_viewer_page))

        ContactEventChannel.subscribe(
            'contact_picked', lambda **kwargs: self.change_to_index(send_email_page))
        ContactEventChannel.subscribe(
            'pick_contact', lambda **kwargs: self.change_to_index(contacts_page))

        ShortcutEventChannel.subscribe('personal', lambda **kwargs: self.show_label(personal_wid))
        ShortcutEventChannel.subscribe('social', lambda **kwargs: self.show_label(social_wid))
        ShortcutEventChannel.subscribe('updates', lambda **kwargs: self.show_label(updates_wid))
        ShortcutEventChannel.subscribe('promotions', lambda **kwargs: self.show_label(promotions_wid))
        ShortcutEventChannel.subscribe('forums', lambda **kwargs: self.show_label(forums_wid))
        ShortcutEventChannel.subscribe('sent', lambda **kwargs: self.show_label(sent_wid))
        ShortcutEventChannel.subscribe('unread', lambda **kwargs: self.show_label(unread_wid))
        ShortcutEventChannel.subscribe('important', lambda **kwargs: self.show_label(important_wid))
        ShortcutEventChannel.subscribe('starred', lambda **kwargs: self.show_label(starred_wid))
        ShortcutEventChannel.subscribe('trash', lambda **kwargs: self.show_label(trash_wid))
        ShortcutEventChannel.subscribe( 'spam', lambda **kwargs: self.show_label(spam_wid))
        ShortcutEventChannel.subscribe('send_email', lambda **kwargs: self.show_label(send_email_wid))
        ShortcutEventChannel.subscribe('contacts', lambda **kwargs: self.show_label(contacts_wid))
        ShortcutEventChannel.subscribe('settings', lambda **kwargs: self.show_label(settings_wid))

        self.other_labels_group_id = group_id
        EmailEventChannel.subscribe('labels_sync', self.handle_labels_sync)

        EmailEventChannel.publish('labels_request')

    def handle_item_pressed(self, widget_id):
        self.item_pressed_handler(widget_id, self.switch, self.item_id_to_label_id)

    def add_page(self, page):
        self.switch.addWidget(page)
        return self.switch.count() - 1

    def add_page_switch_rule(self, page_idx, event_channel, topic):
        event_channel.subscribe(topic, lambda **kwargs: self.change_to_index(page_idx))

    def change_to_index(self, page_idx):
        self.switch.setCurrentIndex(page_idx)
        self.index_changed_handler(page_idx, self.sidebar)
    
    def show_label(self, widget_id):
        self.item_pressed_handler(widget_id, self.switch, self.item_id_to_label_id)
        self.sidebar.select_item(widget_id)

    def handle_labels_sync(self, labels, error=''):
        assert not error

        if 'all' in labels:
            for l in labels['all']:
                lbl_obj = Label(*l)
                if lbl_obj.type == USER_LABEL:
                    item_id = self.sidebar.add_item_to_group(
                        self.other_labels_group_id, lbl_obj.name, ':images/label_image.png')
                    self.item_id_to_label_id[item_id] = lbl_obj.id
                    self.label_id_to_item_id[lbl_obj.id] = item_id
        else:
            for l in labels['added']:
                lbl_obj = Label(*l)
                if lbl_obj.type == USER_LABEL:
                    item_id = self.sidebar.add_item_to_group(
                        self.other_labels_group_id, lbl_obj.name, ':images/label_image.png')
                    self.item_id_to_label_id[item_id] = lbl_obj.id
                    self.label_id_to_item_id[lbl_obj.id] = item_id
            for l in labels['modified']:
                lbl_obj = Label(*l)
                if lbl_obj.type == USER_LABEL:
                    item_id = self.label_id_to_item_id[lbl_obj.id]
                    self.sidebar.change_item_name(item_id, lbl_obj.name)
            for l in labels['deleted']:
                lbl_obj = Label(*l)
                if lbl_obj.type == USER_LABEL:
                    item_id = self.label_id_to_item_id[lbl_obj.id]
                    self.sidebar.remove_item(item_id, self.other_labels_group_id)
Exemplo n.º 22
0
class CentralWidget(QWidget):
    # This signals is used by notificator
    databaseSaved = pyqtSignal('QString')
    querySaved = pyqtSignal('QString')

    def __init__(self):
        QWidget.__init__(self)
        box = QVBoxLayout(self)
        box.setContentsMargins(0, 0, 0, 0)
        box.setSpacing(0)

        self.stacked = QStackedWidget()
        box.addWidget(self.stacked)

        self.created = False
        self.__last_open_folder = None
        self.__recent_dbs = []
        if PSetting.RECENT_DBS:
            self.__recent_dbs = PSetting.RECENT_DBS

        Pireal.load_service("central", self)

    @property
    def recent_databases(self):
        return self.__recent_dbs

    @recent_databases.setter
    def recent_databases(self, database_file):
        if database_file in PSetting.RECENT_DBS:
            PSetting.RECENT_DBS.remove(database_file)
        PSetting.RECENT_DBS.insert(0, database_file)
        self.__recent_dbs = PSetting.RECENT_DBS

    def create_database(self):
        """ Show a wizard widget to create a new database,
        only have one database open at time.
        """

        if self.created:
            QMessageBox.information(self,
                                    self.tr("Information"),
                                    self.tr("You may only have one database"
                                            " open at time."))
            DEBUG("Ya existe una base de datos abierta")
            return
        wizard = database_wizard.DatabaseWizard(self)
        wizard.wizardFinished.connect(
            self.__on_wizard_finished)
        # Hide menubar and toolbar
        pireal = Pireal.get_service("pireal")
        pireal.show_hide_menubar()
        pireal.show_hide_toolbar()
        # Add wizard widget to stacked
        self.add_widget(wizard)

    def __on_wizard_finished(self, data, wizard_widget):
        """ This slot execute when wizard to create a database is finished """

        pireal = Pireal.get_service("pireal")
        if not data:
            # If it's canceled, remove wizard widget and return to Start Page
            self.remove_last_widget()
        else:
            # Create a new data base container
            db_container = database_container.DatabaseContainer()
            # Associate the file name with the PFile object
            pfile_object = pfile.File(data['filename'])
            # Associate PFile object with data base container
            # and add widget to stacked
            db_container.pfile = pfile_object
            self.add_widget(db_container)
            # Remove wizard
            self.stacked.removeWidget(wizard_widget)
            # Set window title
            pireal.change_title(file_manager.get_basename(data['filename']))
            # Enable db actions
            pireal.set_enabled_db_actions(True)
            pireal.set_enabled_relation_actions(True)
            self.created = True
            DEBUG("Base de datos creada correctamente: '{}'".format(
                data['filename']))

        # If data or not, show menubar and toolbar again
        pireal.show_hide_menubar()
        pireal.show_hide_toolbar()

    def open_database(self, filename=''):
        """ This function opens a database and set this on the UI """

        # If not filename provide, then open dialog to select
        if self.created:
            QMessageBox.information(self,
                                    self.tr("Information"),
                                    self.tr("You may only have one database"
                                            " open at time."))
            DEBUG("Ya existe una base de datos abierta")
            return
        if not filename:
            if self.__last_open_folder is None:
                directory = os.path.expanduser("~")
            else:
                directory = self.__last_open_folder
            filter_ = settings.SUPPORTED_FILES.split(';;')[0]
            filename, _ = QFileDialog.getOpenFileName(self,
                                                      self.tr("Open Database"),
                                                      directory,
                                                      filter_)
            # If is canceled, return
            if not filename:
                return

            # Remember the folder
            self.__last_open_folder = file_manager.get_path(filename)

        DEBUG("Abriendo la base de datos: '{}'".format(filename))

        # If filename provide
        try:
            # Read pdb file
            pfile_object = pfile.File(filename)
            db_data = pfile_object.read()
            # Create a dict to manipulate data more easy
            db_data = self.__sanitize_data(db_data)
        except Exception as reason:
            QMessageBox.information(self,
                                    self.tr("The file couldn't be open"),
                                    str(reason))
            CRITICAL("Error al intentar abrir el archivo: {}".format(reason))
            return

        # Create a database container widget
        db_container = database_container.DatabaseContainer()

        try:
            db_container.create_database(db_data)
        except Exception as reason:
            QMessageBox.information(self,
                                    self.tr("Error"),
                                    str(reason))
            CRITICAL("Error al crear la base de datos: {}".format(reason))
            return

        # Set the PFile object to the new database
        db_container.pfile = pfile_object
        # Add data base container to stacked
        self.add_widget(db_container)
        # Database name
        db_name = file_manager.get_basename(filename)
        # Update title with the new database name, and enable some actions
        pireal = Pireal.get_service("pireal")
        pireal.change_title(db_name)
        pireal.set_enabled_db_actions(True)
        pireal.set_enabled_relation_actions(True)
        # Add to recent databases
        self.recent_databases = filename
        self.created = True

    def open_query(self):
        filter_ = settings.SUPPORTED_FILES.split(';;')[1]
        filename, _ = QFileDialog.getOpenFileName(self,
                                                  self.tr("Open Query"),
                                                  os.path.expanduser("~"),
                                                  filter_)
        if not filename:
            return
        # FIXME: mejorar éste y new_query
        self.new_query(filename)

    def save_query(self, editor=None):
        db = self.get_active_db()
        fname = db.save_query(editor)
        if fname:
            self.querySaved.emit(self.tr("Query saved: {}".format(fname)))

    def save_query_as(self):
        pass

    def __sanitize_data(self, data):
        """
        This function converts the data into a dictionary
        for better handling then.
        The argument 'data' is the content of the database.
        """

        # FIXME: controlar cuando al final de la línea hay una coma
        data_dict = {'tables': []}

        for line_count, line in enumerate(data.splitlines()):
            # Ignore blank lines
            if not line:
                continue
            if line.startswith('@'):
                # This line is a header
                tpoint = line.find(':')
                if tpoint == -1:
                    raise Exception("Invalid syntax at line {}".format(
                        line_count + 1))

                table_name, line = line.split(':')
                table_name = table_name[1:].strip()

                table_dict = {}
                table_dict['name'] = table_name
                table_dict['header'] = line.split(',')
                table_dict['tuples'] = []
            else:
                for l in csv.reader([line]):
                    # Remove spaces
                    l = list(map(str.strip, l))
                    # FIXME: this is necesary?
                    if table_dict['name'] == table_name:
                        table_dict['tuples'].append(l)
            if not table_dict['tuples']:
                data_dict['tables'].append(table_dict)

        return data_dict

    def remove_last_widget(self):
        """ Remove last widget from stacked """

        widget = self.stacked.widget(self.stacked.count() - 1)
        self.stacked.removeWidget(widget)

    def close_database(self):
        """ Close the database and return to the main widget """

        db = self.get_active_db()
        query_container = db.query_container

        if db.modified:
            msgbox = QMessageBox(self)
            msgbox.setIcon(QMessageBox.Question)
            msgbox.setWindowTitle(self.tr("Save Changes?"))
            msgbox.setText(self.tr("The <b>{}</b> database has ben"
                                   " modified.<br>Do you want save "
                                   "your changes?".format(
                                       db.dbname())))
            cancel_btn = msgbox.addButton(self.tr("Cancel"),
                                          QMessageBox.RejectRole)
            msgbox.addButton(self.tr("No"),
                             QMessageBox.NoRole)
            yes_btn = msgbox.addButton(self.tr("Yes"),
                                       QMessageBox.YesRole)
            msgbox.exec_()
            r = msgbox.clickedButton()
            if r == cancel_btn:
                return
            if r == yes_btn:
                self.save_database()

        # Check if editor is modified
        query_widget = query_container.currentWidget()
        if query_widget is not None:
            weditor = query_widget.get_editor()
            if weditor is not None:
                # TODO: duplicate code, see tab widget
                if weditor.modified:
                    msgbox = QMessageBox(self)
                    msgbox.setIcon(QMessageBox.Question)
                    msgbox.setWindowTitle(self.tr("File modified"))
                    msgbox.setText(self.tr("The file <b>{}</b> has unsaved "
                                           "changes. You want to keep "
                                           "them?".format(
                                               weditor.name)))
                    cancel_btn = msgbox.addButton(self.tr("Cancel"),
                                                  QMessageBox.RejectRole)
                    msgbox.addButton(self.tr("No"),
                                     QMessageBox.NoRole)
                    yes_btn = msgbox.addButton(self.tr("Yes"),
                                               QMessageBox.YesRole)
                    msgbox.exec_()
                    r = msgbox.clickedButton()
                    if r == cancel_btn:
                        return
                    if r == yes_btn:
                        self.save_query(weditor)

        self.stacked.removeWidget(db)

        pireal = Pireal.get_service("pireal")
        pireal.set_enabled_db_actions(False)
        pireal.set_enabled_relation_actions(False)
        pireal.set_enabled_query_actions(False)
        pireal.set_enabled_editor_actions(False)
        self.created = False
        DEBUG("Se cerró la base de datos: '{}'".format(db.dbname()))
        del db

    def new_query(self, filename=''):
        pireal = Pireal.get_service("pireal")
        db_container = self.get_active_db()
        db_container.new_query(filename)
        # Enable editor actions
        # FIXME: refactoring
        pireal.set_enabled_query_actions(True)
        zoom_in_action = Pireal.get_action("zoom_in")
        zoom_in_action.setEnabled(True)
        zoom_out_action = Pireal.get_action("zoom_out")
        zoom_out_action.setEnabled(True)
        paste_action = Pireal.get_action("paste_action")
        paste_action.setEnabled(True)
        comment_action = Pireal.get_action("comment")
        comment_action.setEnabled(True)
        uncomment_action = Pireal.get_action("uncomment")
        uncomment_action.setEnabled(True)

    def execute_queries(self):
        db_container = self.get_active_db()
        db_container.execute_queries()

    def execute_selection(self):
        db_container = self.get_active_db()
        db_container.execute_selection()

    def save_database(self):

        db = self.get_active_db()
        if not db.modified:
            return

        # Get relations dict
        relations = db.table_widget.relations
        # Generate content
        content = file_manager.generate_database(relations)
        db.pfile.save(content=content)
        filename = db.pfile.filename
        # Emit signal
        self.databaseSaved.emit(
            self.tr("Database saved: {}".format(filename)))

        db.modified = False

    def save_database_as(self):
        filter = settings.SUPPORTED_FILES.split(';;')[0]
        filename, _ = QFileDialog.getSaveFileName(self,
                                                  self.tr("Save Database As"),
                                                  settings.PIREAL_PROJECTS,
                                                  filter)
        if not filename:
            return
        db = self.get_active_db()
        # Get relations
        relations = db.table_widget.relations
        # Content
        content = file_manager.generate_database(relations)
        db.pfile.save(content, filename)
        self.databaseSaved.emit(
            self.tr("Database saved: {}".format(db.pfile.filename)))

        db.modified = False

    def remove_relation(self):
        db = self.get_active_db()
        if db.delete_relation():
            db.modified = True

    def create_new_relation(self):
        data = new_relation_dialog.create_relation()
        if data is not None:
            db = self.get_active_db()
            rela, rela_name = data
            # Add table
            db.table_widget.add_table(rela, rela_name)
            # Add item to lateral widget
            db.lateral_widget.add_item(rela_name, rela.count())
            # Set modified db
            db.modified = True

    def edit_relation(self):
        db = self.get_active_db()
        lateral = db.lateral_widget
        selected_items = lateral.selectedItems()
        if selected_items:
            selected_relation = selected_items[0].text(0)
            relation_text = selected_relation.split()[0].strip()
            rela = db.table_widget.relations[relation_text]
            data = edit_relation_dialog.edit_relation(rela)
            if data is not None:
                # Update table
                db.table_widget.update_table(data)
                # Update relation
                db.table_widget.relations[relation_text] = data
                # Set modified db
                db.modified = True
                lateral.update_item(data.count())

    def load_relation(self, filename=''):
        """ Load Relation file """

        if not filename:
            if self.__last_open_folder is None:
                directory = os.path.expanduser("~")
            else:
                directory = self.__last_open_folder

            msg = self.tr("Open Relation File")
            filter_ = settings.SUPPORTED_FILES.split(';;')[-1]
            filenames = QFileDialog.getOpenFileNames(self, msg, directory,
                                                     filter_)[0]

            if not filenames:
                return

        # Save folder
        self.__last_open_folder = file_manager.get_path(filenames[0])
        db_container = self.get_active_db()
        if db_container.load_relation(filenames):
            db_container.modified = True

    def add_start_page(self):
        """ This function adds the Start Page to the stacked widget """

        sp = start_page.StartPage()
        self.add_widget(sp)

    def show_settings(self):
        """ Show settings dialog on stacked """

        preferences_dialog = preferences.Preferences(self)

        if isinstance(self.widget(1), preferences.Preferences):
            self.widget(1).close()
        else:
            self.stacked.insertWidget(1, preferences_dialog)
            self.stacked.setCurrentIndex(1)

        # Connect the closed signal
        preferences_dialog.settingsClosed.connect(self._settings_closed)

    def widget(self, index):
        """ Returns the widget at the given index """

        return self.stacked.widget(index)

    def add_widget(self, widget):
        """ Appends and show the given widget to the Stacked """

        index = self.stacked.addWidget(widget)
        self.stacked.setCurrentIndex(index)

    def _settings_closed(self):
        self.stacked.removeWidget(self.widget(1))
        self.stacked.setCurrentWidget(self.stacked.currentWidget())

    def get_active_db(self):
        """ Return an instance of DatabaseContainer widget if the
        stacked contains an DatabaseContainer in last index or None if it's
        not an instance of DatabaseContainer """

        index = self.stacked.count() - 1
        widget = self.widget(index)
        if isinstance(widget, database_container.DatabaseContainer):
            return widget
        return None

    def get_unsaved_queries(self):
        query_container = self.get_active_db().query_container
        return query_container.get_unsaved_queries()

    def undo_action(self):
        query_container = self.get_active_db().query_container
        query_container.undo()

    def redo_action(self):
        query_container = self.get_active_db().query_container
        query_container.redo()

    def cut_action(self):
        query_container = self.get_active_db().query_container
        query_container.cut()

    def copy_action(self):
        query_container = self.get_active_db().query_container
        query_container.copy()

    def paste_action(self):
        query_container = self.get_active_db().query_container
        query_container.paste()

    def zoom_in(self):
        query_container = self.get_active_db().query_container
        query_container.zoom_in()

    def zoom_out(self):
        query_container = self.get_active_db().query_container
        query_container.zoom_out()

    def comment(self):
        query_container = self.get_active_db().query_container
        query_container.comment()

    def uncomment(self):
        query_container = self.get_active_db().query_container
        query_container.uncomment()
Exemplo n.º 23
0
class MainWindow(QWidget):
    def __init__(self, title="Raven"):
        super().__init__()
        # self.window_width = 350
        # self.window_height = 600
        # self.setFixedSize(self.window_width, self.window_height)
        self.showMaximized()
        self.setWindowTitle(title)
        self.init_ui()

    def init_ui(self):
        bottom_bar = BottomWidget(self)
        bottom_bar.home_click_listener.connect(self.on_home_click)
        bottom_bar.logout_click_listener.connect(self.on_logout_click)

        self.fragment = QStackedWidget()

        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.fragment)

        bottom_bar.on_back_press_listener.connect(self.on_back_press)
        layout.addWidget(bottom_bar)

        self.setLayout(layout)
        # self.add_screen(LoginWidget("Login Screen"))
        # self.add_screen(CameraWidget())
        self.add_screen(AttendenceOptionWidget())

    def add_screen(self, screen: base_widget):
        screen.add_screen_listener.connect(self.add_screen)
        screen.on_back_press_listener.connect(self.on_back_press)

        self.fragment.addWidget(screen)
        self.fragment.setCurrentIndex(self.fragment.count() - 1)
        self.updateUi()

    def on_back_press(self):
        if self.fragment.count() > 1:
            widget = self.fragment.currentWidget()
            self.fragment.removeWidget(widget)
            widget.deleteLater()
            self.updateUi()

    def updateUi(self):
        widget: base_widget = self.fragment.currentWidget()
        self.setWindowTitle(widget.title)

    def on_home_click(self):
        self.remove_all_screen()

    def remove_all_screen(self):
        while self.fragment.count() > 1:
            widget = self.fragment.currentWidget()
            self.fragment.removeWidget(widget)
            widget.deleteLater()
            self.updateUi()

    def on_logout_click(self):
        print("On Logout Click")
Exemplo n.º 24
0
class AppSettings(QDialog):

    SettingsWidgets = []

    def __init__(self, conf, **kwargs):
        super().__init__(**kwargs)

        self.conf = conf
        self.setWindowTitle(translate('AppSettings', 'LiSP preferences'))

        self.setWindowModality(QtCore.Qt.ApplicationModal)
        self.setMaximumSize(635, 530)
        self.setMinimumSize(635, 530)
        self.resize(635, 530)

        self.listWidget = QListWidget(self)
        self.listWidget.setGeometry(QtCore.QRect(5, 10, 185, 470))

        self.sections = QStackedWidget(self)
        self.sections.setGeometry(QtCore.QRect(200, 10, 430, 470))

        for widget in self.SettingsWidgets:
            widget = widget(parent=self)
            widget.resize(430, 465)
            widget.load_settings(self.conf)

            self.listWidget.addItem(translate('SettingsPageName', widget.Name))
            self.sections.addWidget(widget)

        if self.SettingsWidgets:
            self.listWidget.setCurrentRow(0)

        self.listWidget.currentItemChanged.connect(self._change_page)

        self.dialogButtons = QDialogButtonBox(self)
        self.dialogButtons.setGeometry(10, 495, 615, 30)
        self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel
                                              | QDialogButtonBox.Ok)

        self.dialogButtons.rejected.connect(self.reject)
        self.dialogButtons.accepted.connect(self.accept)

    def get_configuraton(self):
        conf = {}

        for n in range(self.sections.count()):
            widget = self.sections.widget(n)
            newconf = widget.get_settings()
            deep_update(conf, newconf)

        return conf

    @classmethod
    def register_settings_widget(cls, widget):
        if widget not in cls.SettingsWidgets:
            cls.SettingsWidgets.append(widget)

    @classmethod
    def unregister_settings_widget(cls, widget):
        if widget in cls.SettingsWidgets:
            cls.SettingsWidgets.remove(widget)

    def _change_page(self, current, previous):
        if not current:
            current = previous

        self.sections.setCurrentIndex(self.listWidget.row(current))
Exemplo n.º 25
0
class AccountCreationPresenter(QWidget):
    """
    Handles data-validation, saving, and transitions between frames
    """

    should_load_main_app = pyqtSignal(Account)  # Signal to reload LandingWindow

    def __init__(self, parent: QObject):
        super().__init__(parent)

        self.__layout_manager = QVBoxLayout(self)
        self.__slide_stack = QStackedWidget()  # Stores the various frames for creating an account
        self.__proceed_btn = QPushButton("Get Started")  # Changes to say next or finish, slide-depending

        # Connect events
        self.__proceed_btn.clicked.connect(self.__proceed_btn_was_clicked)
        self.__setup_ui()

    @QtCore.pyqtSlot()
    def __proceed_btn_was_clicked(self):
        curr_idx = self.__slide_stack.currentIndex()

        if curr_idx == 0 and self.__slide_stack.isHidden():
            # Get Started, must show
            self.__slide_stack.show()
            self.__proceed_btn.setText("Next")
        elif self.__slide_stack.currentWidget().is_valid():
            if curr_idx + 1 == self.__slide_stack.count() - 1:
                # Second to last slide\
                self.__proceed_btn.setText("Finish")
                self.__slide_stack.setCurrentIndex(curr_idx + 1)
            elif curr_idx == self.__slide_stack.count() - 1:
                # Last slide

                # Save account data to global
                account = Account()
                for i in range(self.__slide_stack.count()):
                    self.__slide_stack.widget(i).fill_account_details(account)
                write_to_data_file(DataType.USER, FileName.GLOBAL, account, False)

                # Port-forward, if approved
                self.hide()
                self.should_load_main_app.emit(account)

            else:
                self.__slide_stack.setCurrentIndex(curr_idx + 1)

    def __setup_ui(self):

        # Create frames and populate stack
        self.__slide_stack.addWidget(AccountDetailsFrame(self.__slide_stack))
        self.__slide_stack.addWidget(NetworkDetailsFrame(self.__slide_stack))
        self.__slide_stack.hide()

        # Set up welcome top-half
        content_frame = QFrame()
        content_frame.setFrameShape(QFrame.StyledPanel)
        content_frame.setFrameShadow(QFrame.Raised)
        content_frame.setObjectName("create-account-top")
        content_layout = QVBoxLayout(content_frame)

        # # Create labels
        welcome_lbl = QLabel('Welcome to UChat.')
        welcome_lbl.setObjectName('create-account-welcome')
        sub_lbl = QLabel('A secure and stateless, peer-to-peer messaging client')
        sub_lbl.setObjectName('sub-lbl')
        #
        # # Create separation line
        line = QFrame()
        line.setFixedHeight(1)
        line.setObjectName("line")
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        # Configure proceed button
        self.__proceed_btn.setFixedSize(QSize(85, 35))

        # # Layout
        content_layout.addWidget(welcome_lbl)
        content_layout.addWidget(sub_lbl)
        content_layout.addSpacing(10)
        content_layout.addWidget(line)
        content_layout.addWidget(self.__slide_stack)

        self.__layout_manager.addWidget(content_frame)
        self.__layout_manager.addSpacing(35)
        self.__layout_manager.addWidget(self.__proceed_btn)
        self.__layout_manager.addStretch()
Exemplo n.º 26
0
class BottomPanel(QWidget):
    def __init__(self, app, parent=None):
        super().__init__(parent)
        self._app = app

        self._layout = QHBoxLayout(self)
        self.back_btn = ToolbarButton('⇦', self)
        self.forward_btn = ToolbarButton('⇨', self)
        self.magicbox = MagicBox(self._app)

        self._stack_switch = ToolbarButton('⁐', self)
        self._stacked_widget = QStackedWidget(self)
        self._stacked_widget.addWidget(self.magicbox)
        self._stack_switch.hide()

        self.status_line = StatusLine(self._app)
        self.settings_btn = ToolbarButton('⋮', self)
        self.settings_btn.setToolTip('配置')

        # initialize widgets
        self.status_line.add_item(StatusLineItem('plugin', PluginStatus(self._app)))
        self.status_line.add_item(StatusLineItem('notify', NotifyStatus(self._app)))
        self.back_btn.setEnabled(False)
        self.forward_btn.setEnabled(False)

        self.back_btn.clicked.connect(self._app.browser.back)
        self.forward_btn.clicked.connect(self._app.browser.forward)
        self._stack_switch.clicked.connect(self._show_next_stacked_widget)

        self._setup_ui()

    def _setup_ui(self):
        self.setObjectName('bottom_panel')

        self._layout.addSpacing(5)
        self._layout.addWidget(self.back_btn)
        self._layout.addSpacing(5)
        self._layout.addWidget(self.forward_btn)
        self._layout.addSpacing(80)
        self._layout.addWidget(self._stacked_widget)
        self._layout.addSpacing(10)
        self._layout.addWidget(self._stack_switch)
        # self._layout.addStretch(0)
        self._layout.addSpacing(80)
        self._layout.addWidget(self.status_line)
        self._layout.addWidget(self.settings_btn)

        # assume the magicbox height is about 30
        h_margin, v_margin = 5, 10
        height = self.magicbox.height()

        self.setFixedHeight(height + v_margin * 2 + 10)
        self._layout.setContentsMargins(h_margin, v_margin, h_margin, v_margin)
        self._layout.setSpacing(0)

    def _show_next_stacked_widget(self):
        current_index = self._stacked_widget.currentIndex()
        if current_index < self._stacked_widget.count() - 1:
            next_index = current_index + 1
        else:
            next_index = 0
        self._stacked_widget.setCurrentIndex(next_index)

    def add_stacked_widget(self, widget):
        """

        .. versionadded:: 3.7.10
        """
        self._stacked_widget.addWidget(widget)
        if self._stacked_widget.count() > 1:
            self._stack_switch.show()

    def set_top_stacked_widget(self, widget):
        """

        .. versionadded:: 3.7.10
        """
        self._stacked_widget.setCurrentWidget(widget)

    def clear_stacked_widget(self):
        """

        .. versionadded:: 3.7.10
        """
        while self._stacked_widget.count() > 0:
            self._stacked_widget.removeWidget(self._stacked_widget.currentWidget())
        self._stack_switch.hide()

    def show_and_focus_magicbox(self):
        """Show and focus magicbox

        .. versionadded:: 3.7.10
        """
        if self._stacked_widget.indexOf(self.magicbox) != -1:
            self.set_top_stacked_widget(self.magicbox)
            self.magicbox.setFocus()
Exemplo n.º 27
0
class PyMultiPageWidget(QWidget):

    currentIndexChanged = pyqtSignal(int)

    pageTitleChanged = pyqtSignal(str)

    def __init__(self, parent=None):
        super(PyMultiPageWidget, self).__init__(parent)

        self.comboBox = QComboBox()
        # MAGIC
        # It is important that the combo box has an object name beginning
        # with '__qt__passive_', otherwise, it is inactive in the form editor
        # of the designer and you can't change the current page via the
        # combo box.
        # MAGIC
        self.comboBox.setObjectName('__qt__passive_comboBox')
        self.stackWidget = QStackedWidget()
        self.comboBox.activated.connect(self.setCurrentIndex)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.comboBox)
        self.layout.addWidget(self.stackWidget)
        self.setLayout(self.layout)

    def sizeHint(self):
        return QSize(200, 150)

    def count(self):
        return self.stackWidget.count()

    def widget(self, index):
        return self.stackWidget.widget(index)

    @pyqtSlot(QWidget)
    def addPage(self, page):
        self.insertPage(self.count(), page)

    @pyqtSlot(int, QWidget)
    def insertPage(self, index, page):
        page.setParent(self.stackWidget)
        self.stackWidget.insertWidget(index, page)
        title = page.windowTitle()
        if title == "":
            title = "Page %d" % (self.comboBox.count() + 1)
            page.setWindowTitle(title)
        self.comboBox.insertItem(index, title)

    @pyqtSlot(int)
    def removePage(self, index):
        widget = self.stackWidget.widget(index)
        self.stackWidget.removeWidget(widget)
        self.comboBox.removeItem(index)

    def getPageTitle(self):
        return self.stackWidget.currentWidget().windowTitle()

    @pyqtSlot(str)
    def setPageTitle(self, newTitle):
        self.comboBox.setItemText(self.getCurrentIndex(), newTitle)
        self.stackWidget.currentWidget().setWindowTitle(newTitle)
        self.pageTitleChanged.emit(newTitle)

    def getCurrentIndex(self):
        return self.stackWidget.currentIndex()

    @pyqtSlot(int)
    def setCurrentIndex(self, index):
        if index != self.getCurrentIndex():
            self.stackWidget.setCurrentIndex(index)
            self.comboBox.setCurrentIndex(index)
            self.currentIndexChanged.emit(index)

    pageTitle = pyqtProperty(str,
                             fget=getPageTitle,
                             fset=setPageTitle,
                             stored=False)
    currentIndex = pyqtProperty(int,
                                fget=getCurrentIndex,
                                fset=setCurrentIndex)
Exemplo n.º 28
0
class Settings(QDialog):
    """Window showing the Settings/settings.

    Parameters
    ----------
    parent : instance of QMainWindow
        the main window
    """
    def __init__(self, parent):
        super().__init__(None, Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.parent = parent
        self.config = ConfigUtils(self.parent.update)

        self.setWindowTitle('Settings')
        self.create_settings()

    def create_settings(self):
        """Create the widget, organized in two parts.

        Notes
        -----
        When you add widgets in config, remember to update show_settings too
        """
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply |
                                QDialogButtonBox.Cancel)
        self.idx_ok = bbox.button(QDialogButtonBox.Ok)
        self.idx_apply = bbox.button(QDialogButtonBox.Apply)
        self.idx_cancel = bbox.button(QDialogButtonBox.Cancel)
        bbox.clicked.connect(self.button_clicked)

        page_list = QListWidget()
        page_list.setSpacing(1)
        page_list.currentRowChanged.connect(self.change_widget)

        pages = ['General', 'Overview', 'Signals', 'Channels', 'Spectrum',
                 'Notes', 'Video']
        for one_page in pages:
            page_list.addItem(one_page)

        self.stacked = QStackedWidget()
        self.stacked.addWidget(self.config)
        self.stacked.addWidget(self.parent.overview.config)
        self.stacked.addWidget(self.parent.traces.config)
        self.stacked.addWidget(self.parent.channels.config)
        self.stacked.addWidget(self.parent.spectrum.config)
        self.stacked.addWidget(self.parent.notes.config)
        self.stacked.addWidget(self.parent.video.config)

        hsplitter = QSplitter()
        hsplitter.addWidget(page_list)
        hsplitter.addWidget(self.stacked)

        btnlayout = QHBoxLayout()
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addWidget(hsplitter)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

    def change_widget(self, new_row):
        """Change the widget on the right side.

        Parameters
        ----------
        new_row : int
            index of the widgets
        """
        self.stacked.setCurrentIndex(new_row)

    def button_clicked(self, button):
        """Action when button was clicked.

        Parameters
        ----------
        button : instance of QPushButton
            which button was pressed
        """
        if button in (self.idx_ok, self.idx_apply):

            # loop over widgets, to see if they were modified
            for i_config in range(self.stacked.count()):
                one_config = self.stacked.widget(i_config)

                if one_config.modified:
                    lg.debug('Settings for ' + one_config.widget +
                             ' were modified')
                    one_config.get_values()

                    if self.parent.info.dataset is not None:
                        one_config.update_widget()
                    one_config.modified = False

            if button == self.idx_ok:
                self.accept()

        if button == self.idx_cancel:
            self.reject()
Exemplo n.º 29
0
class SubTabWidget(QWidget):
    _tabChanged = pyqtSignal(int, name = "tabChanged")

    def __init__(self, subtitleData, videoWidget, parent = None):
        super(SubTabWidget, self).__init__(parent)
        self._subtitleData = subtitleData
        self.__initTabWidget(videoWidget)

    def __initTabWidget(self, videoWidget):
        settings = SubSettings()

        mainLayout = QVBoxLayout(self)
        mainLayout.setContentsMargins(0, 0, 0, 0)
        mainLayout.setSpacing(0)

        #TabBar
        self.tabBar = QTabBar(self)

        # Splitter (bookmarks + pages)
        self.splitter = QSplitter(self)
        self.splitter.setObjectName("sidebar_splitter")

        self._toolbox = ToolBox(self._subtitleData, self)
        self._toolbox.setObjectName("sidebar")
        self._toolbox.setMinimumWidth(100)

        self._toolbox.addTool(Details(self._subtitleData, self))
        self._toolbox.addTool(Synchronizer(videoWidget, self._subtitleData, self))
        self._toolbox.addTool(History(self))

        self.rightWidget = QWidget()
        rightLayout = QGridLayout()
        rightLayout.setContentsMargins(0, 0, 0, 0)
        self.rightWidget.setLayout(rightLayout)

        self._mainTab = FileList(_("Subtitles"), self._subtitleData, self)

        self.pages = QStackedWidget(self)
        rightLayout.addWidget(self.pages, 0, 0)

        self.tabBar.addTab(self._mainTab.name)
        self.pages.addWidget(self._mainTab)

        self.splitter.addWidget(self._toolbox)
        self.splitter.addWidget(self.rightWidget)
        self.__drawSplitterHandle(1)

        # Setting widgets
        mainLayout.addWidget(self.tabBar)
        mainLayout.addWidget(self.splitter)

        # Widgets settings
        self.tabBar.setMovable(True)
        self.tabBar.setTabsClosable(True)
        self.tabBar.setExpanding(False)

        # Don't resize left panel if it's not needed
        leftWidgetIndex = self.splitter.indexOf(self._toolbox)
        rightWidgetIndex = self.splitter.indexOf(self.rightWidget)

        self.splitter.setStretchFactor(leftWidgetIndex, 0)
        self.splitter.setStretchFactor(rightWidgetIndex, 1)
        self.splitter.setCollapsible(leftWidgetIndex, False)
        self.splitter.setSizes([250])

        # Some signals
        self.tabBar.currentChanged.connect(self.showTab)
        self.tabBar.tabCloseRequested.connect(self.closeTab)
        self.tabBar.tabMoved.connect(self.moveTab)
        self._mainTab.requestOpen.connect(self.openTab)
        self._mainTab.requestRemove.connect(self.removeFile)

        self.tabChanged.connect(lambda i: self._toolbox.setContentFor(self.tab(i)))

        self.setLayout(mainLayout)

    def __addTab(self, filePath):
        """Returns existing tab index. Creates a new one if it isn't opened and returns its index
        otherwise."""
        for i in range(self.tabBar.count()):
            widget = self.pages.widget(i)
            if not widget.isStatic and filePath == widget.filePath:
                return i
        tab = SubtitleEditor(filePath, self._subtitleData, self)
        newIndex = self.tabBar.addTab(self._createTabName(tab.name, tab.history.isClean()))
        tab.history.cleanChanged.connect(
            lambda clean: self._cleanStateForFileChanged(filePath, clean))
        self.pages.addWidget(tab)
        return newIndex

    def __drawSplitterHandle(self, index):
        splitterHandle = self.splitter.handle(index)

        splitterLayout = QVBoxLayout(splitterHandle)
        splitterLayout.setSpacing(0)
        splitterLayout.setContentsMargins(0, 0, 0, 0)

        line = QFrame(splitterHandle)
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        splitterLayout.addWidget(line)
        splitterHandle.setLayout(splitterLayout)

    def _createTabName(self, name, cleanState):
        if cleanState is True:
            return name
        else:
            return "%s +" % name

    def _cleanStateForFileChanged(self, filePath, cleanState):
        page = self.tabByPath(filePath)
        if page is not None:
            for i in range(self.tabBar.count()):
                if self.tabBar.tabText(i)[:len(page.name)] == page.name:
                    self.tabBar.setTabText(i, self._createTabName(page.name, cleanState))
                    return

    def saveWidgetState(self, settings):
        settings.setState(self.splitter, self.splitter.saveState())
        settings.setHidden(self._toolbox, self._toolbox.isHidden())

    def restoreWidgetState(self, settings):
        self.showPanel(not settings.getHidden(self._toolbox))

        splitterState = settings.getState(self.splitter)
        if not splitterState.isEmpty():
            self.splitter.restoreState(settings.getState(self.splitter))

    @pyqtSlot(str, bool)
    def openTab(self, filePath, background=False):
        if self._subtitleData.fileExists(filePath):
            tabIndex = self.__addTab(filePath)
            if background is False:
                self.showTab(tabIndex)
        else:
            log.error(_("SubtitleEditor not created for %s!" % filePath))

    @pyqtSlot(str)
    def removeFile(self, filePath):
        tab = self.tabByPath(filePath)
        command = RemoveFile(filePath)
        if tab is not None:
            index = self.pages.indexOf(tab)
            if self.closeTab(index):
                self._subtitleData.execute(command)
        else:
            self._subtitleData.execute(command)


    @pyqtSlot(int)
    def closeTab(self, index):
        tab = self.tab(index)
        if tab.canClose():
            widgetToRemove = self.pages.widget(index)
            self.tabBar.removeTab(index)
            self.pages.removeWidget(widgetToRemove)
            widgetToRemove.deleteLater()
            return True
        return False


    def count(self):
        return self.tabBar.count()

    def currentIndex(self):
        return self.tabBar.currentIndex()

    def currentPage(self):
        return self.pages.currentWidget()

    @pyqtSlot(int, int)
    def moveTab(self, fromIndex, toIndex):
        fromWidget = self.pages.widget(fromIndex)
        toWidget = self.pages.widget(toIndex)
        if fromWidget.isStatic or toWidget.isStatic:
            self.tabBar.blockSignals(True) # signals would cause infinite recursion
            self.tabBar.moveTab(toIndex, fromIndex)
            self.tabBar.blockSignals(False)
            return
        else:
            self.pages.removeWidget(fromWidget)
            self.pages.removeWidget(toWidget)

            if fromIndex < toIndex:
                self.pages.insertWidget(fromIndex, toWidget)
                self.pages.insertWidget(toIndex, fromWidget)
            else:
                self.pages.insertWidget(toIndex, fromWidget)
                self.pages.insertWidget(fromIndex, toWidget)

            # Hack
            # Qt changes tabs during mouse drag and dropping. The next line is added
            # to prevent it.
            self.showTab(self.tabBar.currentIndex())

    @pyqtSlot(int)
    def showTab(self, index):
        showWidget = self.pages.widget(index)
        if showWidget:
            self.pages.setCurrentWidget(showWidget)
            self.tabBar.blockSignals(True)
            self.tabBar.setCurrentIndex(index)
            self.tabBar.blockSignals(False)

            # Try to update current tab.
            showWidget.updateTab()

            self._tabChanged.emit(index)

    def showPanel(self, val):
        if val is True:
            self._toolbox.show()
        else:
            self._toolbox.hide()

    def togglePanel(self):
        if self._toolbox.isHidden():
            self._toolbox.show()
        else:
            self._toolbox.hide()

    def tab(self, index):
        return self.pages.widget(index)

    def tabByPath(self, path):
        for i in range(self.pages.count()):
            page = self.tab(i)
            if not page.isStatic and page.filePath == path:
                return page
        return None

    @property
    def fileList(self):
        return self._mainTab
Exemplo n.º 30
0
class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Tic-Tac-Toe')
        self.resize(2500, 1400)
        self.setStyleSheet(open("style.qss").read())

        self.currentTranslator = None
        self.stack = QStackedWidget(self)
        self.stackDict = {}

        # the different windows to show
        self._mainWindow = MainWindow(self)
        self._subMenu = SubMenu(self)
        self._board = Board(self)

        self.stack.addWidget(self._mainWindow)
        self.stackDict["main"] = 0
        self.stack.addWidget(self._subMenu)
        self.stackDict["sub"] = 1
        self.stack.addWidget(self._board)
        self.stackDict["board"] = 2

        # init the timer
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.showWarning)

        self.timerWarning = QTimer()
        self.timerWarning.setSingleShot(True)
        self.timerWarning.timeout.connect(self.expiredWarning)

        hbox = QHBoxLayout(self)
        hbox.addWidget(self.stack)
        self.setLayout(hbox)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()
            QApplication.quit()

    """
    def paintEvent(self, event):# set background_img
        painter = QPainter(self)
        painter.drawRect(self.rect())
        pixmap = QPixmap("./data/images/background.jpg")#Change to the relative path of your own image
        painter.drawPixmap(self.rect(), pixmap)
    """

    def mouseReleaseEvent(self, event):
        if self.stack.currentIndex() > 0:
            self.timer.start(IDLE_TIME)

    def display(self, name):
        index = self.stackDict[name]
        if index == 0:
            self.timer.stop()
        else:
            self.timer.start(IDLE_TIME)
        self.stack.setCurrentIndex(index)

    def changeLanguage(self, lang):
        if self.currentTranslator is not None:
            QCoreApplication.removeTranslator(self.currentTranslator)
        if lang == 'en':
            self.currentTranslator = None
        else:
            self.currentTranslator = QTranslator()
            self.currentTranslator.load('data/translations/mainform_' + lang)
            QCoreApplication.installTranslator(self.currentTranslator)
        for i in range(self.stack.count()):
            self.stack.widget(i).retranslateUi(self)

    def showWarning(self):
        self.msg = WarningDialog(self)
        self.timerWarning.start(WARNING_TIME)
        self.msg.exec_()

    def removeWarning(self):
        self.msg.close()
        self.timerWarning.stop()
        self.timer.start(IDLE_TIME)

    def expiredWarning(self):
        self.msg.close()
        self.display('main')
Exemplo n.º 31
0
class PyMultiPageWidget(QWidget):

    currentIndexChanged = pyqtSignal(int)

    pageTitleChanged = pyqtSignal(str)

    def __init__(self, parent=None):
        super(PyMultiPageWidget, self).__init__(parent)

        self.comboBox = QComboBox()
        # MAGIC
        # It is important that the combo box has an object name beginning
        # with '__qt__passive_', otherwise, it is inactive in the form editor
        # of the designer and you can't change the current page via the
        # combo box.
        # MAGIC
        self.comboBox.setObjectName('__qt__passive_comboBox')        
        self.stackWidget = QStackedWidget()
        self.comboBox.activated.connect(self.setCurrentIndex)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.comboBox)
        self.layout.addWidget(self.stackWidget)
        self.setLayout(self.layout)

    def sizeHint(self):
        return QSize(200, 150)

    def count(self):
        return self.stackWidget.count()

    def widget(self, index):
        return self.stackWidget.widget(index)

    @pyqtSlot(QWidget)
    def addPage(self, page):
        self.insertPage(self.count(), page)

    @pyqtSlot(int, QWidget)
    def insertPage(self, index, page):
        page.setParent(self.stackWidget)
        self.stackWidget.insertWidget(index, page)
        title = page.windowTitle()
        if title == "":
            title = "Page %d" % (self.comboBox.count() + 1)
            page.setWindowTitle(title)
        self.comboBox.insertItem(index, title)

    @pyqtSlot(int)
    def removePage(self, index):
        widget = self.stackWidget.widget(index)
        self.stackWidget.removeWidget(widget)
        self.comboBox.removeItem(index)

    def getPageTitle(self):
        return self.stackWidget.currentWidget().windowTitle()
    
    @pyqtSlot(str)
    def setPageTitle(self, newTitle):
        self.comboBox.setItemText(self.getCurrentIndex(), newTitle)
        self.stackWidget.currentWidget().setWindowTitle(newTitle)
        self.pageTitleChanged.emit(newTitle)

    def getCurrentIndex(self):
        return self.stackWidget.currentIndex()

    @pyqtSlot(int)
    def setCurrentIndex(self, index):
        if index != self.getCurrentIndex():
            self.stackWidget.setCurrentIndex(index)
            self.comboBox.setCurrentIndex(index)
            self.currentIndexChanged.emit(index)

    pageTitle = pyqtProperty(str, fget=getPageTitle, fset=setPageTitle, stored=False)
    currentIndex = pyqtProperty(int, fget=getCurrentIndex, fset=setCurrentIndex)
Exemplo n.º 32
0
class PreferencesDialog(QDialog):

    def __init__(self, mainwindow):
        super(PreferencesDialog, self).__init__(mainwindow)
        self.setWindowModality(Qt.WindowModal)
        if mainwindow:
            self.addAction(mainwindow.actionCollection.help_whatsthis)
        layout = QVBoxLayout()
        layout.setSpacing(10)
        self.setLayout(layout)

        # listview to the left, stacked widget to the right
        top = QHBoxLayout()
        layout.addLayout(top)

        self.pagelist = QListWidget(self)
        self.stack = QStackedWidget(self)
        top.addWidget(self.pagelist, 0)
        top.addWidget(self.stack, 2)

        layout.addWidget(widgets.Separator(self))

        b = self.buttons = QDialogButtonBox(self)
        b.setStandardButtons(
            QDialogButtonBox.Ok
            | QDialogButtonBox.Cancel
            | QDialogButtonBox.Apply
            | QDialogButtonBox.Reset
            | QDialogButtonBox.Help)
        layout.addWidget(b)
        b.accepted.connect(self.accept)
        b.rejected.connect(self.reject)
        b.button(QDialogButtonBox.Apply).clicked.connect(self.saveSettings)
        b.button(QDialogButtonBox.Reset).clicked.connect(self.loadSettings)
        b.button(QDialogButtonBox.Help).clicked.connect(self.showHelp)
        b.button(QDialogButtonBox.Help).setShortcut(QKeySequence.HelpContents)
        b.button(QDialogButtonBox.Apply).setEnabled(False)

        # fill the pagelist
        self.pagelist.setIconSize(QSize(32, 32))
        self.pagelist.setSpacing(2)
        for item in pageorder():
            self.pagelist.addItem(item())
        self.pagelist.currentItemChanged.connect(self.slotCurrentItemChanged)

        app.translateUI(self, 100)
        # read our size and selected page
        qutil.saveDialogSize(self, "preferences/dialog/size", QSize(500, 300))
        self.pagelist.setCurrentRow(_prefsindex)

    def translateUI(self):
        self.pagelist.setFixedWidth(self.pagelist.sizeHintForColumn(0) + 12)
        self.setWindowTitle(app.caption(_("Preferences")))

    def done(self, result):
        if result and self.buttons.button(QDialogButtonBox.Apply).isEnabled():
            self.saveSettings()
        # save our size and selected page
        global _prefsindex
        _prefsindex = self.pagelist.currentRow()
        super(PreferencesDialog, self).done(result)

    def pages(self):
        """Yields the settings pages that are already instantiated."""
        for n in range(self.stack.count()):
            yield self.stack.widget(n)

    def showHelp(self):
        userguide.show(self.pagelist.currentItem().help)

    def loadSettings(self):
        """Loads the settings on reset."""
        for page in self.pages():
            page.loadSettings()
            page.hasChanges = False
        self.buttons.button(QDialogButtonBox.Apply).setEnabled(False)

    def saveSettings(self):
        """Saves the settings and applies them."""
        for page in self.pages():
            if page.hasChanges:
                page.saveSettings()
                page.hasChanges = False
        self.buttons.button(QDialogButtonBox.Apply).setEnabled(False)

        # emit the signal
        app.settingsChanged()

    def slotCurrentItemChanged(self, item):
        item.activate()

    def changed(self):
        """Call this to enable the Apply button."""
        self.buttons.button(QDialogButtonBox.Apply).setEnabled(True)
Exemplo n.º 33
0
class AppSettings(QDialog):

    SettingsWidgets = []

    def __init__(self, conf, **kwargs):
        super().__init__(**kwargs)

        self.conf = conf
        self.setWindowTitle('LiSP preferences')

        self.setWindowModality(QtCore.Qt.ApplicationModal)
        self.setMaximumSize(635, 530)
        self.setMinimumSize(635, 530)
        self.resize(635, 530)

        self.listWidget = QListWidget(self)
        self.listWidget.setGeometry(QtCore.QRect(5, 10, 185, 470))

        self.sections = QStackedWidget(self)
        self.sections.setGeometry(QtCore.QRect(200, 10, 430, 470))

        for widget in self.SettingsWidgets:
            widget = widget(QtCore.QSize(430, 465), self)
            widget.set_configuration(self.conf)

            self.listWidget.addItem(widget.NAME)
            self.sections.addWidget(widget)

        if len(self.SettingsWidgets) > 0:
            self.listWidget.setCurrentRow(0)

        self.listWidget.currentItemChanged.connect(self._change_page)

        self.dialogButtons = QDialogButtonBox(self)
        self.dialogButtons.setGeometry(10, 495, 615, 30)
        self.dialogButtons.setStandardButtons(QDialogButtonBox.Cancel |
                                              QDialogButtonBox.Ok)

        self.dialogButtons.rejected.connect(self.reject)
        self.dialogButtons.accepted.connect(self.accept)

    def get_configuraton(self):
        conf = {}

        for n in range(self.sections.count()):
            widget = self.sections.widget(n)
            newconf = widget.get_configuration()
            deep_update(conf, newconf)

        return conf

    @classmethod
    def register_settings_widget(cls, widget):
        if widget not in cls.SettingsWidgets:
            cls.SettingsWidgets.append(widget)

    @classmethod
    def unregister_settings_widget(cls, widget):
        if widget not in cls.SettingsWidgets:
            cls.SettingsWidgets.remove(widget)

    def _change_page(self, current, previous):
        if not current:
            current = previous

        self.sections.setCurrentIndex(self.listWidget.row(current))
Exemplo n.º 34
0
class BatchTrackingDialog(QDialog):
    """Dialog for batch processing videos."""
    def __init__(self, root_folder, parent=None):
        super(BatchTrackingDialog, self).__init__(parent)
        self.setWindowFlags(Qt.WindowMinimizeButtonHint
                            | Qt.WindowCloseButtonHint)
        self.setWindowTitle('Batch Processing Wizard')
        app_icon = QIcon(QPixmap(os.path.join(DIR, '..', 'icons', 'logo.png')))
        self.setWindowIcon(app_icon)

        self.step_number = 0
        self.video_settings = []
        self.root_folder = root_folder

        self.layout = QGridLayout()
        # move all steps into a stacked layout.
        # this allows each layout to be shown individually,
        # while providing a container for all layouts.
        self.widgets = QStackedWidget()
        file_widget = StackedStepWidget(0,
                                        BatchFileSelector(self.root_folder, 0))
        arena_widget = StackedStepWidget(1, BatchArenaSpecifier(1))
        try:
            arena_widget.settings_widget.background_frame_ix.connect(
                self.update_progress_bar)
        except RuntimeError:
            print('Error while trying to connect the arena widget' +
                  'to the update_progress_bar fxn.')

        female_widget = StackedStepWidget(2, BatchFemaleSpecifier(2))
        tight_threshold_widget = StackedStepWidget(
            3, BatchTightThresholdSpecifier(3))
        tight_threshold_widget.settings_widget.image_calc_progress.connect(
            self.update_progress_bar)

        loose_threshold_widget = StackedStepWidget(
            4, BatchLooseThresholdSpecifier(4))
        loose_threshold_widget.settings_widget.image_calc_progress.connect(
            self.update_progress_bar)

        tracking_widget = StackedStepWidget(5, BatchTrackingWidget(5))
        tracking_widget.settings_widget.tracking_progress.connect(
            self.update_progress_bar)

        self.widgets.addWidget(file_widget)
        self.widgets.addWidget(arena_widget)
        self.widgets.addWidget(female_widget)
        self.widgets.addWidget(tight_threshold_widget)
        self.widgets.addWidget(loose_threshold_widget)
        self.widgets.addWidget(tracking_widget)

        # connect all widget signals to this class's update_settings
        # function.
        self.connect_widget_signals()

        self.layout.addWidget(self.widgets, 0, 0, 6, 6)

        # setup the next and previous buttons
        self.next_button = QPushButton('Next')
        self.next_button.setEnabled(False)
        previous_button = QPushButton('Previous')
        self.next_button.clicked.connect(self.step_forward)
        previous_button.clicked.connect(self.step_backward)

        self.layout.addWidget(previous_button, 6, 0, 1, 1)
        self.layout.addWidget(self.next_button, 6, 5, 1, 1)

        # setup the status bar interface
        self.set_status()
        self.layout.addWidget(self.status, 7, 0, 1, 6)
        self.setLayout(self.layout)
        self.resize(1000, 600)

    def connect_widget_signals(self):
        """Connects all widgets in the StackedLayout to self.update_settings."""
        for i in xrange(self.widgets.count() + 1):
            # this call returns 0 if widget at index is not present.
            if self.widgets.widget(i):
                self.widgets.widget(
                    i).settings_widget.all_settings_valid.connect(
                        self.update_settings)

    def set_status(self):
        """Sets interface for status bar at bottom of dialog window."""
        self.status = QStatusBar()
        self.status.setSizeGripEnabled(True)

        self.process_label = QLabel('Process')
        self.process_label.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.progress_bar = QProgressBar()

        self.status.addWidget(self.process_label)
        self.status.addPermanentWidget(self.progress_bar)

    def _step(self):
        """Updates layout currently being displayed in StackedLayout."""
        self.widgets.setCurrentIndex(self.step_number)
        if self.widgets.currentWidget().settings_widget.check_settings_valid():
            self.next_button.setEnabled(True)

    def step_forward(self):
        """Moves the StackedLayout forward by one index."""
        if self.step_number < self.widgets.count() - 1:
            self.step_number += 1
        self._step()

    def step_backward(self):
        """Moves the StackedLayout backward by one index."""
        if self.step_number > 0:
            self.step_number -= 1
        self._step()

    @pyqtSlot(bool, int, list)
    def update_settings(self, is_set, widget_ix, settings_list):
        """This function updates all of the video settings.

        It is a slot for the BatchSettingsWidget.all_settings_valid
        signal, and not only sets the video_settings of this 
        class (TrackingDialog), but updates all of the settings in
        each of the widgets in the StackedLayout.
        """
        if is_set:
            self.next_button.setEnabled(True)
            self.video_settings = settings_list
            for i in xrange(self.widgets.count() + 1):
                if self.widgets.widget(i):
                    self.widgets.widget(i).settings_widget.update_settings(
                        settings_list)
        else:
            self.next_button.setEnabled(False)

    @pyqtSlot(int, str)
    def update_progress_bar(self, progress, description):
        """Updates the progress bar based on actions happening
        in widgets within the StackedLayout."""
        self.process_label.setText('Process -- {}'.format(description))
        self.progress_bar.setValue(progress)
Exemplo n.º 35
0
class Pager(QWidget):
    finished = pyqtSignal()
    changed = pyqtSignal(int)

    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)

        lay = QVBoxLayout(self)

        self.stack = QStackedWidget()
        self.stack.currentChanged.connect(self.onChanged)

        self.nextButton = QPushButton("Next")
        self.nextButton.clicked.connect(self.onNext)
        self.finishButton = QPushButton("Finish")
        self.finishButton.clicked.connect(self.onFinish)
        self.previousButton = QPushButton("Previous")
        self.previousButton.clicked.connect(self.onPrevious)
        self.disableControls()
        self.btnLayout = QHBoxLayout()
        self.btnLayout.addWidget(self.previousButton)
        self.btnLayout.addWidget(self.nextButton)
        self.btnLayout.addWidget(self.finishButton)

        lay.addWidget(self.stack)
        lay.addLayout(self.btnLayout)

    # events
    @pyqtSlot()
    def onNext(self):
        index = self.stack.currentIndex()
        top = self.stack.count() - 1
        index += 1
        if index >= top:
            index = top
        self.stack.setCurrentIndex(index)

    @pyqtSlot()
    def onPrevious(self):
        index = self.stack.currentIndex()
        bottom = 0
        index -= 1
        if index <= bottom:
            index = bottom
        self.stack.setCurrentIndex(index)

    @pyqtSlot()
    def onFinish(self):
        self.disableFinish()
        self.disablePrevious()
        self.enableNext()
        self.finished.emit()
        self.stack.setCurrentIndex(0)

    @pyqtSlot(int)
    def onChanged(self, index):
        self.disableControls()
        self.changed.emit(index)
        widget = self.stack.widget(index)
        widget.onEnter()
        if widget.nextEnabled:
            self.enableNext()
        if widget.previousEnabled:
            self.enablePrevious()

    @pyqtSlot()
    def onPageFinished(self):
        self.disableControls()

        index = self.stack.currentIndex()
        top = self.stack.count() - 1
        bottom = 0

        self.enableNext()
        if index >= top:
            self.disableNext()
            self.enableFinish()

        self.enablePrevious()
        if index <= bottom:
            self.disablePrevious()

    # previous/next/finish controls
    def enableNext(self):
        self.nextButton.show()

    def enableFinish(self):
        self.finishButton.show()

    def enablePrevious(self):
        self.previousButton.show()

    def enableControls(self):
        self.enableNext()
        self.enableFinish()
        self.enablePrevious()

    def disableNext(self):
        self.nextButton.hide()

    def disableFinish(self):
        self.finishButton.hide()

    def disablePrevious(self):
        self.previousButton.hide()

    def disableControls(self):
        self.disableNext()
        self.disableFinish()
        self.disablePrevious()

    # for adding widgets and controlling page
    def addPage(self, pageWidget):
        pageWidget.setPager(self)
        self.stack.addWidget(pageWidget)
        pageWidget.finished.connect(self.onPageFinished)

    def getButtonHeight(self):
        return self.btnLayout.sizeHint().height()

    def clearPages(self):
        self.stack.clear()

    def setPageIndex(self, index):
        self.stack.setCurrentIndex(index)
Exemplo n.º 36
0
class ParamsByType(QWidget, MooseWidget):
    """
    Has a QComboBox for the different allowed types.
    On switching type a new ParamsByGroup is shown.
    """
    needBlockList = pyqtSignal(list)
    blockRenamed = pyqtSignal(object, str)
    changed = pyqtSignal()

    def __init__(self, block, type_block_map, **kwds):
        """
        Constructor.
        Input:
            block[BlockInfo]: The block to show.
        """
        super(ParamsByType, self).__init__(**kwds)
        self.block = block
        self.combo = QComboBox()
        self.types = []
        self.type_params_map = {}
        self.type_block_map = type_block_map
        self.table_stack = QStackedWidget()
        self.type_table_map = {}

        for t in sorted(self.block.types.keys()):
            self.types.append(t)
            params_list = []
            for p in self.block.parameters_list:
                params_list.append(self.block.parameters[p])
            t_block = self.block.types[t]
            for p in t_block.parameters_list:
                params_list.append(t_block.parameters[p])
            self.type_params_map[t] = params_list

        self.combo.addItems(sorted(self.block.types.keys()))
        self.combo.currentTextChanged.connect(self.setBlockType)

        self.top_layout = WidgetUtils.addLayout(vertical=True)
        self.top_layout.addWidget(self.combo)
        self.top_layout.addWidget(self.table_stack)
        self.setLayout(self.top_layout)
        self.user_params = []
        self.setDefaultBlockType()

        self.setup()

    def _syncUserParams(self, current, to):
        """
        Sync user added parameters that are on the main block into
        each type ParamsByGroup.
        Input:
            current[ParamsByGroup]: The current group parameter table
            to[ParamsByGroup]: The new group parameter table
        """
        ct = current.findTable("Main")
        tot = to.findTable("Main")
        if not ct or not tot or ct == tot:
            return
        tot.removeUserParams()
        params = ct.getUserParams()
        tot.addUserParams(params)
        to.syncParamsFrom(current)
        # Make sure the name parameter stays the same
        idx = ct.findRow("Name")
        if idx >= 0:
            name = ct.item(idx, 1).text()
            idx = tot.findRow("Name")
            if idx >= 0:
                tot.item(idx, 1).setText(name)

    def currentType(self):
        return self.combo.currentText()

    def save(self):
        """
        Look at the user params in self.block.parameters.
        update the type tables
        Save type on block
        """
        t = self.getTable()
        if t:
            t.save()
            self.block.setBlockType(self.combo.currentText())

    def reset(self):
        t = self.getTable()
        t.reset()

    def getOrCreateTypeTable(self, type_name):
        """
        Gets the table for the type name or create it if it doesn't exist.
        Input:
            type_name[str]: Name of the type
        Return:
            ParamsByGroup: The parameters corresponding to the type
        """
        t = self.type_table_map.get(type_name)
        if t:
            return t
        t = ParamsByGroup(self.block, self.type_params_map.get(type_name, self.block.orderedParameters()), self.type_block_map)
        t.needBlockList.connect(self.needBlockList)
        t.blockRenamed.connect(self.blockRenamed)
        t.changed.connect(self.changed)
        self.type_table_map[type_name] = t
        self.table_stack.addWidget(t)
        return t

    def setDefaultBlockType(self):
        param = self.block.getParamInfo("type")
        if param and param.value:
            self.setBlockType(param.value)
        elif self.block.types:
            self.setBlockType(sorted(self.block.types.keys())[0])

    def setBlockType(self, type_name):
        if type_name not in self.block.types:
            return
        t = self.getOrCreateTypeTable(type_name)
        t.updateWatchers()
        self.combo.blockSignals(True)
        self.combo.setCurrentText(type_name)
        self.combo.blockSignals(False)
        t.updateType(type_name)
        current = self.table_stack.currentWidget()
        self._syncUserParams(current, t)
        self.table_stack.setCurrentWidget(t)
        self.changed.emit()

    def addUserParam(self, param):
        t = self.table_stack.currentWidget()
        t.addUserParam(param)

    def setWatchedBlockList(self, path, children):
        for i in range(self.table_stack.count()):
            t = self.table_stack.widget(i)
            t.setWatchedBlockList(path, children)

    def updateWatchers(self):
        for i in range(self.table_stack.count()):
            t = self.table_stack.widget(i)
            t.updateWatchers()

    def getTable(self):
        return self.table_stack.currentWidget()

    def paramValue(self, name):
        for i in range(self.table_stack.count()):
            t = self.table_stack.widget(i)
            if t.paramValue(name):
                return t.paramValue(name)
Exemplo n.º 37
0
class TilesetDock(QDockWidget):
    ##
    # Emitted when the current tile changed.
    ##
    currentTileChanged = pyqtSignal(list)
    ##
    # Emitted when the currently selected tiles changed.
    ##
    stampCaptured = pyqtSignal(TileStamp)
    ##
    # Emitted when files are dropped at the tileset dock.
    ##
    tilesetsDropped = pyqtSignal(QStringList)
    newTileset = pyqtSignal()

    ##
    # Constructor.
    ##
    def __init__(self, parent=None):
        super().__init__(parent)

        # Shared tileset references because the dock wants to add new tiles
        self.mTilesets = QVector()
        self.mCurrentTilesets = QMap()
        self.mMapDocument = None
        self.mTabBar = QTabBar()
        self.mViewStack = QStackedWidget()
        self.mToolBar = QToolBar()
        self.mCurrentTile = None
        self.mCurrentTiles = None
        self.mNewTileset = QAction(self)
        self.mImportTileset = QAction(self)
        self.mExportTileset = QAction(self)
        self.mPropertiesTileset = QAction(self)
        self.mDeleteTileset = QAction(self)
        self.mEditTerrain = QAction(self)
        self.mAddTiles = QAction(self)
        self.mRemoveTiles = QAction(self)
        self.mTilesetMenuButton = TilesetMenuButton(self)
        self.mTilesetMenu = QMenu(self)  # opens on click of mTilesetMenu
        self.mTilesetActionGroup = QActionGroup(self)
        self.mTilesetMenuMapper = None  # needed due to dynamic content
        self.mEmittingStampCaptured = False
        self.mSynchronizingSelection = False

        self.setObjectName("TilesetDock")
        self.mTabBar.setMovable(True)
        self.mTabBar.setUsesScrollButtons(True)
        self.mTabBar.currentChanged.connect(self.updateActions)
        self.mTabBar.tabMoved.connect(self.moveTileset)
        w = QWidget(self)
        horizontal = QHBoxLayout()
        horizontal.setSpacing(0)
        horizontal.addWidget(self.mTabBar)
        horizontal.addWidget(self.mTilesetMenuButton)
        vertical = QVBoxLayout(w)
        vertical.setSpacing(0)
        vertical.setContentsMargins(5, 5, 5, 5)
        vertical.addLayout(horizontal)
        vertical.addWidget(self.mViewStack)
        horizontal = QHBoxLayout()
        horizontal.setSpacing(0)
        horizontal.addWidget(self.mToolBar, 1)
        vertical.addLayout(horizontal)
        self.mNewTileset.setIcon(QIcon(":images/16x16/document-new.png"))
        self.mImportTileset.setIcon(QIcon(":images/16x16/document-import.png"))
        self.mExportTileset.setIcon(QIcon(":images/16x16/document-export.png"))
        self.mPropertiesTileset.setIcon(
            QIcon(":images/16x16/document-properties.png"))
        self.mDeleteTileset.setIcon(QIcon(":images/16x16/edit-delete.png"))
        self.mEditTerrain.setIcon(QIcon(":images/16x16/terrain.png"))
        self.mAddTiles.setIcon(QIcon(":images/16x16/add.png"))
        self.mRemoveTiles.setIcon(QIcon(":images/16x16/remove.png"))
        Utils.setThemeIcon(self.mNewTileset, "document-new")
        Utils.setThemeIcon(self.mImportTileset, "document-import")
        Utils.setThemeIcon(self.mExportTileset, "document-export")
        Utils.setThemeIcon(self.mPropertiesTileset, "document-properties")
        Utils.setThemeIcon(self.mDeleteTileset, "edit-delete")
        Utils.setThemeIcon(self.mAddTiles, "add")
        Utils.setThemeIcon(self.mRemoveTiles, "remove")
        self.mNewTileset.triggered.connect(self.newTileset)
        self.mImportTileset.triggered.connect(self.importTileset)
        self.mExportTileset.triggered.connect(self.exportTileset)
        self.mPropertiesTileset.triggered.connect(self.editTilesetProperties)
        self.mDeleteTileset.triggered.connect(self.removeTileset)
        self.mEditTerrain.triggered.connect(self.editTerrain)
        self.mAddTiles.triggered.connect(self.addTiles)
        self.mRemoveTiles.triggered.connect(self.removeTiles)
        self.mToolBar.addAction(self.mNewTileset)
        self.mToolBar.setIconSize(QSize(16, 16))
        self.mToolBar.addAction(self.mImportTileset)
        self.mToolBar.addAction(self.mExportTileset)
        self.mToolBar.addAction(self.mPropertiesTileset)
        self.mToolBar.addAction(self.mDeleteTileset)
        self.mToolBar.addAction(self.mEditTerrain)
        self.mToolBar.addAction(self.mAddTiles)
        self.mToolBar.addAction(self.mRemoveTiles)
        self.mZoomable = Zoomable(self)
        self.mZoomComboBox = QComboBox()
        self.mZoomable.connectToComboBox(self.mZoomComboBox)
        horizontal.addWidget(self.mZoomComboBox)
        self.mViewStack.currentChanged.connect(self.updateCurrentTiles)
        TilesetManager.instance().tilesetChanged.connect(self.tilesetChanged)
        DocumentManager.instance().documentAboutToClose.connect(
            self.documentAboutToClose)
        self.mTilesetMenuButton.setMenu(self.mTilesetMenu)
        self.mTilesetMenu.aboutToShow.connect(self.refreshTilesetMenu)
        self.setWidget(w)
        self.retranslateUi()
        self.setAcceptDrops(True)
        self.updateActions()

    def __del__(self):
        del self.mCurrentTiles

    ##
    # Sets the map for which the tilesets should be displayed.
    ##
    def setMapDocument(self, mapDocument):
        if (self.mMapDocument == mapDocument):
            return
        # Hide while we update the tab bar, to avoid repeated layouting
        if sys.platform != 'darwin':
            self.widget().hide()

        self.setCurrentTiles(None)
        self.setCurrentTile(None)

        if (self.mMapDocument):
            # Remember the last visible tileset for this map
            tilesetName = self.mTabBar.tabText(self.mTabBar.currentIndex())
            self.mCurrentTilesets.insert(self.mMapDocument, tilesetName)

        # Clear previous content
        while (self.mTabBar.count()):
            self.mTabBar.removeTab(0)
        while (self.mViewStack.count()):
            self.mViewStack.removeWidget(self.mViewStack.widget(0))
        #self.mTilesets.clear()
        # Clear all connections to the previous document
        if (self.mMapDocument):
            self.mMapDocument.disconnect()
        self.mMapDocument = mapDocument
        if (self.mMapDocument):
            self.mTilesets = self.mMapDocument.map().tilesets()
            for tileset in self.mTilesets:
                view = TilesetView()
                view.setMapDocument(self.mMapDocument)
                view.setZoomable(self.mZoomable)
                self.mTabBar.addTab(tileset.name())
                self.mViewStack.addWidget(view)

            self.mMapDocument.tilesetAdded.connect(self.tilesetAdded)
            self.mMapDocument.tilesetRemoved.connect(self.tilesetRemoved)
            self.mMapDocument.tilesetMoved.connect(self.tilesetMoved)
            self.mMapDocument.tilesetNameChanged.connect(
                self.tilesetNameChanged)
            self.mMapDocument.tilesetFileNameChanged.connect(
                self.updateActions)
            self.mMapDocument.tilesetChanged.connect(self.tilesetChanged)
            self.mMapDocument.tileAnimationChanged.connect(
                self.tileAnimationChanged)
            cacheName = self.mCurrentTilesets.take(self.mMapDocument)
            for i in range(self.mTabBar.count()):
                if (self.mTabBar.tabText(i) == cacheName):
                    self.mTabBar.setCurrentIndex(i)
                    break

            object = self.mMapDocument.currentObject()
            if object:
                if object.typeId() == Object.TileType:
                    self.setCurrentTile(object)

        self.updateActions()
        if sys.platform != 'darwin':
            self.widget().show()

    ##
    # Synchronizes the selection with the given stamp. Ignored when the stamp is
    # changing because of a selection change in the TilesetDock.
    ##
    def selectTilesInStamp(self, stamp):
        if self.mEmittingStampCaptured:
            return
        processed = QSet()
        selections = QMap()
        for variation in stamp.variations():
            tileLayer = variation.tileLayer()
            for cell in tileLayer:
                tile = cell.tile
                if tile:
                    if (processed.contains(tile)):
                        continue
                    processed.insert(tile)  # avoid spending time on duplicates
                    tileset = tile.tileset()
                    tilesetIndex = self.mTilesets.indexOf(
                        tileset.sharedPointer())
                    if (tilesetIndex != -1):
                        view = self.tilesetViewAt(tilesetIndex)
                        if (not view.model()):  # Lazily set up the model
                            self.setupTilesetModel(view, tileset)
                        model = view.tilesetModel()
                        modelIndex = model.tileIndex(tile)
                        selectionModel = view.selectionModel()

                        _x = QItemSelection()
                        _x.select(modelIndex, modelIndex)
                        selections[selectionModel] = _x

        if (not selections.isEmpty()):
            self.mSynchronizingSelection = True
            # Mark captured tiles as selected
            for i in selections:
                selectionModel = i[0]
                selection = i[1]
                selectionModel.select(selection,
                                      QItemSelectionModel.SelectCurrent)

            # Show/edit properties of all captured tiles
            self.mMapDocument.setSelectedTiles(processed.toList())
            # Update the current tile (useful for animation and collision editors)
            first = selections.first()
            selectionModel = first[0]
            selection = first[1]
            currentIndex = QModelIndex(selection.first().topLeft())
            if (selectionModel.currentIndex() != currentIndex):
                selectionModel.setCurrentIndex(currentIndex,
                                               QItemSelectionModel.NoUpdate)
            else:
                self.currentChanged(currentIndex)
            self.mSynchronizingSelection = False

    def currentTilesetChanged(self):
        view = self.currentTilesetView()
        if view:
            s = view.selectionModel()
            if s:
                self.setCurrentTile(view.tilesetModel().tileAt(
                    s.currentIndex()))

    ##
    # Returns the currently selected tile.
    ##
    def currentTile(self):
        return self.mCurrentTile

    def changeEvent(self, e):
        super().changeEvent(e)
        x = e.type()
        if x == QEvent.LanguageChange:
            self.retranslateUi()
        else:
            pass

    def dragEnterEvent(self, e):
        urls = e.mimeData().urls()
        if (not urls.isEmpty() and not urls.at(0).toLocalFile().isEmpty()):
            e.accept()

    def dropEvent(self, e):
        paths = QStringList()
        for url in e.mimeData().urls():
            localFile = url.toLocalFile()
            if (not localFile.isEmpty()):
                paths.append(localFile)

        if (not paths.isEmpty()):
            self.tilesetsDropped.emit(paths)
            e.accept()

    def selectionChanged(self):
        self.updateActions()
        if not self.mSynchronizingSelection:
            self.updateCurrentTiles()

    def currentChanged(self, index):
        if (not index.isValid()):
            return
        model = index.model()
        self.setCurrentTile(model.tileAt(index))

    def updateActions(self):
        external = False
        hasImageSource = False
        hasSelection = False
        view = None
        index = self.mTabBar.currentIndex()
        if (index > -1):
            view = self.tilesetViewAt(index)
            if (view):
                tileset = self.mTilesets.at(index)
                if (not view.model()):  # Lazily set up the model
                    self.setupTilesetModel(view, tileset)

                self.mViewStack.setCurrentIndex(index)
                external = tileset.isExternal()
                hasImageSource = tileset.imageSource() != ''
                hasSelection = view.selectionModel().hasSelection()

        tilesetIsDisplayed = view != None
        mapIsDisplayed = self.mMapDocument != None
        self.mNewTileset.setEnabled(mapIsDisplayed)
        self.mImportTileset.setEnabled(tilesetIsDisplayed and external)
        self.mExportTileset.setEnabled(tilesetIsDisplayed and not external)
        self.mPropertiesTileset.setEnabled(tilesetIsDisplayed and not external)
        self.mDeleteTileset.setEnabled(tilesetIsDisplayed)
        self.mEditTerrain.setEnabled(tilesetIsDisplayed and not external)
        self.mAddTiles.setEnabled(tilesetIsDisplayed and not hasImageSource
                                  and not external)
        self.mRemoveTiles.setEnabled(tilesetIsDisplayed and not hasImageSource
                                     and hasSelection and not external)

    def updateCurrentTiles(self):
        view = self.currentTilesetView()
        if (not view):
            return
        s = view.selectionModel()
        if (not s):
            return
        indexes = s.selection().indexes()
        if len(indexes) == 0:
            return
        first = indexes[0]
        minX = first.column()
        maxX = first.column()
        minY = first.row()
        maxY = first.row()
        for index in indexes:
            if minX > index.column():
                minX = index.column()
            if maxX < index.column():
                maxX = index.column()
            if minY > index.row():
                minY = index.row()
            if maxY < index.row():
                maxY = index.row()

        # Create a tile layer from the current selection
        tileLayer = TileLayer(QString(), 0, 0, maxX - minX + 1,
                              maxY - minY + 1)
        model = view.tilesetModel()
        for index in indexes:
            tileLayer.setCell(index.column() - minX,
                              index.row() - minY, Cell(model.tileAt(index)))

        self.setCurrentTiles(tileLayer)

    def indexPressed(self, index):
        view = self.currentTilesetView()
        tile = view.tilesetModel().tileAt(index)
        if tile:
            self.mMapDocument.setCurrentObject(tile)

    def tilesetAdded(self, index, tileset):
        view = TilesetView()
        view.setMapDocument(self.mMapDocument)
        view.setZoomable(self.mZoomable)
        self.mTilesets.insert(index, tileset.sharedPointer())
        self.mTabBar.insertTab(index, tileset.name())
        self.mViewStack.insertWidget(index, view)
        self.updateActions()

    def tilesetChanged(self, tileset):
        # Update the affected tileset model, if it exists
        index = indexOf(self.mTilesets, tileset)
        if (index < 0):
            return
        model = self.tilesetViewAt(index).tilesetModel()
        if model:
            model.tilesetChanged()

    def tilesetRemoved(self, tileset):
        # Delete the related tileset view
        index = indexOf(self.mTilesets, tileset)
        self.mTilesets.removeAt(index)
        self.mTabBar.removeTab(index)
        self.tilesetViewAt(index).close()

        # Make sure we don't reference this tileset anymore
        if (self.mCurrentTiles):
            # TODO: Don't clean unnecessarily (but first the concept of
            #       "current brush" would need to be introduced)
            cleaned = self.mCurrentTiles.clone()
            cleaned.removeReferencesToTileset(tileset)
            self.setCurrentTiles(cleaned)

        if (self.mCurrentTile and self.mCurrentTile.tileset() == tileset):
            self.setCurrentTile(None)
        self.updateActions()

    def tilesetMoved(self, _from, to):
        self.mTilesets.insert(to, self.mTilesets.takeAt(_from))
        # Move the related tileset views
        widget = self.mViewStack.widget(_from)
        self.mViewStack.removeWidget(widget)
        self.mViewStack.insertWidget(to, widget)
        self.mViewStack.setCurrentIndex(self.mTabBar.currentIndex())
        # Update the titles of the affected tabs
        start = min(_from, to)
        end = max(_from, to)
        for i in range(start, end + 1):
            tileset = self.mTilesets.at(i)
            if (self.mTabBar.tabText(i) != tileset.name()):
                self.mTabBar.setTabText(i, tileset.name())

    def tilesetNameChanged(self, tileset):
        index = indexOf(self.mTilesets, tileset)
        self.mTabBar.setTabText(index, tileset.name())

    def tileAnimationChanged(self, tile):
        view = self.currentTilesetView()
        if view:
            model = view.tilesetModel()
            if model:
                model.tileChanged(tile)

    ##
    # Removes the currently selected tileset.
    ##
    def removeTileset(self, *args):
        l = len(args)
        if l == 0:
            currentIndex = self.mViewStack.currentIndex()
            if (currentIndex != -1):
                self.removeTileset(self.mViewStack.currentIndex())
        elif l == 1:
            ##
            # Removes the tileset at the given index. Prompting the user when the tileset
            # is in use by the map.
            ##
            index = args[0]
            tileset = self.mTilesets.at(index).data()
            inUse = self.mMapDocument.map().isTilesetUsed(tileset)
            # If the tileset is in use, warn the user and confirm removal
            if (inUse):
                warning = QMessageBox(
                    QMessageBox.Warning, self.tr("Remove Tileset"),
                    self.tr("The tileset \"%s\" is still in use by the map!" %
                            tileset.name()), QMessageBox.Yes | QMessageBox.No,
                    self)
                warning.setDefaultButton(QMessageBox.Yes)
                warning.setInformativeText(
                    self.tr("Remove this tileset and all references "
                            "to the tiles in this tileset?"))
                if (warning.exec() != QMessageBox.Yes):
                    return

            remove = RemoveTileset(self.mMapDocument, index, tileset)
            undoStack = self.mMapDocument.undoStack()
            if (inUse):
                # Remove references to tiles in this tileset from the current map
                def referencesTileset(cell):
                    tile = cell.tile
                    if tile:
                        return tile.tileset() == tileset
                    return False

                undoStack.beginMacro(remove.text())
                removeTileReferences(self.mMapDocument, referencesTileset)

            undoStack.push(remove)
            if (inUse):
                undoStack.endMacro()

    def moveTileset(self, _from, to):
        command = MoveTileset(self.mMapDocument, _from, to)
        self.mMapDocument.undoStack().push(command)

    def editTilesetProperties(self):
        tileset = self.currentTileset()
        if (not tileset):
            return
        self.mMapDocument.setCurrentObject(tileset)
        self.mMapDocument.emitEditCurrentObject()

    def importTileset(self):
        tileset = self.currentTileset()
        if (not tileset):
            return
        command = SetTilesetFileName(self.mMapDocument, tileset, QString())
        self.mMapDocument.undoStack().push(command)

    def exportTileset(self):
        tileset = self.currentTileset()
        if (not tileset):
            return

        tsxFilter = self.tr("Tiled tileset files (*.tsx)")
        helper = FormatHelper(FileFormat.ReadWrite, tsxFilter)

        prefs = preferences.Preferences.instance()

        suggestedFileName = prefs.lastPath(
            preferences.Preferences.ExternalTileset)
        suggestedFileName += '/'
        suggestedFileName += tileset.name()

        extension = ".tsx"

        if (not suggestedFileName.endswith(extension)):
            suggestedFileName += extension

        selectedFilter = tsxFilter
        fileName, _ = QFileDialog.getSaveFileName(self,
                                                  self.tr("Export Tileset"),
                                                  suggestedFileName,
                                                  helper.filter(),
                                                  selectedFilter)
        if fileName == '':
            return
        prefs.setLastPath(preferences.Preferences.ExternalTileset,
                          QFileInfo(fileName).path())

        tsxFormat = TsxTilesetFormat()
        format = helper.formatByNameFilter(selectedFilter)
        if not format:
            format = tsxFormat

        if format.write(tileset, fileName):
            command = SetTilesetFileName(self.mMapDocument, tileset, fileName)
            self.mMapDocument.undoStack().push(command)
        else:
            error = format.errorString()
            QMessageBox.critical(self.window(), self.tr("Export Tileset"),
                                 self.tr("Error saving tileset: %s" % error))

    def editTerrain(self):
        tileset = self.currentTileset()
        if (not tileset):
            return
        editTerrainDialog = EditTerrainDialog(self.mMapDocument, tileset, self)
        editTerrainDialog.exec()

    def addTiles(self):
        tileset = self.currentTileset()
        if (not tileset):
            return
        prefs = preferences.Preferences.instance()
        startLocation = QFileInfo(
            prefs.lastPath(preferences.Preferences.ImageFile)).absolutePath()
        filter = Utils.readableImageFormatsFilter()
        files = QFileDialog.getOpenFileNames(self.window(),
                                             self.tr("Add Tiles"),
                                             startLocation, filter)
        tiles = QList()
        id = tileset.tileCount()
        for file in files:
            image = QPixmap(file)
            if (not image.isNull()):
                tiles.append(Tile(image, file, id, tileset))
                id += 1
            else:
                warning = QMessageBox(QMessageBox.Warning,
                                      self.tr("Add Tiles"),
                                      self.tr("Could not load \"%s\"!" % file),
                                      QMessageBox.Ignore | QMessageBox.Cancel,
                                      self.window())
                warning.setDefaultButton(QMessageBox.Ignore)
                if (warning.exec() != QMessageBox.Ignore):
                    tiles.clear()
                    return

        if (tiles.isEmpty()):
            return
        prefs.setLastPath(preferences.Preferences.ImageFile, files.last())
        self.mMapDocument.undoStack().push(
            AddTiles(self.mMapDocument, tileset, tiles))

    def removeTiles(self):
        view = self.currentTilesetView()
        if (not view):
            return
        if (not view.selectionModel().hasSelection()):
            return
        indexes = view.selectionModel().selectedIndexes()
        model = view.tilesetModel()
        tileIds = RangeSet()
        tiles = QList()
        for index in indexes:
            tile = model.tileAt(index)
            if tile:
                tileIds.insert(tile.id())
                tiles.append(tile)

        def matchesAnyTile(cell):
            tile = cell.tile
            if tile:
                return tiles.contains(tile)
            return False

        inUse = self.hasTileReferences(self.mMapDocument, matchesAnyTile)
        # If the tileset is in use, warn the user and confirm removal
        if (inUse):
            warning = QMessageBox(
                QMessageBox.Warning, self.tr("Remove Tiles"),
                self.tr("One or more of the tiles to be removed are "
                        "still in use by the map!"),
                QMessageBox.Yes | QMessageBox.No, self)
            warning.setDefaultButton(QMessageBox.Yes)
            warning.setInformativeText(
                self.tr("Remove all references to these tiles?"))
            if (warning.exec() != QMessageBox.Yes):
                return

        undoStack = self.mMapDocument.undoStack()
        undoStack.beginMacro(self.tr("Remove Tiles"))
        removeTileReferences(self.mMapDocument, matchesAnyTile)
        # Iterate backwards over the ranges in order to keep the indexes valid
        firstRange = tileIds.begin()
        it = tileIds.end()
        if (it == firstRange):  # no range
            return
        tileset = view.tilesetModel().tileset()
        while (it != firstRange):
            it -= 1
            item = tileIds.item(it)
            length = item[1] - item[0] + 1
            undoStack.push(
                RemoveTiles(self.mMapDocument, tileset, item[0], length))

        undoStack.endMacro()
        # Clear the current tiles, will be referencing the removed tiles
        self.setCurrentTiles(None)
        self.setCurrentTile(None)

    def documentAboutToClose(self, mapDocument):
        self.mCurrentTilesets.remove(mapDocument)

    def refreshTilesetMenu(self):
        self.mTilesetMenu.clear()
        if (self.mTilesetMenuMapper):
            self.mTabBar.disconnect(self.mTilesetMenuMapper)
            del self.mTilesetMenuMapper

        self.mTilesetMenuMapper = QSignalMapper(self)
        self.mTilesetMenuMapper.mapped.connect(self.mTabBar.setCurrentIndex)
        currentIndex = self.mTabBar.currentIndex()
        for i in range(self.mTabBar.count()):
            action = QAction(self.mTabBar.tabText(i), self)
            action.setCheckable(True)
            self.mTilesetActionGroup.addAction(action)
            if (i == currentIndex):
                action.setChecked(True)
            self.mTilesetMenu.addAction(action)
            action.triggered.connect(self.mTilesetMenuMapper.map)
            self.mTilesetMenuMapper.setMapping(action, i)

    def setCurrentTile(self, tile):
        if (self.mCurrentTile == tile):
            return
        self.mCurrentTile = tile
        self.currentTileChanged.emit([tile])
        if (tile):
            self.mMapDocument.setCurrentObject(tile)

    def setCurrentTiles(self, tiles):
        if (self.mCurrentTiles == tiles):
            return
        del self.mCurrentTiles
        self.mCurrentTiles = tiles
        # Set the selected tiles on the map document
        if (tiles):
            selectedTiles = QList()
            for y in range(tiles.height()):
                for x in range(tiles.width()):
                    cell = tiles.cellAt(x, y)
                    if (not cell.isEmpty()):
                        selectedTiles.append(cell.tile)

            self.mMapDocument.setSelectedTiles(selectedTiles)

            # Create a tile stamp with these tiles
            map = self.mMapDocument.map()
            stamp = Map(map.orientation(), tiles.width(), tiles.height(),
                        map.tileWidth(), map.tileHeight())
            stamp.addLayer(tiles.clone())
            stamp.addTilesets(tiles.usedTilesets())

            self.mEmittingStampCaptured = True
            self.stampCaptured.emit(TileStamp(stamp))
            self.mEmittingStampCaptured = False

    def retranslateUi(self):
        self.setWindowTitle(self.tr("Tilesets"))
        self.mNewTileset.setText(self.tr("New Tileset"))
        self.mImportTileset.setText(self.tr("Import Tileset"))
        self.mExportTileset.setText(self.tr("Export Tileset As..."))
        self.mPropertiesTileset.setText(self.tr("Tileset Properties"))
        self.mDeleteTileset.setText(self.tr("Remove Tileset"))
        self.mEditTerrain.setText(self.tr("Edit Terrain Information"))
        self.mAddTiles.setText(self.tr("Add Tiles"))
        self.mRemoveTiles.setText(self.tr("Remove Tiles"))

    def currentTileset(self):
        index = self.mTabBar.currentIndex()
        if (index == -1):
            return None
        return self.mTilesets.at(index)

    def currentTilesetView(self):
        return self.mViewStack.currentWidget()

    def tilesetViewAt(self, index):
        return self.mViewStack.widget(index)

    def setupTilesetModel(self, view, tileset):
        view.setModel(TilesetModel(tileset, view))

        s = view.selectionModel()
        s.selectionChanged.connect(self.selectionChanged)
        s.currentChanged.connect(self.currentChanged)
        view.pressed.connect(self.indexPressed)
Exemplo n.º 38
0
class PreferencesDialog(QDialog):
    def __init__(self, mainwindow):
        super(PreferencesDialog, self).__init__(mainwindow)
        self.setWindowModality(Qt.WindowModal)
        if mainwindow:
            self.addAction(mainwindow.actionCollection.help_whatsthis)
        layout = QVBoxLayout()
        layout.setSpacing(10)
        self.setLayout(layout)

        # listview to the left, stacked widget to the right
        top = QHBoxLayout()
        layout.addLayout(top)

        self.pagelist = QListWidget(self)
        self.stack = QStackedWidget(self)
        top.addWidget(self.pagelist, 0)
        top.addWidget(self.stack, 2)

        layout.addWidget(widgets.Separator(self))

        b = self.buttons = QDialogButtonBox(self)
        b.setStandardButtons(QDialogButtonBox.Ok
                             | QDialogButtonBox.Cancel
                             | QDialogButtonBox.Apply
                             | QDialogButtonBox.Reset
                             | QDialogButtonBox.Help)
        layout.addWidget(b)
        b.accepted.connect(self.accept)
        b.rejected.connect(self.reject)
        b.button(QDialogButtonBox.Apply).clicked.connect(self.saveSettings)
        b.button(QDialogButtonBox.Reset).clicked.connect(self.loadSettings)
        b.button(QDialogButtonBox.Help).clicked.connect(self.showHelp)
        b.button(QDialogButtonBox.Help).setShortcut(QKeySequence.HelpContents)
        b.button(QDialogButtonBox.Apply).setEnabled(False)

        # fill the pagelist
        self.pagelist.setIconSize(QSize(32, 32))
        self.pagelist.setSpacing(2)
        for item in pageorder():
            self.pagelist.addItem(item())
        self.pagelist.currentItemChanged.connect(self.slotCurrentItemChanged)

        app.translateUI(self, 100)
        # read our size and selected page
        qutil.saveDialogSize(self, "preferences/dialog/size", QSize(500, 300))
        self.pagelist.setCurrentRow(_prefsindex)

    def translateUI(self):
        self.pagelist.setFixedWidth(self.pagelist.sizeHintForColumn(0) + 12)
        self.setWindowTitle(app.caption(_("Preferences")))

    def done(self, result):
        if result and self.buttons.button(QDialogButtonBox.Apply).isEnabled():
            self.saveSettings()
        # save our size and selected page
        global _prefsindex
        _prefsindex = self.pagelist.currentRow()
        super(PreferencesDialog, self).done(result)

    def pages(self):
        """Yields the settings pages that are already instantiated."""
        for n in range(self.stack.count()):
            yield self.stack.widget(n)

    def showHelp(self):
        userguide.show(self.pagelist.currentItem().help)

    def loadSettings(self):
        """Loads the settings on reset."""
        for page in self.pages():
            page.loadSettings()
            page.hasChanges = False
        self.buttons.button(QDialogButtonBox.Apply).setEnabled(False)

    def saveSettings(self):
        """Saves the settings and applies them."""
        for page in self.pages():
            if page.hasChanges:
                page.saveSettings()
                page.hasChanges = False
        self.buttons.button(QDialogButtonBox.Apply).setEnabled(False)

        # emit the signal
        app.settingsChanged()

    def slotCurrentItemChanged(self, item):
        item.activate()

    def changed(self):
        """Call this to enable the Apply button."""
        self.buttons.button(QDialogButtonBox.Apply).setEnabled(True)
Exemplo n.º 39
0
class Settings(QDialog):
    """Window showing the Settings/settings.

    Parameters
    ----------
    parent : instance of QMainWindow
        the main window
    """
    def __init__(self, parent):
        super().__init__(None, Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.parent = parent
        self.config = ConfigUtils(self.parent.refresh)

        self.setWindowTitle('Settings')
        self.create_settings()

    def create_settings(self):
        """Create the widget, organized in two parts.

        Notes
        -----
        When you add widgets in config, remember to update show_settings too
        """
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply |
                                QDialogButtonBox.Cancel)
        self.idx_ok = bbox.button(QDialogButtonBox.Ok)
        self.idx_apply = bbox.button(QDialogButtonBox.Apply)
        self.idx_cancel = bbox.button(QDialogButtonBox.Cancel)
        bbox.clicked.connect(self.button_clicked)

        page_list = QListWidget()
        page_list.setSpacing(1)
        page_list.currentRowChanged.connect(self.change_widget)

        pages = ['General', 'Overview', 'Signals', 'Channels', 'Spectrum',
                 'Notes', 'Video']
        for one_page in pages:
            page_list.addItem(one_page)

        self.stacked = QStackedWidget()
        self.stacked.addWidget(self.config)
        self.stacked.addWidget(self.parent.overview.config)
        self.stacked.addWidget(self.parent.traces.config)
        self.stacked.addWidget(self.parent.channels.config)
        self.stacked.addWidget(self.parent.spectrum.config)
        self.stacked.addWidget(self.parent.notes.config)
        self.stacked.addWidget(self.parent.video.config)

        hsplitter = QSplitter()
        hsplitter.addWidget(page_list)
        hsplitter.addWidget(self.stacked)

        btnlayout = QHBoxLayout()
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addWidget(hsplitter)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

    def change_widget(self, new_row):
        """Change the widget on the right side.

        Parameters
        ----------
        new_row : int
            index of the widgets
        """
        self.stacked.setCurrentIndex(new_row)

    def button_clicked(self, button):
        """Action when button was clicked.

        Parameters
        ----------
        button : instance of QPushButton
            which button was pressed
        """
        if button in (self.idx_ok, self.idx_apply):

            # loop over widgets, to see if they were modified
            for i_config in range(self.stacked.count()):
                one_config = self.stacked.widget(i_config)

                if one_config.modified:
                    lg.debug('Settings for ' + one_config.widget +
                             ' were modified')
                    one_config.get_values()

                    if self.parent.info.dataset is not None:
                        one_config.update_widget()
                    one_config.modified = False

            if button == self.idx_ok:
                self.accept()

        if button == self.idx_cancel:
            self.reject()
Exemplo n.º 40
0
class PyStream(QMainWindow):
    receive_signal = pyqtSignal(Metric)

    def __init__(self, rootPath):
        super().__init__()
        self.rootPath = rootPath
        self.__server = WebSocketServer(self)
        self.__server.start()
        self.__relay = PyRelay()
        self.__temp = PyTemp()
        self.__temp
        self.__temp.start()
        self.__stats_tab_index = 0
        self.__buttons_tab_index = 1
        self.enable_gui_switch = True
        self.timer = QTimer()
        self.timer.timeout.connect(self.__timer_tick)

        try:
            self.backlight = Backlight()
        except:
            self.fakeBacklightSysfs = FakeBacklightSysfs()
            self.fakeBacklightSysfs.__enter__()
            self.backlight = Backlight(
                backlight_sysfs_path=self.fakeBacklightSysfs.path)

        self.initUI()
        self.enable_screensaver()

    def initUI(self):
        logging.info("[GUI] Init main frame")
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setGeometry(0, 0, 800, 480)
        self.setWindowTitle("MediaDisplay-Server")
        self.setWindowIcon(QIcon("pystream/resource/pyalarm.png"))

        self.is_updating = False

        self.stack = QStackedWidget(self)
        self.stack.setGeometry(0, 0, 800, 480)
        self.panel_1 = QWidget()
        #self.panel_2 = QWidget()
        self.panel_3 = QWidget()
        self.stack.addWidget(self.panel_1)
        #self.stack.addWidget(self.panel_2)
        self.stack.addWidget(self.panel_3)

        #####################
        ##### Panel 1
        background_1 = QLabel(self.panel_1)
        background_1.setGeometry(0, 0, 800, 480)
        background_1.setStyleSheet(
            "background-image: url(pystream/resource/page_1.jpg);")

        self.gauge_cpu_1 = self.__create_gauge(self.panel_1, 95, 67)
        self.gauge_cpu_2 = self.__create_gauge(self.panel_1, 335, 67)
        self.gauge_cpu_3 = self.__create_gauge(self.panel_1, 580, 67)
        self.gauge_cpu_4 = self.__create_gauge(self.panel_1, 95, 230)
        self.gauge_cpu_5 = self.__create_gauge(self.panel_1, 335, 230)
        self.gauge_cpu_6 = self.__create_gauge(self.panel_1, 580, 230)

        self.label_cpu_1 = self.__create_label(self.panel_1,
                                               135,
                                               164,
                                               text="-- °C")
        self.label_cpu_2 = self.__create_label(self.panel_1,
                                               380,
                                               164,
                                               text="-- °C")
        self.label_cpu_3 = self.__create_label(self.panel_1,
                                               620,
                                               164,
                                               text="-- °C")
        self.label_cpu_4 = self.__create_label(self.panel_1,
                                               135,
                                               333,
                                               text="-- °C")
        self.label_cpu_5 = self.__create_label(self.panel_1,
                                               380,
                                               333,
                                               text="-- °C")
        self.label_cpu_6 = self.__create_label(self.panel_1,
                                               620,
                                               333,
                                               text="-- °C")

        self.__create_label(self.panel_1,
                            35,
                            384,
                            text="GPU",
                            font_size=20,
                            color="#FFFFFF")
        self.label_gpu_temp = self.__create_label(self.panel_1,
                                                  37,
                                                  419,
                                                  text="--°C",
                                                  font_size=15,
                                                  color="#FFFFFF")
        self.progress_gpu_load = self.__create_progressbar(
            self.panel_1, 95, 390, 174, 20)
        self.progress_gpu_mem_load = self.__create_progressbar(
            self.panel_1, 95, 420, 174, 20)

        self.__create_label(self.panel_1,
                            330,
                            395,
                            text="Down",
                            font_size=15,
                            color="#FFFFFF")
        self.__create_label(self.panel_1,
                            330,
                            419,
                            text="Up",
                            font_size=15,
                            color="#FFFFFF")
        self.label_net_down = self.__create_label(self.panel_1,
                                                  430,
                                                  395,
                                                  width=100,
                                                  height=25,
                                                  text="0",
                                                  font_size=15,
                                                  color="#FFFFFF")
        self.label_net_down.setAlignment(Qt.AlignRight)
        self.label_net_up = self.__create_label(self.panel_1,
                                                430,
                                                419,
                                                width=100,
                                                height=25,
                                                text="0",
                                                font_size=15,
                                                color="#FFFFFF")
        self.label_net_up.setAlignment(Qt.AlignRight)

        self.__create_label(self.panel_1,
                            546,
                            379,
                            text="Memory",
                            font_size=18,
                            color="#FFFFFF")
        self.progress_mem_load = self.__create_progressbar(
            self.panel_1, 551, 407, 203, 34)

        self.__create_button(self.panel_1, 774, 227, 26, 26, "arrow_right.png",
                             lambda: self.__change_page("Forward"))

        #####################
        ##### Panel 3
        background_3 = QLabel(self.panel_3)
        background_3.setGeometry(0, 0, 800, 480)
        background_3.setStyleSheet(
            "background-image: url(pystream/resource/page_2.jpg);")

        self.__create_button(self.panel_3,
                             125,
                             180,
                             100,
                             120,
                             "desk_lamp.png",
                             lambda: self.__relay.toggle_relay(PyRelay.BIG_2),
                             checkable=True)
        self.__create_button(
            self.panel_3,
            350,
            180,
            100,
            120,
            "keyboard.png",
            press=lambda: self.__relay.activate_relay(PyRelay.SMALL_1),
            release=lambda: self.__relay.deactivate_relay(PyRelay.SMALL_1))
        self.__create_button(self.panel_3,
                             575,
                             180,
                             100,
                             120,
                             "laptop.png",
                             lambda: self.__relay.toggle_relay(PyRelay.BIG_1),
                             checkable=True)

        self.__create_button(self.panel_3, 0, 227, 26, 26, "arrow_left.png",
                             lambda: self.__change_page("Backward"))

        self.label_room_temp = self.__create_label(self,
                                                   110,
                                                   0,
                                                   text="--°C",
                                                   color="#FFFFFF")
        self.label_time = self.__create_label(self,
                                              590,
                                              0,
                                              text="00:00",
                                              font_size=15,
                                              color="#FFFFFF")

        self.restore_gui()
        self.setCursor(QtCore.Qt.BlankCursor)
        logging.info("[GUI] Init done")
        self.timer.start(1000)
        self.receive_signal.connect(self.receive_gui)
        self.show()

    def __create_gauge(self, parent, x, y):
        gauge = AnalogGaugeWidget(parent)
        gauge.set_enable_fine_scaled_marker(False)
        gauge.set_enable_big_scaled_grid(False)
        gauge.set_enable_ScaleText(False)
        gauge.set_enable_CenterPoint(False)
        gauge.set_enable_Needle_Polygon(False)
        gauge.set_enable_barGraph(False)
        gauge.set_start_scale_angle(165)
        gauge.set_total_scale_angle_size(210)
        gauge.set_gauge_color_inner_radius_factor(600)
        gauge.set_MaxValue(100)
        gauge.setGeometry(x, y, 130, 130)
        gauge.update_value(50)
        gauge.set_DisplayValueColor(0, 255, 255)
        return gauge

    def __create_label(self,
                       parent,
                       x,
                       y,
                       width=None,
                       height=None,
                       text="",
                       font_size=15,
                       color="#00FFFF"):
        label = QLabel(parent)
        label.setText(text)
        font = QFont("Decorative", font_size)
        font.setBold(True)
        label.setFont(font)
        label.setStyleSheet("color: %s;" % color)
        if width is None or height is None:
            label.move(x, y)
        else:
            label.setGeometry(x, y, width, height)
        return label

    def __create_progressbar(self, parent, x, y, width, height):
        progress = GradiantProgressBar(parent)
        progress.setFormat("")
        progress.setValue(50)
        progress.setMaximum(100)
        progress.setGeometry(x, y, width, height)
        return progress

    def __create_button(self,
                        parent,
                        x,
                        y,
                        width,
                        height,
                        image,
                        click=None,
                        press=None,
                        release=None,
                        checkable=False):
        button = QPushButton(parent)
        button.setCheckable(checkable)
        if checkable:
            pressed_image = image.replace(".", "_pressed.")
            stre = "QPushButton {border-image: url(pystream/resource/" + image + ");} " \
                 + "QPushButton:checked {border-image: url(pystream/resource/" + pressed_image + ");}"
            button.setStyleSheet(stre)
        else:
            button.setStyleSheet("border-image: url(pystream/resource/" +
                                 image + ");")

        if click is not None:
            button.clicked.connect(click)
        if press is not None:
            button.pressed.connect(press)
        if release is not None:
            button.released.connect(release)

        button.setGeometry(x, y, width, height)
        button.setFlat(True)
        return button

    def __timer_tick(self):
        time = QDateTime.currentDateTime()
        timeDisplay = time.toString('hh:mm')
        temp = self.__temp.temperature

        self.label_time.setText(timeDisplay)
        self.label_room_temp.setText("%1.0f°C" % temp)

    def __change_page(self, direction):
        self.enable_gui_switch = False
        if direction == "Forward":
            if self.stack.currentIndex() < self.stack.count() - 1:
                self.stack.setCurrentIndex(self.stack.currentIndex() + 1)
        elif direction == "Backward":
            if self.stack.currentIndex() > 0:
                self.stack.setCurrentIndex(self.stack.currentIndex() - 1)

    def __send_key(self, key):
        msgObj = EventMessage(Action.Click, key)
        msg = json.dumps(msgObj.__dict__)
        self.__server.broadcast(msg)

    def udpate_gui(self, data: Metric):
        self.gauge_cpu_1.update_value(data.cpus[0].load)
        self.label_cpu_1.setText("%1.0f°C" % data.cpus[0].temperature)
        self.gauge_cpu_2.update_value(data.cpus[1].load)
        self.label_cpu_2.setText("%1.0f°C" % data.cpus[1].temperature)
        self.gauge_cpu_3.update_value(data.cpus[2].load)
        self.label_cpu_3.setText("%1.0f°C" % data.cpus[2].temperature)
        self.gauge_cpu_4.update_value(data.cpus[3].load)
        self.label_cpu_4.setText("%1.0f°C" % data.cpus[3].temperature)
        self.gauge_cpu_5.update_value(data.cpus[4].load)
        self.label_cpu_5.setText("%1.0f°C" % data.cpus[4].temperature)
        self.gauge_cpu_6.update_value(data.cpus[5].load)
        self.label_cpu_6.setText("%1.0f°C" % data.cpus[5].temperature)

        self.progress_mem_load.setValue(data.memory_load)

        self.label_gpu_temp.setText("%1.0f°C" % data.gpu.temperature)
        self.progress_gpu_load.setValue(data.gpu.load)
        self.progress_gpu_mem_load.setValue(data.gpu.memory_load)

        self.label_net_down.setText(data.network.down)
        self.label_net_up.setText(data.network.up)

    def receive_gui(self, data: Metric):
        if data.reset is not None and data.reset:
            logging.info("[GUI] Restoring initial image")
            self.restore_gui()
            self.enable_screensaver()
        else:
            if self.is_updating == False:
                self.is_updating = True
                try:
                    self.udpate_gui(data)
                    self.enable_gui()
                except Exception as e:
                    print(e)
                finally:
                    self.is_updating = False
            else:
                print("Gui is locked")
            self.disable_screensaver()

    def receive(self, data: Metric):
        if data is None:
            data = Metric(reset=True)
        self.receive_signal.emit(data)

        if data.send_display_brightness == True:
            msgObj = EventMessage(Action.Brightness, self.backlight.brightness)
            msg = json.dumps(msgObj.__dict__)
            self.__server.broadcast(msg)
        if data.display_brightness is not None and data.display_brightness >= 0 and data.display_brightness <= 100:
            self.backlight.brightness = data.display_brightness

    def enable_gui(self):
        if self.enable_gui_switch == True and self.stack.currentIndex(
        ) == self.__buttons_tab_index:
            self.stack.setCurrentIndex(self.__stats_tab_index)

    def restore_gui(self):
        self.enable_gui_switch = True

        self.stack.setCurrentIndex(2)
        self.label_cpu_1.setText("-- °C")
        self.label_cpu_2.setText("-- °C")
        self.label_cpu_3.setText("-- °C")
        self.label_cpu_4.setText("-- °C")
        self.label_cpu_5.setText("-- °C")
        self.label_cpu_6.setText("-- °C")

        self.gauge_cpu_1.update_value(50)
        self.gauge_cpu_2.update_value(50)
        self.gauge_cpu_3.update_value(50)
        self.gauge_cpu_4.update_value(50)
        self.gauge_cpu_5.update_value(50)
        self.gauge_cpu_6.update_value(50)

        self.progress_mem_load.setValue(50)

        self.label_gpu_temp.setText("%1.0f°C" % 0)
        self.progress_gpu_load.setValue(50)
        self.progress_gpu_mem_load.setValue(50)

        self.label_net_down.setText("0")
        self.label_net_up.setText("0")

        self.stack.setCurrentIndex(self.__buttons_tab_index)

    def update_app(self):
        GitUpdater.update(self.rootPath)

    def disable_screensaver(self):
        # disp = display.Display()
        # disp.set_screen_saver(0, 0, X.DontPreferBlanking, X.AllowExposures)
        # disp.sync()
        pyautogui.moveRel(0, 10)

    def enable_screensaver(self):
        disp = display.Display()
        screensaver = disp.get_screen_saver()
        if screensaver.timeout != 60:
            disp.set_screen_saver(60, 60, X.DefaultBlanking, X.AllowExposures)
            disp.sync()