Пример #1
0
class ProcessMonitorUI:
    def __init__(self, window: ProcessMonitorWindow):
        self.window = window

        self.main_widget = QWidget(window)
        self.main_layout = QVBoxLayout()
        self.layout_connection = QHBoxLayout()
        self.txt_conenction = QLineEdit()
        self.btn_connect = QPushButton()
        self.layout_connection.addWidget(self.txt_conenction)
        self.layout_connection.addWidget(self.btn_connect)
        self.process_list = ProcessListWidget()

        self.tabs_output = ProcessOutputTabsWidget()

        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.setMinimumSize(680, 540)
        policy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        policy.setHorizontalStretch(0)
        policy.setVerticalStretch(0)
        policy.setHeightForWidth(self.splitter.sizePolicy().hasHeightForWidth())
        self.splitter.setSizePolicy(policy)
        self.splitter.setStretchFactor(0, 1)
        self.splitter.setStretchFactor(1, 0)

        self.main_layout.addLayout(self.layout_connection)
        self.splitter.addWidget(self.process_list)
        self.splitter.addWidget(self.tabs_output)
        self.main_layout.addWidget(self.splitter)

        self.main_widget.setLayout(self.main_layout)
        self.statusbar = QStatusBar(window)

        self.txt_conenction.setPlaceholderText("ws://127.0.0.1:8766")
        self.txt_conenction.setText("ws://127.0.0.1:8766")
        self.btn_connect.setText("Connect")

        window.setCentralWidget(self.main_widget)
        window.setStatusBar(self.statusbar)
        window.setWindowTitle("Process Monitor")
        window.setWindowIcon(window.style().standardIcon(QStyle.SP_BrowserReload))
        self.set_disconnected_ui("Click on Connect to establish a connection")

    def set_disconnected_ui(self, msg: str):
        self.process_list.setDisabled(True)
        self.btn_connect.setDisabled(False)
        self.statusbar.showMessage(f"Disconnected. {msg}")

    def set_connected_ui(self):
        self.process_list.setDisabled(False)
        self.btn_connect.setDisabled(True)
        self.statusbar.showMessage("Connection established.")

    def handle_output(self, output: OutputEvent):
        self.tabs_output.append_output(output.uid, output.output)
Пример #2
0
class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()

        self.setWindowTitle("Status Bar")
        self.setGeometry(300, 300, 300, 300)
        self.createStatusBar()

    def createStatusBar(self):
        self.myStatus = QStatusBar()
        self.myStatus.showMessage("hello", 3000)
        self.setStatusBar(self.myStatus)
Пример #3
0
class SerialWindow(QWidget):

    def __init__(self, parent=None, port=None, baud=115200):

        QWidget.__init__(self, parent)

        self.alive = False

        self.console = Console(self)
        self.console.key_data_signal.connect(self.write_data)

        self.status_bar = QStatusBar(self)

        layout = QGridLayout(self)
        layout.addWidget(self.console)
        layout.addWidget(self.status_bar)

        ser = serial.serial_for_url(port, baudrate=baud, timeout=1)

        t = ReaderThread(ser, SerialProtocol)
        t.start()

        _, self.protocol = t.connect()
        self.protocol.bind_signals(self)

    def showStatusMessage(self, message):
        self.status_bar.showMessage(message)

    @Slot()
    def connected(self):
        self.showStatusMessage('opened')
        self.alive = True

    @Slot()
    def connect_losed(self):
        self.showStatusMessage('closed')
        self.alive = False

    @Slot(str)
    def ingoing_data(self, data):
        self.console.putData(data)

    @Slot(str, int)
    def write_data(self, data, is_binary=0):
        self.protocol.write_packet(data, is_binary)

    @Slot()
    def handle_error(self, error):
        self.showStatusMessage(error)
        pass
Пример #4
0
class MainWindow(QMainWindow):

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

        #private attributes
        self.menuBar_ = QMenuBar()
        self.fileMenu_ = FileMenu(self)
        self.editMenu_ = EditMenu(self)
        self.toolbarDoc_ = QDockWidget(self)
        self.toolbarWidget_ = Toolbar(self)

        self.setGeometry(0,0,1280,720)

        #public attributes
        self.application = application
        self.contentTab = ContentTab()
        self.statusBar = QStatusBar()

        #menuBar
        self.menuBar_.addMenu(self.fileMenu_)
        self.menuBar_.addMenu(self.editMenu_)
        self.menuBar_.addAction("Help",self.helpFunc)
        self.setMenuBar(self.menuBar_)
        #statusBar
        self.statusBar.showMessage("status bar")
        self.setStatusBar(self.statusBar)
        #toolBar
        self.toolbarDoc_.setFeatures(QDockWidget.NoDockWidgetFeatures )
        self.addDockWidget(Qt.LeftDockWidgetArea,self.toolbarDoc_)
        self.toolbarDoc_.setWidget(self.toolbarWidget_)
        #contentab
        self.setCentralWidget(self.contentTab)



    def helpFunc(self):
        """
        Call the help tab
        To be added
        """
        print("called helpFunc")



    def quit(self):
        """
        Quit the application
        """
        self.application.quit()
Пример #5
0
class xBanWindow(QMainWindow):
    """The main window of xban

    The main window serves three major purposes:
    - statusbar (sand the save button)
    - scrollable area
    """
    def __init__(self, base_path, file, file_config, parent=None):
        super().__init__(parent)

        board = BanBoard(file, file_config)
        board_area = QScrollArea()
        board_area.setWidget(board)
        board_area.setWidgetResizable(True)
        self.setCentralWidget(board_area)

        self.stbar = QStatusBar()

        # add a save button at the right bottom corner
        save_btn = BanButton(
            "save",
            objectName="appBtn_save",
            toolTip="save xban file",
            shortcut="Ctrl+S",
        )

        shadow = QGraphicsDropShadowEffect(self,
                                           blurRadius=10,
                                           offset=5,
                                           color=QColor("lightgrey"))
        save_btn.setGraphicsEffect(shadow)
        save_btn.pressed.connect(board.save_board)

        self.stbar.addPermanentWidget(save_btn)
        self.setStatusBar(self.stbar)
        log_handler = QLogHandler(self)
        root_logger = logging.getLogger()
        root_logger.addHandler(log_handler)
        log_handler.signal.log_msg.connect(
            partial(self.stbar.showMessage, timeout=1500))
        self.stbar.showMessage(f"Initiate {file}", 1500)
        self.show()

    def closeEvent(self, event):
        """Auto save when close"""

        self.centralWidget().widget().save_board()
        super().closeEvent(event)
Пример #6
0
class MainWindow(QMainWindow):
    # Constructor function
    def __init__(self):
        super(MainWindow, self).__init__()
        self.initGUI()

    def initGUI(self):
        self.setWindowTitle("Main Window")
        self.setGeometry(300, 250, 400, 300)
        self.CreateStatusBar()
        self.show()

    def CreateStatusBar(self):
        self.myStatusBar = QStatusBar()
        self.myStatusBar.showMessage('Ready PySide2', 2000)
        self.setStatusBar(self.myStatusBar)
Пример #7
0
class Main_Window(QMainWindow):
    '''Our Main Class Window'''
    def __init__(self):
        '''Constructor Function'''
        QMainWindow.__init__(self)
        self.setWindowTitle("Login Window")
        #self.setWindowIcon(QIcon("company_logo.png"))
        self.setGeometry(350, 250, 550, 400)  #self.setGeometry(x,y,w,h)
        self.setUp_GUI()

    def setUp_GUI(self):
        '''Generate all UI Elements'''
        #Menubar
        self.Create_MenuBar()
        #Statusbar
        self.Create_StatusBar()
        #Toolbar
        #Dock Widget
        #Central Widget
        self.Create_CentralWidget()
        return

    def Create_MenuBar(self):
        #Menu Bar Item Creation
        '''Function to Create Actual Menu Bar'''
        self.fileMenu = self.menuBar().addMenu("&File")
        self.editMenu = self.menuBar().addMenu("&Edit")
        self.helpMenu = self.menuBar().addMenu("&Help")

    def Create_StatusBar(self):
        '''Function to Create Status Bar'''
        self.myStatusBar = QStatusBar()
        self.myStatusBar.showMessage("Getting Ready", 2000)
        self.setStatusBar(self.myStatusBar)

    def Create_ToolBar(self):
        return

    def Create_DockWidget(self):
        return

    def Create_CentralWidget(self):
        self.Main_Layout = Layout_Main_Window()
        self.setCentralWidget(self.Main_Layout)
Пример #8
0
    def ui_setup(self):
        """Initialize user interface of main window."""
        loader = QUiLoader()
        file = QFile('./user_interface/form/main_window.ui')
        file.open(QFile.ReadOnly)
        self._window = loader.load(file)
        file.close()

        status_bar = QStatusBar(self._window)
        status_bar.showMessage(__copyright__)
        self._window.setStatusBar(status_bar)
        self._window.setWindowIcon(
            QIcon('./user_interface/media/bucketing_icon.jpeg'))
        self._window.setWindowTitle('PySide2 Project - Basic UI Framework')

        self._option_panel = OptionPanel()
        self._option_panel.add_button('DekBan',
                                      './user_interface/media/dekban.png')
        self._option_panel.add_button('Charlie',
                                      './user_interface/media/charlie.jpeg')
        self._option_panel.add_button('Simon',
                                      './user_interface/media/Simon.jpeg')

        # Add widget to main layout
        main_layout = self._window.main_layout
        main_layout.itemAtPosition(0, 0).setAlignment(QtCore.Qt.AlignCenter)
        main_layout.itemAtPosition(0, 1).setAlignment(QtCore.Qt.AlignVCenter)
        main_layout.addWidget(self._option_panel, 2, 0, 1, 1)

        # Add page widget to stack
        self._pages['item'] = ItemWidget()
        self._pages['text1'] = TextPage(text=PAUSE_TEXT)
        self._pages['text2'] = TextPage(text=STOP_TEXT)

        for index, name in enumerate(self._pages):
            print('pages {} : {} page'.format(index, name))
            self._window.widget_stack.addWidget(self._pages[name].widget)

        self._window.widget_stack.setCurrentIndex(0)

        # Build up signal / slot
        self._option_panel.currentItemChanged.connect(self.set_page)
Пример #9
0
class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle(
            "Pyside2 Status Bar")  # Configure le titre de la fenêtre
        self.setGeometry(300, 300, 500,
                         400)  # Configure la taille de la fenêtre

        self.setIcon()
        self.createStatusBar()

    def setIcon(self):
        appIcon = QIcon("icon.png")
        self.setWindowIcon(appIcon)

    def createStatusBar(self):
        self.myStatus = QStatusBar()
        self.myStatus.showMessage("Status Bar Is Ready", 3000)
        self.setStatusBar(self.myStatus)
Пример #10
0
class MainWindow(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("A Company Login Windows")
        self.setGeometry(300,250,400,300)
        
        #self.CreateStatusBar()
        self.CreateProgressBar()
        #self.showProgress()


    def CreateStatusBar(self):
        self.myStatusBar = QStatusBar()

        self.myStatusBar.showMessage('Ready',0)
        
        self.setStatusBar(self.myStatusBar)

    def CreateProgressBar(self):
        self.myStatusBar = QStatusBar()
        
        self.progressBar = QProgressBar()
        self.statusLabel = QLabel("Showing Progress")
        #self.finishLabel = QLabel("Finish Progress")
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(100)
        self.progressBar.setValue(0)
        self.myStatusBar.addWidget(self.statusLabel,1)
        self.myStatusBar.addWidget(self.progressBar,2)
        #self.myStatusBar.addWidget(self.finishLabel,3)

        self.setStatusBar(self.myStatusBar)

    def showProgress(self):
        while(self.progressBar.value() < self.progressBar.maximum()):
            self.progressBar.setValue(self.progressBar.value() + 10)
            time.sleep(1)
        self.statusLabel.setText('Ready') 
Пример #11
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        layout = QVBoxLayout()
        layout.setMargin(0)
        layout.setSpacing(0)

        self.menu_bar = MenuBar()
        layout.addWidget(self.menu_bar)

        self.main_area = MainArea()
        layout.addWidget(self.main_area)

        self.status = QStatusBar()
        self.status.showMessage('Application Started')
        layout.addWidget(self.status)

        self.setLayout(layout)
        HyperController.sub.onconnected.connect(self.connected)

    def connected(self):
        HyperController.post_connection()
Пример #12
0
class ListenerView(QMainWindow):
    def __init__(self, *args):
        super(ListenerView, self).__init__(*args)

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.window_branding()

        self.create_menu()
        self.create_main()
        self.create_status()

        self.create_container_view()
        self.create_audio_view()

    app = property(appref.app)

    def window_branding(self):
        """Setup our general branding for the window"""
        self.setWindowTitle(defaults.APP_NAME_HUMAN)
        self.setWindowIcon(icons.get_icon("microphone"))

    def create_menu(self):
        """Create the overall application menu"""
        self.menu = QMenuBar()
        app = self.app
        self.dictation_menu = self.menu.addMenu('&Dictation')
        self.dictation_menu.addAction(app.start_listening)
        self.dictation_menu.addAction(app.stop_listening)
        self.dictation_menu.addAction(app.reposition_overlay)
        self.setMenuBar(self.menu)

    def create_main(self):
        """Create our main central widgets"""
        self.mdi = QMdiArea(self)
        self.setCentralWidget(self.mdi)

    def create_status(self):
        """Create our listening status-bar"""
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage('%s is loading...' %
                                    (defaults.APP_NAME_HUMAN))

    def create_audio_view(self):
        """Create the audio control view"""
        self.audio_dock = QDockWidget("Audio", self)
        self.audio_dock.setFloating(False)
        self.audio_view = audioview.ListenerAudio(self)
        self.audio_dock.setWidget(self.audio_view)
        self.addDockWidget(
            Qt.LeftDockWidgetArea,
            self.audio_dock,
        )

    def create_container_view(self):
        """Create the container view"""
        self.container_dock = QDockWidget("Container", self)
        self.container_dock.setFloating(False)
        self.container_view = containersettings.ContainerSettings(self)
        self.container_dock.setWidget(self.container_view)
        self.addDockWidget(
            Qt.LeftDockWidgetArea,
            self.container_dock,
        )
Пример #13
0
class MetricsDialog(PluginDialog):

    ENABLE = True

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

        self.tab_widget = QTabWidget()
        self.status_bar = QStatusBar()

        self.meta_view = DictWidget()
        self.stat_view = DictWidget()
        self.ann_view = DictWidget()

        self.tab_widget.addTab(self.meta_view, "Metadata")
        self.tab_widget.addTab(self.stat_view, "Variants")
        self.tab_widget.addTab(self.ann_view, "Annotations")

        self.buttons = QDialogButtonBox(QDialogButtonBox.Ok)

        self.buttons.accepted.connect(self.accept)

        self.setWindowTitle(self.tr("Project metrics"))

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.tab_widget)
        v_layout.addWidget(self.status_bar)
        v_layout.addWidget(self.buttons)
        v_layout.setSpacing(0)
        self.setLayout(v_layout)

        self.resize(640, 480)
        # Async stuff
        self.metric_thread = None
        self.populate()

    def populate(self):
        """Async implementation to populate the view

        Notes:
            When closing the dialog window, the thread is not stopped.
        """
        def compute_metrics(conn):
            """Async function"""

            meta_data = sql.get_metadatas(conn)

            stats_data = {
                "Variant count": get_variant_count(conn),
                "Snp count": get_snp_count(conn),
                "Indel count": get_indel_count(conn),
                "Transition count": get_variant_transition(conn),
                "Transversion count": get_variant_transversion(conn),
                "Sample count": get_sample_count(conn),
            }

            stats_data["Tr/tv ratio"] = round(
                stats_data["Transition count"] /
                stats_data["Transversion count"], 2)

            if sql.table_exists(conn, "annotations"):
                genes_data = get_gene_counts(conn)
            else:
                gene_data = {}

            return meta_data, stats_data, genes_data

        self.status_bar.showMessage("Loading ...")
        self.metric_thread = SqlThread(self.conn, compute_metrics)
        self.metric_thread.result_ready.connect(self.loaded)
        self.metric_thread.start()

    def loaded(self):
        """Called at the end of the thread and populate data"""
        meta_data, stats_data, genes_data = self.metric_thread.results

        self.stat_view.set_dict(stats_data)
        self.meta_view.set_dict(meta_data)
        self.ann_view.set_dict(genes_data)
        self.status_bar.showMessage("")
class ToolSpecificationWidget(QWidget):
    def __init__(self, toolbox, tool_specification=None):
        """A widget to query user's preferences for a new tool specification.

        Args:
            toolbox (ToolboxUI): QMainWindow instance
            tool_specification (ToolSpecification): If given, the form is pre-filled with this specification
        """
        from ..ui.tool_specification_form import Ui_Form

        super().__init__(parent=toolbox, f=Qt.Window)  # Inherit stylesheet from ToolboxUI
        # Setup UI from Qt Designer file
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        # Class attributes
        self._toolbox = toolbox
        self._project = self._toolbox.project()
        # init models
        self.sourcefiles_model = QStandardItemModel()
        self.inputfiles_model = QStandardItemModel()
        self.inputfiles_opt_model = QStandardItemModel()
        self.outputfiles_model = QStandardItemModel()
        # Add status bar to form
        self.statusbar = QStatusBar(self)
        self.statusbar.setFixedHeight(20)
        self.statusbar.setSizeGripEnabled(False)
        self.statusbar.setStyleSheet(STATUSBAR_SS)
        self.ui.horizontalLayout_statusbar_placeholder.addWidget(self.statusbar)
        # init ui
        self.ui.treeView_sourcefiles.setModel(self.sourcefiles_model)
        self.ui.treeView_inputfiles.setModel(self.inputfiles_model)
        self.ui.treeView_inputfiles_opt.setModel(self.inputfiles_opt_model)
        self.ui.treeView_outputfiles.setModel(self.outputfiles_model)
        self.ui.treeView_sourcefiles.setStyleSheet(TREEVIEW_HEADER_SS)
        self.ui.treeView_inputfiles.setStyleSheet(TREEVIEW_HEADER_SS)
        self.ui.treeView_inputfiles_opt.setStyleSheet(TREEVIEW_HEADER_SS)
        self.ui.treeView_outputfiles.setStyleSheet(TREEVIEW_HEADER_SS)
        self.ui.comboBox_tooltype.addItem("Select type...")
        self.ui.comboBox_tooltype.addItems(TOOL_TYPES)
        # if a specification is given, fill the form with data from it
        if tool_specification:
            self.ui.lineEdit_name.setText(tool_specification.name)
            check_state = Qt.Checked if tool_specification.execute_in_work else Qt.Unchecked
            self.ui.checkBox_execute_in_work.setCheckState(check_state)
            self.ui.textEdit_description.setPlainText(tool_specification.description)
            self.ui.lineEdit_args.setText(" ".join(tool_specification.cmdline_args))
            tool_types = [x.lower() for x in TOOL_TYPES]
            index = tool_types.index(tool_specification.tooltype) + 1
            self.ui.comboBox_tooltype.setCurrentIndex(index)
        # Init lists
        self.main_program_file = ""
        self.sourcefiles = list(tool_specification.includes) if tool_specification else list()
        self.inputfiles = list(tool_specification.inputfiles) if tool_specification else list()
        self.inputfiles_opt = list(tool_specification.inputfiles_opt) if tool_specification else list()
        self.outputfiles = list(tool_specification.outputfiles) if tool_specification else list()
        self.def_file_path = tool_specification.def_file_path if tool_specification else None
        self.program_path = tool_specification.path if tool_specification else None
        self.definition = dict()
        # Get first item from sourcefiles list as the main program file
        try:
            self.main_program_file = self.sourcefiles.pop(0)
            self.ui.lineEdit_main_program.setText(os.path.join(self.program_path, self.main_program_file))
        except IndexError:
            pass  # sourcefiles list is empty
        # Populate lists (this will also create headers)
        self.populate_sourcefile_list(self.sourcefiles)
        self.populate_inputfiles_list(self.inputfiles)
        self.populate_inputfiles_opt_list(self.inputfiles_opt)
        self.populate_outputfiles_list(self.outputfiles)
        self.ui.lineEdit_name.setFocus()
        self.ui.label_mainpath.setText(self.program_path)
        # Add includes popup menu
        self.add_source_files_popup_menu = AddIncludesPopupMenu(self)
        self.ui.toolButton_add_source_files.setMenu(self.add_source_files_popup_menu)
        self.ui.toolButton_add_source_files.setStyleSheet('QToolButton::menu-indicator { image: none; }')
        # Add create new or add existing main program popup menu
        self.add_main_prgm_popup_menu = CreateMainProgramPopupMenu(self)
        self.ui.toolButton_add_main_program.setMenu(self.add_main_prgm_popup_menu)
        self.ui.toolButton_add_source_files.setStyleSheet('QToolButton::menu-indicator { image: none; }')
        self.ui.toolButton_add_cmdline_tag.setMenu(self._make_add_cmdline_tag_menu())
        self.connect_signals()

    def connect_signals(self):
        """Connect signals to slots."""
        self.ui.toolButton_add_source_files.clicked.connect(self.show_add_source_files_dialog)
        self.ui.toolButton_add_source_dirs.clicked.connect(self.show_add_source_dirs_dialog)
        self.ui.lineEdit_main_program.file_dropped.connect(self.set_main_program_path)
        self.ui.treeView_sourcefiles.files_dropped.connect(self.add_dropped_includes)
        self.ui.treeView_sourcefiles.doubleClicked.connect(self.open_includes_file)
        self.ui.toolButton_minus_source_files.clicked.connect(self.remove_source_files)
        self.ui.toolButton_plus_inputfiles.clicked.connect(self.add_inputfiles)
        self.ui.toolButton_minus_inputfiles.clicked.connect(self.remove_inputfiles)
        self.ui.toolButton_plus_inputfiles_opt.clicked.connect(self.add_inputfiles_opt)
        self.ui.toolButton_minus_inputfiles_opt.clicked.connect(self.remove_inputfiles_opt)
        self.ui.toolButton_plus_outputfiles.clicked.connect(self.add_outputfiles)
        self.ui.toolButton_minus_outputfiles.clicked.connect(self.remove_outputfiles)
        self.ui.pushButton_ok.clicked.connect(self.handle_ok_clicked)
        self.ui.pushButton_cancel.clicked.connect(self.close)
        # Enable removing items from QTreeViews by pressing the Delete key
        self.ui.treeView_sourcefiles.del_key_pressed.connect(self.remove_source_files_with_del)
        self.ui.treeView_inputfiles.del_key_pressed.connect(self.remove_inputfiles_with_del)
        self.ui.treeView_inputfiles_opt.del_key_pressed.connect(self.remove_inputfiles_opt_with_del)
        self.ui.treeView_outputfiles.del_key_pressed.connect(self.remove_outputfiles_with_del)

    def populate_sourcefile_list(self, items):
        """List source files in QTreeView.
        If items is None or empty list, model is cleared.
        """
        self.sourcefiles_model.clear()
        self.sourcefiles_model.setHorizontalHeaderItem(0, QStandardItem("Additional source files"))  # Add header
        if items is not None:
            for item in items:
                qitem = QStandardItem(item)
                qitem.setFlags(~Qt.ItemIsEditable)
                qitem.setData(QFileIconProvider().icon(QFileInfo(item)), Qt.DecorationRole)
                self.sourcefiles_model.appendRow(qitem)

    def populate_inputfiles_list(self, items):
        """List input files in QTreeView.
        If items is None or empty list, model is cleared.
        """
        self.inputfiles_model.clear()
        self.inputfiles_model.setHorizontalHeaderItem(0, QStandardItem("Input files"))  # Add header
        if items is not None:
            for item in items:
                qitem = QStandardItem(item)
                qitem.setData(QFileIconProvider().icon(QFileInfo(item)), Qt.DecorationRole)
                self.inputfiles_model.appendRow(qitem)

    def populate_inputfiles_opt_list(self, items):
        """List optional input files in QTreeView.
        If items is None or empty list, model is cleared.
        """
        self.inputfiles_opt_model.clear()
        self.inputfiles_opt_model.setHorizontalHeaderItem(0, QStandardItem("Optional input files"))  # Add header
        if items is not None:
            for item in items:
                qitem = QStandardItem(item)
                qitem.setData(QFileIconProvider().icon(QFileInfo(item)), Qt.DecorationRole)
                self.inputfiles_opt_model.appendRow(qitem)

    def populate_outputfiles_list(self, items):
        """List output files in QTreeView.
        If items is None or empty list, model is cleared.
        """
        self.outputfiles_model.clear()
        self.outputfiles_model.setHorizontalHeaderItem(0, QStandardItem("Output files"))  # Add header
        if items is not None:
            for item in items:
                qitem = QStandardItem(item)
                qitem.setData(QFileIconProvider().icon(QFileInfo(item)), Qt.DecorationRole)
                self.outputfiles_model.appendRow(qitem)

    @Slot(bool, name="browse_main_program")
    def browse_main_program(self, checked=False):
        """Open file browser where user can select the path of the main program file."""
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        answer = QFileDialog.getOpenFileName(self, "Add existing main program file", APPLICATION_PATH, "*.*")
        file_path = answer[0]
        if not file_path:  # Cancel button clicked
            return
        self.set_main_program_path(file_path)

    @Slot("QString", name="set_main_program_path")
    def set_main_program_path(self, file_path):
        """Set main program file and folder path."""
        folder_path = os.path.split(file_path)[0]
        self.program_path = os.path.abspath(folder_path)
        # Update UI
        self.ui.lineEdit_main_program.setText(file_path)
        self.ui.label_mainpath.setText(self.program_path)

    @Slot()
    def new_main_program_file(self):
        """Creates a new blank main program file. Let's user decide the file name and path.
         Alternative version using only one getSaveFileName dialog.
         """
        # noinspection PyCallByClass
        answer = QFileDialog.getSaveFileName(self, "Create new main program", APPLICATION_PATH)
        file_path = answer[0]
        if not file_path:  # Cancel button clicked
            return
        # Remove file if it exists. getSaveFileName has asked confirmation for us.
        try:
            os.remove(file_path)
        except OSError:
            pass
        try:
            with open(file_path, "w"):
                pass
        except OSError:
            msg = "Please check directory permissions."
            # noinspection PyTypeChecker, PyArgumentList, PyCallByClass
            QMessageBox.information(self, "Creating file failed", msg)
            return
        main_dir = os.path.dirname(file_path)
        self.program_path = os.path.abspath(main_dir)
        # Update UI
        self.ui.lineEdit_main_program.setText(file_path)
        self.ui.label_mainpath.setText(self.program_path)

    @Slot(name="new_source_file")
    def new_source_file(self):
        """Let user create a new source file for this tool specification."""
        path = self.program_path if self.program_path else APPLICATION_PATH
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        dir_path = QFileDialog.getSaveFileName(self, "Create source file", path, "*.*")
        file_path = dir_path[0]
        if file_path == '':  # Cancel button clicked
            return
        # create file. NOTE: getSaveFileName does the 'check for existence' for us
        open(file_path, 'w').close()
        self.add_single_include(file_path)

    @Slot(bool, name="show_add_source_files_dialog")
    def show_add_source_files_dialog(self, checked=False):
        """Let user select source files for this tool specification."""
        path = self.program_path if self.program_path else APPLICATION_PATH
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        answer = QFileDialog.getOpenFileNames(self, "Add source file", path, "*.*")
        file_paths = answer[0]
        if not file_paths:  # Cancel button clicked
            return
        for path in file_paths:
            if not self.add_single_include(path):
                continue

    @Slot(bool, name="show_add_source_dirs_dialog")
    def show_add_source_dirs_dialog(self, checked=False):
        """Let user select a source directory for this tool specification.
        All files and sub-directories will be added to the source files.
        """
        path = self.program_path if self.program_path else APPLICATION_PATH
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        answer = QFileDialog.getExistingDirectory(self, "Select a directory to add to source files", path)
        file_paths = list()
        for root, _, files in os.walk(answer):
            for file in files:
                file_paths.append(os.path.abspath(os.path.join(root, file)))
        for path in file_paths:
            if not self.add_single_include(path):
                continue

    @Slot("QVariant", name="add_dropped_includes")
    def add_dropped_includes(self, file_paths):
        """Adds dropped file paths to Source files list."""
        for path in file_paths:
            if not self.add_single_include(path):
                continue

    def add_single_include(self, path):
        """Add file path to Source files list."""
        dirname, file_pattern = os.path.split(path)
        # logging.debug("program path:{0}".format(self.program_path))
        # logging.debug("{0}, {1}".format(dirname, file_pattern))
        if not self.program_path:
            self.program_path = dirname
            self.ui.label_mainpath.setText(self.program_path)
            path_to_add = file_pattern
        else:
            # check if path is a descendant of main dir.
            common_prefix = os.path.commonprefix([os.path.abspath(self.program_path), os.path.abspath(path)])
            # logging.debug("common_prefix:{0}".format(common_prefix))
            if common_prefix != self.program_path:
                self.statusbar.showMessage(
                    "Source file {0}'s location is invalid " "(should be in main directory)".format(file_pattern), 5000
                )
                return False
            path_to_add = os.path.relpath(path, self.program_path)
        if self.sourcefiles_model.findItems(path_to_add):
            self.statusbar.showMessage("Source file {0} already included".format(path_to_add), 5000)
            return False
        qitem = QStandardItem(path_to_add)
        qitem.setFlags(~Qt.ItemIsEditable)
        qitem.setData(QFileIconProvider().icon(QFileInfo(path_to_add)), Qt.DecorationRole)
        self.sourcefiles_model.appendRow(qitem)
        return True

    @busy_effect
    @Slot("QModelIndex", name="open_includes_file")
    def open_includes_file(self, index):
        """Open source file in default program."""
        if not index:
            return
        if not index.isValid():
            self._toolbox.msg_error.emit("Selected index not valid")
            return
        includes_file = self.sourcefiles_model.itemFromIndex(index).text()
        _, ext = os.path.splitext(includes_file)
        if ext in [".bat", ".exe"]:
            self._toolbox.msg_warning.emit(
                "Sorry, opening files with extension <b>{0}</b> not implemented. "
                "Please open the file manually.".format(ext)
            )
            return
        url = "file:///" + os.path.join(self.program_path, includes_file)
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        res = QDesktopServices.openUrl(QUrl(url, QUrl.TolerantMode))
        if not res:
            self._toolbox.msg_error.emit("Failed to open file: <b>{0}</b>".format(includes_file))

    @Slot(name="remove_source_files_with_del")
    def remove_source_files_with_del(self):
        """Support for deleting items with the Delete key."""
        self.remove_source_files()

    @Slot(bool, name="remove_source_files")
    def remove_source_files(self, checked=False):
        """Remove selected source files from include list.
        Do not remove anything if there are no items selected.
        """
        indexes = self.ui.treeView_sourcefiles.selectedIndexes()
        if not indexes:  # Nothing selected
            self.statusbar.showMessage("Please select the source files to remove", 3000)
        else:
            rows = [ind.row() for ind in indexes]
            rows.sort(reverse=True)
            for row in rows:
                self.sourcefiles_model.removeRow(row)
            if self.sourcefiles_model.rowCount() == 0:
                if self.ui.lineEdit_main_program.text().strip() == "":
                    self.program_path = None
                    self.ui.label_mainpath.clear()
            self.statusbar.showMessage("Selected source files removed", 3000)

    @Slot(bool, name="add_inputfiles")
    def add_inputfiles(self, checked=False):
        """Let user select input files for this tool specification."""
        msg = (
            "Add an input file or a directory required by your program. Wildcards "
            "<b>are not</b> supported.<br/><br/>"
            "Examples:<br/>"
            "<b>data.csv</b> -> File is copied to the same work directory as the main program.<br/>"
            "<b>input/data.csv</b> -> Creates subdirectory /input to work directory and "
            "copies file data.csv there.<br/>"
            "<b>output/</b> -> Creates an empty directory into the work directory.<br/><br/>"
        )
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        answer = QInputDialog.getText(self, "Add input item", msg, flags=Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
        file_name = answer[0]
        if not file_name:  # Cancel button clicked
            return
        qitem = QStandardItem(file_name)
        qitem.setData(QFileIconProvider().icon(QFileInfo(file_name)), Qt.DecorationRole)
        self.inputfiles_model.appendRow(qitem)

    @Slot(name="remove_inputfiles_with_del")
    def remove_inputfiles_with_del(self):
        """Support for deleting items with the Delete key."""
        self.remove_inputfiles()

    @Slot(bool, name="remove_inputfiles")
    def remove_inputfiles(self, checked=False):
        """Remove selected input files from list.
        Do not remove anything if there are no items selected.
        """
        indexes = self.ui.treeView_inputfiles.selectedIndexes()
        if not indexes:  # Nothing selected
            self.statusbar.showMessage("Please select the input files to remove", 3000)
        else:
            rows = [ind.row() for ind in indexes]
            rows.sort(reverse=True)
            for row in rows:
                self.inputfiles_model.removeRow(row)
            self.statusbar.showMessage("Selected input files removed", 3000)

    @Slot(bool, name="add_inputfiles_opt")
    def add_inputfiles_opt(self, checked=False):
        """Let user select optional input files for this tool specification."""
        msg = (
            "Add optional input files that may be utilized by your program. <br/>"
            "Wildcards are supported.<br/><br/>"
            "Examples:<br/>"
            "<b>data.csv</b> -> If found, file is copied to the same work directory as the main program.<br/>"
            "<b>*.csv</b> -> All found CSV files are copied to the same work directory as the main program.<br/>"
            "<b>input/data_?.dat</b> -> All found files matching the pattern 'data_?.dat' will be copied to <br/>"
            "input/ subdirectory under the same work directory as the main program.<br/><br/>"
        )
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        answer = QInputDialog.getText(
            self, "Add optional input item", msg, flags=Qt.WindowTitleHint | Qt.WindowCloseButtonHint
        )
        file_name = answer[0]
        if not file_name:  # Cancel button clicked
            return
        qitem = QStandardItem(file_name)
        qitem.setData(QFileIconProvider().icon(QFileInfo(file_name)), Qt.DecorationRole)
        self.inputfiles_opt_model.appendRow(qitem)

    @Slot(name="remove_inputfiles_opt_with_del")
    def remove_inputfiles_opt_with_del(self):
        """Support for deleting items with the Delete key."""
        self.remove_inputfiles_opt()

    @Slot(bool, name="remove_inputfiles_opt")
    def remove_inputfiles_opt(self, checked=False):
        """Remove selected optional input files from list.
        Do not remove anything if there are no items selected.
        """
        indexes = self.ui.treeView_inputfiles_opt.selectedIndexes()
        if not indexes:  # Nothing selected
            self.statusbar.showMessage("Please select the optional input files to remove", 3000)
        else:
            rows = [ind.row() for ind in indexes]
            rows.sort(reverse=True)
            for row in rows:
                self.inputfiles_opt_model.removeRow(row)
            self.statusbar.showMessage("Selected optional input files removed", 3000)

    @Slot(bool, name="add_outputfiles")
    def add_outputfiles(self, checked=False):
        """Let user select output files for this tool specification."""
        msg = (
            "Add output files that will be archived into the Tool results directory after the <br/>"
            "Tool specification has finished execution. Wildcards are supported.<br/><br/>"
            "Examples:<br/>"
            "<b>results.csv</b> -> File is copied from work directory into results.<br/> "
            "<b>*.csv</b> -> All CSV files will copied into results.<br/> "
            "<b>output/*.gdx</b> -> All GDX files from the work subdirectory /output will be copied into <br/>"
            "results /output subdirectory.<br/><br/>"
        )
        # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
        answer = QInputDialog.getText(self, "Add output item", msg, flags=Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
        file_name = answer[0]
        if not file_name:  # Cancel button clicked
            return
        qitem = QStandardItem(file_name)
        qitem.setData(QFileIconProvider().icon(QFileInfo(file_name)), Qt.DecorationRole)
        self.outputfiles_model.appendRow(qitem)

    @Slot(name="remove_outputfiles_with_del")
    def remove_outputfiles_with_del(self):
        """Support for deleting items with the Delete key."""
        self.remove_outputfiles()

    @Slot(bool, name="remove_outputfiles")
    def remove_outputfiles(self, checked=False):
        """Remove selected output files from list.
        Do not remove anything if there are no items selected.
        """
        indexes = self.ui.treeView_outputfiles.selectedIndexes()
        if not indexes:  # Nothing selected
            self.statusbar.showMessage("Please select the output files to remove", 3000)
        else:
            rows = [ind.row() for ind in indexes]
            rows.sort(reverse=True)
            for row in rows:
                self.outputfiles_model.removeRow(row)
            self.statusbar.showMessage("Selected output files removed", 3000)

    @Slot()
    def handle_ok_clicked(self):
        """Checks that everything is valid, creates Tool spec definition dictionary and adds Tool spec to project."""
        # Check that tool type is selected
        if self.ui.comboBox_tooltype.currentIndex() == 0:
            self.statusbar.showMessage("Tool type not selected", 3000)
            return
        self.definition["name"] = self.ui.lineEdit_name.text()
        self.definition["description"] = self.ui.textEdit_description.toPlainText()
        self.definition["tooltype"] = self.ui.comboBox_tooltype.currentText().lower()
        flags = Qt.MatchContains
        # Check that path of main program file is valid before saving it
        main_program = self.ui.lineEdit_main_program.text().strip()
        if not os.path.isfile(main_program):
            self.statusbar.showMessage("Main program file is not valid", 6000)
            return
        # Fix for issue #241
        folder_path, file_path = os.path.split(main_program)
        self.program_path = os.path.abspath(folder_path)
        self.ui.label_mainpath.setText(self.program_path)
        self.definition["execute_in_work"] = self.ui.checkBox_execute_in_work.isChecked()
        self.definition["includes"] = [file_path]
        self.definition["includes"] += [i.text() for i in self.sourcefiles_model.findItems("", flags)]
        self.definition["inputfiles"] = [i.text() for i in self.inputfiles_model.findItems("", flags)]
        self.definition["inputfiles_opt"] = [i.text() for i in self.inputfiles_opt_model.findItems("", flags)]
        self.definition["outputfiles"] = [i.text() for i in self.outputfiles_model.findItems("", flags)]
        # Strip whitespace from args before saving it to JSON
        self.definition["cmdline_args"] = ToolSpecification.split_cmdline_args(self.ui.lineEdit_args.text())
        for k in REQUIRED_KEYS:
            if not self.definition[k]:
                self.statusbar.showMessage("{} missing".format(k), 3000)
                return
        # Create new Tool specification
        short_name = self.definition["name"].lower().replace(" ", "_")
        self.def_file_path = os.path.join(self.program_path, short_name + ".json")
        if self.call_add_tool_specification():
            self.close()

    def call_add_tool_specification(self):
        """Adds or updates Tool specification according to user's selections.
        If the name is the same as an existing tool specification, it is updated and
        auto-saved to the definition file. (User is editing an existing
        tool specification.) If the name is not in the tool specification model, creates
        a new tool specification and offer to save the definition file. (User is
        creating a new tool specification from scratch or spawning from an existing one).
        """
        # Load tool specification
        path = self.program_path
        tool = self._project.load_tool_specification_from_dict(self.definition, path)
        if not tool:
            self.statusbar.showMessage("Adding Tool specification failed", 3000)
            return False
        # Check if a tool specification with this name already exists
        row = self._toolbox.tool_specification_model.tool_specification_row(tool.name)
        if row >= 0:  # NOTE: Row 0 at this moment has 'No tool', but in the future it may change. Better be ready.
            old_tool = self._toolbox.tool_specification_model.tool_specification(row)
            def_file = old_tool.get_def_path()
            tool.set_def_path(def_file)
            if tool.__dict__ == old_tool.__dict__:  # Nothing changed. We're done here.
                return True
            # logging.debug("Updating definition for tool specification '{}'".format(tool.name))
            self._toolbox.update_tool_specification(row, tool)
        else:
            # noinspection PyCallByClass, PyTypeChecker, PyArgumentList
            answer = QFileDialog.getSaveFileName(
                self, "Save Tool specification file", self.def_file_path, "JSON (*.json)"
            )
            if answer[0] == "":  # Cancel button clicked
                return False
            def_file = os.path.abspath(answer[0])
            tool.set_def_path(def_file)
            self._toolbox.add_tool_specification(tool)
        # Save path of main program file relative to definition file in case they differ
        def_path = os.path.dirname(def_file)
        if def_path != self.program_path:
            self.definition["includes_main_path"] = os.path.relpath(self.program_path, def_path)
        # Save file descriptor
        with open(def_file, "w") as fp:
            try:
                json.dump(self.definition, fp, indent=4)
            except ValueError:
                self.statusbar.showMessage("Error saving file", 3000)
                self._toolbox.msg_error.emit("Saving Tool specification file failed. Path:{0}".format(def_file))
                return False
        return True

    def keyPressEvent(self, e):
        """Close Setup form when escape key is pressed.

        Args:
            e (QKeyEvent): Received key press event.
        """
        if e.key() == Qt.Key_Escape:
            self.close()

    def closeEvent(self, event=None):
        """Handle close window.

        Args:
            event (QEvent): Closing event if 'X' is clicked.
        """
        if event:
            event.accept()

    def _make_add_cmdline_tag_menu(self):
        """Constructs a popup menu for the '@@' button."""
        menu = QMenu(self.ui.toolButton_add_cmdline_tag)
        action = menu.addAction(str(CmdlineTag.URL_INPUTS))
        action.triggered.connect(self._add_cmdline_tag_url_inputs)
        action.setToolTip("Insert a tag that is replaced by all input database URLs.")
        action = menu.addAction(str(CmdlineTag.URL_OUTPUTS))
        action.triggered.connect(self._add_cmdline_tag_url_outputs)
        action.setToolTip("Insert a tag that is replaced be all output database URLs.")
        action = menu.addAction(str(CmdlineTag.URL))
        action.triggered.connect(self._add_cmdline_tag_data_store_url)
        action.setToolTip("Insert a tag that is replaced by the URL provided by Data Store '<data-store-name>'.")
        action = menu.addAction(str(CmdlineTag.OPTIONAL_INPUTS))
        action.triggered.connect(self._add_cmdline_tag_optional_inputs)
        action.setToolTip("Insert a tag that is replaced by a list of optional input files.")
        return menu

    def _insert_spaces_around_tag_in_args_edit(self, tag_length, restore_cursor_to_tag_end=False):
        """
        Inserts spaces before/after @@ around cursor position/selection

        Expects cursor to be at the end of the tag.
        """
        args_edit = self.ui.lineEdit_args
        text = args_edit.text()
        cursor_position = args_edit.cursorPosition()
        if cursor_position == len(text) or (cursor_position < len(text) - 1 and not text[cursor_position].isspace()):
            args_edit.insert(" ")
            appended_spaces = 1
            text = args_edit.text()
        else:
            appended_spaces = 0
        tag_start = cursor_position - tag_length
        if tag_start > 1 and text[tag_start - 2 : tag_start] == CMDLINE_TAG_EDGE:
            args_edit.setCursorPosition(tag_start)
            args_edit.insert(" ")
            prepended_spaces = 1
        else:
            prepended_spaces = 0
        if restore_cursor_to_tag_end:
            args_edit.setCursorPosition(cursor_position + prepended_spaces)
        else:
            args_edit.setCursorPosition(cursor_position + appended_spaces + prepended_spaces)

    @Slot("QAction")
    def _add_cmdline_tag_url_inputs(self, _):
        """Inserts @@url_inputs@@ tag to command line arguments."""
        tag = CmdlineTag.URL_INPUTS
        self.ui.lineEdit_args.insert(tag)
        self._insert_spaces_around_tag_in_args_edit(len(tag))

    @Slot("QAction")
    def _add_cmdline_tag_url_outputs(self, _):
        """Inserts @@url_outputs@@ tag to command line arguments."""
        tag = CmdlineTag.URL_OUTPUTS
        self.ui.lineEdit_args.insert(tag)
        self._insert_spaces_around_tag_in_args_edit(len(tag))

    @Slot("QAction")
    def _add_cmdline_tag_data_store_url(self, _):
        """Inserts @@url:<data-store-name>@@ tag to command line arguments and selects '<data-store-name>'."""
        args_edit = self.ui.lineEdit_args
        tag = CmdlineTag.URL
        args_edit.insert(tag)
        self._insert_spaces_around_tag_in_args_edit(len(tag), restore_cursor_to_tag_end=True)
        cursor_position = args_edit.cursorPosition()
        args_edit.setSelection(cursor_position - len(CMDLINE_TAG_EDGE + "<data-store_name>"), len("<data-store_name>"))

    @Slot("QAction")
    def _add_cmdline_tag_optional_inputs(self, _):
        """Inserts @@optional_inputs@@ tag to command line arguments."""
        tag = CmdlineTag.OPTIONAL_INPUTS
        self.ui.lineEdit_args.insert(tag)
        self._insert_spaces_around_tag_in_args_edit(len(tag))
Пример #15
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        # 设置时间
        self.__time = QDateTime()

        # 创建图形界面
        self.__SetupUI()

        self.__ConfigureFileToolBar()

        self.__ConfigureTranslateToolBar()

        self.__ConfigureMainLayout()

        # 设置ComboBox的显示内容
        self.__ConfigureComboBox()

        # 设置保存文件的信息
        self.__currentPath = QDir.currentPath()

        self.__ConfigureSysIniFile()

        self.__ConfigureHistoryFile()

        salt = random.randint(1466000000, 1467000000)
        self.__salt = str(salt)

        self.__httpClient = None

        self.__statusBar.showMessage(
            self.__GetCurrentTime() + " - Please choose a file or enter data!",
            2000)

        self.show()

    def __del__(self):
        self.__historyFile.close()

    def __SetupUI(self):
        self.__mainWidget = QWidget()
        self.setCentralWidget(self.__mainWidget)

        self.__fileToolBar = QToolBar("File")
        self.addToolBar(self.__fileToolBar)
        self.__translateToolBar = QToolBar("Translate")
        self.addToolBar(self.__translateToolBar)

        self.__statusBar = QStatusBar()
        self.setStatusBar(self.__statusBar)

        self.setWindowIcon(QIcon("./image/translate.png"))

    def __ConfigureFileToolBar(self):
        self.__openFileAction = QAction(QIcon("./image/open.png"), "Open File")
        self.__fileToolBar.addAction(self.__openFileAction)
        self.__openFileAction.triggered.connect(
            self.__on_triggered_openFileAction)

        self.__saveFileAction = QAction(QIcon("./image/save.png"), "Save File")
        self.__fileToolBar.addAction(self.__saveFileAction)
        self.__saveFileAction.triggered.connect(
            self.__on_triggered_saveFileAction)

    def __ConfigureTranslateToolBar(self):
        self.__translateAction = QAction(QIcon("./image/translate.png"),
                                         "Translate")
        self.__translateToolBar.addAction(self.__translateAction)
        self.__translateAction.triggered.connect(
            self.__on_triggered_translateAction)

        self.__clearAction = QAction(QIcon("./image/clear.png"), "Clear")
        self.__translateToolBar.addAction(self.__clearAction)
        self.__clearAction.triggered.connect(self.__on_triggered_clearAction)

    def __ConfigureMainLayout(self):
        self.__appIDLabel = QLabel("AppID:")
        self.__appIDLineEdit = QLineEdit(self)
        self.__appIDLineEdit.returnPressed.connect(
            self.__on_returnPressed_appIDLineEdit)

        self.__keyLabel = QLabel("Key:")
        self.__keyLineEdit = QLineEdit(self)
        self.__keyLineEdit.returnPressed.connect(
            self.__on_returnPressed_keyLineEdit)

        self.__fromLabel = QLabel("From:")
        self.__fromComboBox = QComboBox(self)
        self.__toLabel = QLabel("To:")
        self.__toComboBox = QComboBox(self)

        self.__srcLabel = QLabel("Src Text:")
        self.__srcTextEdit = QTextEdit(self)

        self.__translateLabel = QLabel("Translate Text:")
        self.__translateTextEdit = QTextEdit(self)

        self.__mainLayout = QGridLayout()
        self.__mainLayout.addWidget(self.__appIDLabel, 0, 0)
        self.__mainLayout.addWidget(self.__appIDLineEdit, 0, 1, 1, 3)
        self.__mainLayout.addWidget(self.__keyLabel, 1, 0)
        self.__mainLayout.addWidget(self.__keyLineEdit, 1, 1, 1, 3)
        self.__mainLayout.addWidget(self.__fromLabel, 2, 0)
        self.__mainLayout.addWidget(self.__fromComboBox, 2, 1)
        self.__mainLayout.addWidget(self.__toLabel, 2, 2)
        self.__mainLayout.addWidget(self.__toComboBox, 2, 3)
        self.__mainLayout.addWidget(self.__srcLabel, 3, 0)
        self.__mainLayout.addWidget(self.__srcTextEdit, 3, 1, 1, 3)
        self.__mainLayout.addWidget(self.__translateLabel, 4, 0)
        self.__mainLayout.addWidget(self.__translateTextEdit, 4, 1, 1, 3)

        self.__mainWidget.setLayout(self.__mainLayout)

    def __ConfigureComboBox(self):
        self.__fromComboBox.clear()
        self.__fromComboBox.addItem("English", "en")
        self.__fromComboBox.addItem("Chinese", "zh")
        self.__fromComboBox.addItem("Auto", "auto")
        self.__fromComboBox.setCurrentIndex(2)

        self.__toComboBox.clear()
        self.__toComboBox.addItem("English", "en")
        self.__toComboBox.addItem("Chinese", "zh")
        self.__toComboBox.setCurrentIndex(1)

    def __ConfigureSysIniFile(self):
        iniFile = QFile("info.ini")

        if iniFile.exists():
            self.__infoSettings = QSettings("info.ini", QSettings.IniFormat)

            self.__appIDLineEdit.setText(
                self.__infoSettings.value("info/appid"))
            self.__keyLineEdit.setText(self.__infoSettings.value("info/key"))

        else:
            if iniFile.open(QFile.ReadWrite):
                self.__infoSettings = QSettings("info.ini",
                                                QSettings.IniFormat)

                self.__infoSettings.beginGroup("info")

                self.__infoSettings.setValue("appid", "00000000000000000")
                self.__infoSettings.setValue("key", "00000000000000000")
                self.__infoSettings.endGroup()

                iniFile.close()

                self.__appIDLineEdit.setText("00000000000000000")
                self.__keyLineEdit.setText("00000000000000000")

    def __ConfigureHistoryFile(self):
        self.__historyFile = QFile("history.txt")
        if self.__historyFile.open(QFile.ReadWrite or QFile.Append):
            self.__historyFileStream = QTextStream(self.__historyFile)

        else:
            self.__statusBar.showMessage(
                self.__GetCurrentTime() + " - History fie create failed!",
                2000)

    def __GetCurrentTime(self, format="hh:mm:ss"):
        return self.__time.currentDateTime().toString(format)

    def __GetSign(self, srcData, appid, key):
        sign = appid + srcData + self.__salt + key
        mymd5 = hashlib.md5(sign.encode()).hexdigest()
        return mymd5

    def __on_triggered_openFileAction(self):
        filePath = QFileDialog.getOpenFileName(self, "open",
                                               self.__currentPath, "* txt")
        if filePath[0].strip():
            file = QFile(filePath[0])
            if file.open(QFile.ReadOnly):

                readData = str(file.readAll())
                fileData = readData[2:readData.__len__() - 1].replace(
                    r"\r\n", r" ")
                self.__srcTextEdit.insertPlainText(fileData)
            else:
                self.__statusBar.showMessage(
                    self.__GetCurrentTime() + " - open file failed!", 2000)
        else:
            self.__statusBar.showMessage(
                self.__GetCurrentTime() + " - Choose file failed!", 2000)
            return

    def __on_triggered_saveFileAction(self):
        self.__historyFile.close()
        self.__historyFile.open(QFile.ReadWrite or QFile.Append)

        self.__statusBar.showMessage(
            self.__GetCurrentTime() + " - Save file successful!", 2000)

    def __on_triggered_translateAction(self):
        srcData = self.__srcTextEdit.toPlainText()
        if srcData.strip():
            self.__translateTextEdit.clear()
            self.__translateTextEdit.document().clear()

            dstData = srcData.replace("\n", " ")

        else:
            self.__statusBar.showMessage(
                self.__GetCurrentTime() + " - There is no data!", 2000)
            return

        myurl = "/api/trans/vip/translate"

        appid = self.__appIDLineEdit.text()
        key = self.__keyLineEdit.text()
        strFrom = self.__fromComboBox.currentData()
        strTo = self.__toComboBox.currentData()
        mymd5 = self.__GetSign(dstData, appid, key)

        myurl = myurl + "?appid=" + appid + "&q=" + urllib.parse.quote(
            srcData
        ) + "&from=" + strFrom + "&to=" + strTo + "&salt=" + self.__salt + "&sign=" + mymd5

        httpClient = http.client.HTTPConnection("api.fanyi.baidu.com")
        httpClient.request("GET", myurl)

        response = httpClient.getresponse()
        jsonResponse = response.read().decode("utf-8")

        js = json.loads(jsonResponse)
        translate = str(js["trans_result"][0]["dst"])

        self.__translateTextEdit.insertPlainText(translate)
        self.__historyFileStream << translate + "\r\n"

        self.__statusBar.showMessage(
            self.__GetCurrentTime() + " - Translate successful!", 2000)

    def __on_triggered_clearAction(self):
        self.__srcTextEdit.clear()
        self.__translateTextEdit.clear()

        self.__statusBar.showMessage(
            self.__GetCurrentTime() + " - Clear successful!", 2000)

    def __on_returnPressed_appIDLineEdit(self):
        appid = self.__appIDLineEdit.text()
        if not appid.strip():
            self.__statusBar.showMessage(
                self.__GetCurrentTime() + " - There is no appid!", 2000)
            return

        self.__infoSettings.beginGroup("info")
        self.__infoSettings.setValue("appid", appid)
        self.__infoSettings.endGroup()
        self.__statusBar.showMessage(
            self.__GetCurrentTime() + " - Enter appid successful!", 2000)

    def __on_returnPressed_keyLineEdit(self):
        key = self.__keyLineEdit.text()
        if not key.strip():
            self.__statusBar.showMessage(
                self.__GetCurrentTime() + " - There is no key!", 2000)
            return

        self.__infoSettings.beginGroup("info")
        self.__infoSettings.setValue("key", key)
        self.__infoSettings.endGroup()
        self.__statusBar.showMessage(
            self.__GetCurrentTime() + " - Enter key successful!", 2000)
Пример #16
0
class MainWindow(QMainWindow):
    """ Our Main Window class"""
    def __init__(self, fileName=None):
        """ Constructor Function"""
        QMainWindow.__init__(self)
        self.setWindowTitle("A Simple Text Editor")
        self.setWindowIcon(QIcon('appicon.png'))
        self.setGeometry(100, 100, 800, 600)
        self.textEdit = QTextEdit()
        self.setCentralWidget(self.textEdit)
        self.fileName = None
        self.filters = "Text files (*.txt)"

    def SetupComponents(self):
        """ Function to setup status bar, central widget, menu bar, tool bar"""
        self.myStatusBar = QStatusBar()
        self.setStatusBar(self.myStatusBar)
        self.myStatusBar.showMessage('Ready', 10000)
        self.CreateActions()
        self.CreateMenus()
        self.CreateToolBar()
        self.fileMenu.addAction(self.newAction)
        self.fileMenu.addAction(self.openAction)
        self.fileMenu.addAction(self.saveAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAction)
        self.editMenu.addAction(self.cutAction)
        self.editMenu.addAction(self.copyAction)
        self.editMenu.addAction(self.pasteAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.undoAction)
        self.editMenu.addAction(self.redoAction)
        self.editMenu.addSeparator()
        self.editMenu.addAction(self.selectAllAction)
        self.formatMenu.addAction(self.fontAction)
        self.helpMenu.addAction(self.aboutAction)
        self.helpMenu.addSeparator()
        self.helpMenu.addAction(self.aboutQtAction)
        self.mainToolBar.addAction(self.newAction)
        self.mainToolBar.addAction(self.openAction)
        self.mainToolBar.addAction(self.saveAction)
        self.mainToolBar.addSeparator()
        self.mainToolBar.addAction(self.cutAction)
        self.mainToolBar.addAction(self.copyAction)
        self.mainToolBar.addAction(self.pasteAction)
        self.mainToolBar.addSeparator()
        self.mainToolBar.addAction(self.undoAction)
        self.mainToolBar.addAction(self.redoAction)

        # Slots called when the menu actions are triggered
    def newFile(self):
        self.textEdit.setText('')
        self.fileName = None

    def openFile(self):
        self.fileName, self.filterName = QFileDialog.getOpenFileName(self)
        self.textEdit.setText(open(self.fileName).read())

    def saveFile(self):
        if (self.fileName == None or self.fileName == ''):
            self.fileName, self.filterName = QFileDialog.getSaveFileName(
                self, filter=self.filters)
        if (self.fileName != ''):
            file = open(self.fileName, 'w')
            file.write(self.textEdit.toPlainText())
            self.statusBar().showMessage("File saved", 2000)

    def exitFile(self):
        self.close()

    def fontChange(self):
        (font, ok) = QFontDialog.getFont(QFont("Helvetica[Cronyx]", 10), self)
        if ok:
            self.textEdit.setCurrentFont(font)

    def aboutHelp(self):
        QMessageBox.about(
            self, "About Simple Text Editor",
            "A Simple Text Editor where you can edit and save files")

    def CreateActions(self):
        '''Function to create actions for menus'''
        self.newAction = QAction(QIcon('doc_write.png'),
                                 '&New',
                                 self,
                                 shortcut=QKeySequence.New,
                                 statusTip="Create a New File",
                                 triggered=self.newFile)

        self.openAction = QAction(QIcon('doc_write.png'),
                                  'O&pen',
                                  self,
                                  shortcut=QKeySequence.Open,
                                  statusTip="Open an existing file",
                                  triggered=self.openFile)
        self.saveAction = QAction(QIcon('doc_write.png'),
                                  '&Save',
                                  self,
                                  shortcut=QKeySequence.Save,
                                  statusTip="Save the current file to disk",
                                  triggered=self.saveFile)
        self.exitAction = QAction(QIcon('doc_write.png'),
                                  'E&xit',
                                  self,
                                  shortcut="Ctrl+Q",
                                  statusTip="Exit the Application",
                                  triggered=self.exitFile)
        self.cutAction = QAction(
            QIcon('doc_write.png'),
            'C&ut',
            self,
            shortcut=QKeySequence.Cut,
            statusTip="Cut the current selection to clipboard",
            triggered=self.textEdit.cut)
        self.copyAction = QAction(
            QIcon('doc_write.png'),
            'C&opy',
            self,
            shortcut=QKeySequence.Copy,
            statusTip="Copy the current selection to clipboard",
            triggered=self.textEdit.copy)
        self.pasteAction = QAction(
            QIcon('doc_write.png'),
            '&Paste',
            self,
            shortcut=QKeySequence.Paste,
            statusTip="Paste the clipboard's content in current location",
            triggered=self.textEdit.paste)
        self.selectAllAction = QAction(QIcon('doc_write.png'),
                                       'Select All',
                                       self,
                                       statusTip="Select All",
                                       triggered=self.textEdit.selectAll)
        self.redoAction = QAction(QIcon('doc_write.png'),
                                  'Redo',
                                  self,
                                  shortcut=QKeySequence.Redo,
                                  statusTip="Redo previous action",
                                  triggered=self.textEdit.redo)
        self.undoAction = QAction(QIcon('doc_write.png'),
                                  'Undo',
                                  self,
                                  shortcut=QKeySequence.Undo,
                                  statusTip="Undo previous action",
                                  triggered=self.textEdit.undo)
        self.fontAction = QAction('F&ont',
                                  self,
                                  statusTip="Modify font properties",
                                  triggered=self.fontChange)
        self.aboutAction = QAction(QIcon('doc_write.png'),
                                   'A&bout',
                                   self,
                                   statusTip="Displays info about text editor",
                                   triggered=self.aboutHelp)
        self.aboutQtAction = QAction(
            "About &Qt",
            self,
            statusTip="Show the Qt library's About box",
            triggered=qApp.aboutQt)

    def CreateMenus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.editMenu = self.menuBar().addMenu("&Edit")
        self.formatMenu = self.menuBar().addMenu("F&ormat")
        self.helpMenu = self.menuBar().addMenu("&Help")

    def CreateToolBar(self):
        self.mainToolBar = self.addToolBar('Main')
Пример #17
0
class MainWindow(QMainWindow):
    """Main Window of the application"""
    def __init__(self):
        super().__init__()
        self.schedule_object = None
        self.init_ui()
        self.setStyleSheet(WINDOW_STYLE)
        self.setWindowTitle("NRC a iCalendar")
        self.setWindowIcon(QIcon(get_path("assets", "icon.svg")))
        self.clikboard = QClipboard()

        # Dialogo para guardar el archivo
        self.save_dialog = QFileDialog(self)
        self.save_dialog.setFileMode(QFileDialog.AnyFile)
        self.save_dialog.setNameFilter("iCalendar (*.ics)")
        self.save_dialog.setDefaultSuffix("ics")
        self.save_dialog.setAcceptMode(QFileDialog.AcceptSave)

    def init_ui(self):
        """Makes the layout"""

        # Barra de opciones y links de interés
        menu_bar = self.menuBar()

        options_menu = menu_bar.addMenu("&Opciones")
        act_allways_visible = options_menu.addAction("Siempre visible")
        act_allways_visible.setCheckable(True)
        act_allways_visible.toggled.connect(self.__allways_visible)

        uc_calendars_menu = menu_bar.addMenu("&Calendarios")
        for name, link in OTHER_CALENDARS:
            calendar_option = uc_calendars_menu.addAction(name)
            # TODO: Ni idea pq se necesita tener una variable `s`, sin esta no funciona
            calendar_option.triggered.connect(
                lambda s=None, l=link: self.__to_clipboard(l))

        go_to_menu = menu_bar.addMenu("&Ir a")
        go_to_options = [
            ("Feed del calendario de Canvas",
             "https://cursos.canvas.uc.cl/calendar"),
            (
                "Importar calendario a Google",
                "https://calendar.google.com/calendar/a/uc.cl/r/settings/export",
            ),
        ]

        for name, link in go_to_options:
            new_option = go_to_menu.addAction(name)
            new_option.triggered.connect(
                lambda s=None, l=link: webbrowser.open(l))

        # Main widget

        main_widget = QFrame()
        self.setCentralWidget(main_widget)

        main_layout = QVBoxLayout(main_widget)
        main_widget.setLayout(main_layout)

        main_layout.setSizeConstraint(QLayout.SetMinimumSize)

        # Lista de códigos a ingresar
        code_layout = QHBoxLayout()
        main_layout.addLayout(code_layout)
        self.code_list = [QLineEdit(main_widget) for i in range(6)]
        code_validator = QIntValidator(main_layout, 10**4, 10**5)
        for code in self.code_list:
            code.setObjectName("code_field")
            code.setAlignment(Qt.AlignCenter)
            code.setMaxLength(5)
            code.setValidator(code_validator)
            code.textEdited.connect(self.check_codes)
            code_layout.addWidget(code)

        self.get_button = QPushButton("Obtener horario", main_widget)
        self.get_button.clicked.connect(self.get_schedule)
        self.get_button.setCursor(Qt.PointingHandCursor)
        self.get_button.setDisabled(True)
        main_layout.addWidget(self.get_button)

        self.schedule_view = ScheduleView(8, 6, main_widget)
        main_layout.addWidget(self.schedule_view)

        self.save_button = QPushButton("Guardar horario", main_widget)
        self.save_button.clicked.connect(self.save_schedule)
        self.save_button.setCursor(Qt.PointingHandCursor)
        self.save_button.setDisabled(True)
        main_layout.addWidget(self.save_button)

        self.status_bar = QStatusBar(self)
        self.status_bar.showMessage("Ingrese los códigos NRC")
        self.setStatusBar(self.status_bar)

        self.adjustSize()

    def __allways_visible(self, option):
        flags = self.windowFlags()
        if option:
            self.setWindowFlags(flags | Qt.WindowStaysOnTopHint)
        else:
            self.setWindowFlags(flags ^ Qt.WindowStaysOnTopHint)
        self.show()

    def __to_clipboard(self, link):
        self.clikboard.setText(link)
        self.status_bar.showMessage(
            "URL del calendario copiado a portapapeles")

    def check_codes(self):
        """Check if the codes are valid"""
        at_least_one_valid = False
        for code in self.code_list:
            if valid_nrc(code.text()):
                at_least_one_valid = True
            elif code.text():
                # TODO: cambiar el estilo al ser invalido y tener texto
                pass
            else:
                pass

        self.get_button.setDisabled(not at_least_one_valid)

        if at_least_one_valid:
            self.status_bar.clearMessage()
        else:
            self.status_bar.showMessage("Ingrese los códigos NRC")

    def get_schedule(self):
        """Get the schedule of Buscacursos UC"""
        valid_codes = list(
            filter(valid_nrc, map(QLineEdit.text, self.code_list)))

        if not valid_codes:
            return

        try:
            self.schedule_object = Schedule.get(valid_codes)
        except OSError:
            error_box = QMessageBox(QMessageBox.Critical, "Error",
                                    "No se ha podido importar el horario")
            error_box.exec_()
        else:
            self.show_schedule()

    def show_schedule(self):
        """Show the schedule in the table"""
        # Limpia el horario
        self.schedule_view.clearContents()

        # Si no hay módulos, se deshabilita la opción de guardar y termina
        if not self.schedule_object.courses:
            self.schedule_view.update_size()
            self.save_button.setDisabled(True)
            return

        # Si existen módulos, se muestran en el horario
        for i_row, row in enumerate(self.schedule_object.get_table()):
            for sub_row in row:
                for i_col, element in enumerate(sub_row):

                    if not element:
                        continue

                    module_label = QLabel(self.schedule_view)
                    module_label.setText(element.code)
                    module_label.setObjectName(element.type_)
                    module_label.setAlignment(Qt.AlignCenter)
                    module_label.setFixedHeight(20)

                    cell_frame = self.schedule_view.cellWidget(i_row, i_col)

                    # Se crea un frame para los widgets si no existe
                    if not cell_frame:
                        cell_frame = QFrame(self.schedule_view)
                        cell_layout = QVBoxLayout(cell_frame)
                        cell_layout.setSpacing(0)
                        cell_layout.setMargin(0)
                        self.schedule_view.setCellWidget(
                            i_row, i_col, cell_frame)

                    cell_frame.layout().addWidget(module_label)

        self.schedule_view.update_size()
        self.save_button.setDisabled(False)

    def save_schedule(self):
        """Saves the schedule"""
        if self.save_dialog.exec_():
            out_dir = self.save_dialog.selectedFiles()[0]
            with open(out_dir, mode="w", encoding="utf-8") as file:
                file.write(self.schedule_object.to_ics())
class NewProjectForm(QWidget):
    """Class for a new project widget.

    Attributes:
        toolbox (ToolboxUI): Parent widget.
        configs (ConfigurationParser): Configurations object
    """
    def __init__(self, toolbox, configs):
        """Initialize class."""
        super().__init__(parent=toolbox,
                         f=Qt.Window)  # Inherits stylesheet from parent
        self._toolbox = toolbox
        self._configs = configs
        # Set up the user interface from Designer.
        self.ui = ui.project_form.Ui_Form()
        self.ui.setupUi(self)
        # Add status bar to form
        self.statusbar = QStatusBar(self)
        self.statusbar.setFixedHeight(20)
        self.statusbar.setSizeGripEnabled(False)
        self.statusbar.setStyleSheet(STATUSBAR_SS)
        self.ui.horizontalLayout_statusbar_placeholder.addWidget(
            self.statusbar)
        # Class attributes
        self.name = ''  # Project name
        self.description = ''  # Project description
        self.connect_signals()
        self.ui.pushButton_ok.setDefault(True)
        self.ui.lineEdit_project_name.setFocus()
        # Ensure this window gets garbage-collected when closed
        self.setAttribute(Qt.WA_DeleteOnClose)

    def connect_signals(self):
        """Connect signals to slots."""
        self.ui.lineEdit_project_name.textChanged.connect(self.name_changed)
        self.ui.pushButton_ok.clicked.connect(self.ok_clicked)
        self.ui.pushButton_cancel.clicked.connect(self.close)

    @Slot(name='name_changed')
    def name_changed(self):
        """Update label to show a preview of the project directory name."""
        project_name = self.ui.lineEdit_project_name.text()
        default = "Project folder:"
        if project_name == '':
            self.ui.label_folder.setText(default)
        else:
            folder_name = project_name.lower().replace(' ', '_')
            msg = default + " " + folder_name
            self.ui.label_folder.setText(msg)

    @Slot(name='ok_clicked')
    def ok_clicked(self):
        """Check that project name is valid and create project."""
        self.name = self.ui.lineEdit_project_name.text()
        self.description = self.ui.textEdit_description.toPlainText()
        if self.name == '':
            self.statusbar.showMessage("No project name given", 5000)
            return
        # Check for invalid characters for a folder name
        invalid_chars = ["<", ">", ":", "\"", "/", "\\", "|", "?", "*", "."]
        # "." is actually valid in a folder name but
        # this is to prevent creating folders like "...."
        if any((True for x in self.name if x in invalid_chars)):
            self.statusbar.showMessage(
                "Project name contains invalid character(s) for a folder name",
                5000)
            return
        # Check if project with same name already exists
        short_name = self.name.lower().replace(' ', '_')
        project_folder = os.path.join(project_dir(self._configs), short_name)
        if os.path.isdir(project_folder):
            self.statusbar.showMessage("Project already exists", 5000)
            return
        # Create new project
        self.call_create_project()
        self.close()

    def call_create_project(self):
        """Call ToolboxUI method create_project()."""
        self._toolbox.create_project(self.name, self.description)

    def keyPressEvent(self, e):
        """Close project form when escape key is pressed.

        Args:
            e (QKeyEvent): Received key press event.
        """
        if e.key() == Qt.Key_Escape:
            self.close()
        elif e.key() == Qt.Key_Enter or e.key() == Qt.Key_Return:
            self.ok_clicked()

    def closeEvent(self, event=None):
        """Handle close window.

        Args:
            event (QEvent): Closing event if 'X' is clicked.
        """
        if event:
            event.accept()
Пример #19
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # SET => App Icon
        self.icon = QIcon("img/iconXLNK.png")
        # End

        self.tree_view = None
        self.file_system_model = None

        # SET => Window Icon
        self.setWindowIcon(self.icon)
        # End

        # WSET => Window Title
        self.setWindowTitle("XLNK | Data Manager")
        # End

        # Menus
        self.menu = self.menuBar()
        self.file_menu = self.menu.addMenu("&File")
        self.edit_menu = self.menu.addMenu("&Edit")
        self.view_menu = self.menu.addMenu("&View")
        self.help_menu = self.menu.addMenu("&Help")
        # End

        # ===================
        # Menu Button Actions

        # Exit QAction
        exit_action = QAction("Exit", self)
        exit_action.setShortcut(QKeySequence.Quit)
        exit_action.triggered.connect(self.close)
        self.file_menu.addAction(exit_action)
        # End
        # End

        # Tool Bar
        toolbar = QToolBar(self)
        self.addToolBar(toolbar)

        # delete action on toolbar
        delete_action_tb = QAction("DELETE TABLE ROW", self)
        delete_action_tb.setStatusTip("Obrisi Red U Tabeli")
        delete_action_tb.triggered.connect(self.delete_table_row_tb)
        toolbar.addAction(delete_action_tb)

        # Dock Widget
        dock_widget = QDockWidget("EXPLORER", self)
        # File System Model
        self.file_system_model = QFileSystemModel()
        self.file_system_model.setRootPath(QDir.currentPath())
        # SET => Tree View MOdel
        self.tree_view = QTreeView()
        self.tree_view.setModel(self.file_system_model)
        self.tree_view.setRootIndex(
            self.file_system_model.index(QDir.currentPath() + "/data"))
        self.tree_view.clicked.connect(self.file_clicked_handler)
        dock_widget.setWidget(self.tree_view)
        dock_widget.setFloating(False)
        self.addDockWidget(Qt.LeftDockWidgetArea, dock_widget)

        # QLabel
        qlabel = QLabel(self)
        qlabel.setText("Welcome to XLNK.")

        # Central Widget
        self.clicked_file = None

        self.setCentralWidget(qlabel)

        self.showMaximized()

    # ToolBar Functions
    # TODO
    def delete_table_row_tb(self):
        print("Ugraditi funkciju za brisanje reda iz tabele.")

    def file_clicked_handler(self, index):
        index = self.tree_view.currentIndex()
        file_clicked_param = os.path.basename(
            self.file_system_model.filePath(index))
        self.clicked_file = file_clicked_param
        self.status_bar = QStatusBar(self)
        self.status_bar.showMessage(
            "File Selected: {}".format(file_clicked_param), 3000)
        self.setStatusBar(self.status_bar)
        self.workspace_widget = WorkSpaceWidget(self, self.clicked_file)
        self.setCentralWidget(self.workspace_widget)
Пример #20
0
class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.available_cameras = QCameraInfo.availableCameras()
        if not self.available_cameras:
            pass #quit

        self.status = QStatusBar()
        self.setStatusBar(self.status)


        self.save_path = ""

        self.viewfinder = QCameraViewfinder()
        self.viewfinder.show()
        self.setCentralWidget(self.viewfinder)

        # Set the default camera.
        self.select_camera(0)

        # Setup tools
        camera_toolbar = QToolBar("Camera")
        camera_toolbar.setIconSize(QSize(14, 14))
        self.addToolBar(camera_toolbar)

        photo_action = QAction(QIcon(os.path.join('images', 'camera-black.png')), "Take photo...", self)
        photo_action.setStatusTip("Take photo of current view")
        photo_action.triggered.connect(self.take_photo)
        camera_toolbar.addAction(photo_action)

        change_folder_action = QAction(QIcon(os.path.join('images', 'blue-folder-horizontal-open.png')), "Change save location...", self)
        change_folder_action.setStatusTip("Change folder where photos are saved.")
        change_folder_action.triggered.connect(self.change_folder)
        camera_toolbar.addAction(change_folder_action)


        camera_selector = QComboBox()
        camera_selector.addItems([c.description() for c in self.available_cameras])
        camera_selector.currentIndexChanged.connect( self.select_camera )

        camera_toolbar.addWidget(camera_selector)


        self.setWindowTitle("NSAViewer")
        self.show()

    def select_camera(self, i):
        self.camera = QCamera(self.available_cameras[i])
        self.camera.setViewfinder(self.viewfinder)
        self.camera.setCaptureMode(QCamera.CaptureStillImage)
        self.camera.error.connect(lambda: self.alert(self.camera.errorString()))
        self.camera.start()

        self.capture = QCameraImageCapture(self.camera)
        self.capture.error.connect(lambda i, e, s: self.alert(s))
        self.capture.imageCaptured.connect(lambda d, i: self.status.showMessage("Image %04d captured" % self.save_seq))

        self.current_camera_name = self.available_cameras[i].description()
        self.save_seq = 0

    def take_photo(self):
        timestamp = time.strftime("%d-%b-%Y-%H_%M_%S")
        self.capture.capture(os.path.join(self.save_path, "%s-%04d-%s.jpg" % (
            self.current_camera_name,
            self.save_seq,
            timestamp
        )))
        self.save_seq += 1

    def change_folder(self):
        path = QFileDialog.getExistingDirectory(self, "Snapshot save location", "")
        if path:
            self.save_path = path
            self.save_seq = 0

    def alert(self, s):
        """
        Handle errors coming from QCamera dn QCameraImageCapture by displaying alerts.
        """
        err = QErrorMessage(self)
        err.showMessage(s)
Пример #21
0
class MainWindow(QMainWindow):
    changePage = Signal(bool)

    def __init__(self):
        super().__init__()
        self.setWindowTitle('Petro-Explorer v1.0')
        win_icon = QIcon('icons/Logo.ico')
        self.setWindowIcon(win_icon)
        self.setStyleSheet('background-color: #363f49;')

        self.header = Header()
        self.navigation = Navigation()
        self.p_top_bar = PetrophysicsTopBar()
        self.s_top_bar = StatisticsTopBar()
        self.p_options_1 = PetrophysicsOptions(_mode='KCarman')
        self.p_options_2 = PetrophysicsOptions(_mode='TCoates')
        self.p_options_3 = PetrophysicsOptions(_mode='Winland')
        self.p_options_4 = PetrophysicsOptions(_mode='RQIFZI')
        self.p_options_5 = PetrophysicsOptions(_mode='Lucia')
        self.p_options_6 = PetrophysicsOptions(_mode='DParsons')
        self.s_options_1 = StatisticsOptions(_mode='Regression')
        self.s_options_2 = StatisticsOptions(_mode='Statistics')
        self.s_options_3 = StatisticsOptions(_mode='Histogram')
        self.s_options_4 = StatisticsOptions(_mode='Boxplot')
        self.sp_controls = SpreadsheetControls()
        self.plot_controls = PlotControls()
        self.plot_viewer = PlotViewer()
        self.spreadsheet = Spreadsheet()
        self.status_bar = QStatusBar()

        self.scroll_area_1 = QScrollArea()
        self.scroll_area_1.setWidget(self.spreadsheet)
        self.scroll_area_1.setWidgetResizable(True)

        self.scroll_area_2 = QScrollArea()
        self.scroll_area_2.setWidget(self.plot_viewer)
        self.scroll_area_2.setWidgetResizable(True)

        self.bar_widget = QWidget()
        self.bar_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)

        self.central_widget = QWidget()
        self.options_widget = QWidget()

        self.docking_options = QDockWidget()
        self.docking_options.setWidget(self.options_widget)
        self.docking_options.setTitleBarWidget(
            DockWidgetRibbon(' Opções de cálculo'))

        self.docking_options2 = QDockWidget()
        self.docking_options2.setWidget(self.sp_controls)
        self.docking_options2.setTitleBarWidget(
            DockWidgetRibbon(' Controles de visualização'))

        self.setCentralWidget(self.central_widget)
        self.setStatusBar(self.status_bar)
        self.addToolBar(self.navigation)
        self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea,
                           self.docking_options)
        self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea,
                           self.docking_options2)
        self.connections()
        self.buildLayout()

        self.centralWidget().setStyleSheet('background-color: #2e3843')
        self.spreadsheet.setStyleSheet('background-color: white')
        self.status_bar.setStyleSheet('color: white')

    def buildLayout(self):
        stacked_layout_1 = QStackedLayout()
        # stacked_layout_1.addWidget(QWidget())
        stacked_layout_1.addWidget(self.p_top_bar)
        stacked_layout_1.addWidget(self.s_top_bar)

        stacked_layout_2 = QStackedLayout()
        # stacked_layout_2.addWidget(QWidget())
        stacked_layout_2.addWidget(self.p_options_1)
        stacked_layout_2.addWidget(self.p_options_2)
        stacked_layout_2.addWidget(self.p_options_3)
        stacked_layout_2.addWidget(self.p_options_4)
        stacked_layout_2.addWidget(self.p_options_5)
        stacked_layout_2.addWidget(self.p_options_6)
        stacked_layout_2.addWidget(self.s_options_1)
        stacked_layout_2.addWidget(self.s_options_2)
        stacked_layout_2.addWidget(self.s_options_3)
        stacked_layout_2.addWidget(self.s_options_4)

        self.stacked_layout_3 = QStackedLayout()
        self.stacked_layout_3.addWidget(self.scroll_area_1)
        self.stacked_layout_3.addWidget(self.scroll_area_2)

        central_widget_layout = QVBoxLayout()
        central_widget_layout.addWidget(self.bar_widget)
        central_widget_layout.addLayout(self.stacked_layout_3)

        self.central_widget.setLayout(central_widget_layout)
        self.bar_widget.setLayout(stacked_layout_1)
        self.options_widget.setLayout(stacked_layout_2)

    def connections(self):
        self.navigation.PetroAnalysis.connect(
            lambda: self.bar_widget.layout().setCurrentIndex(0))
        self.navigation.StatsAnalysis.connect(
            lambda: self.bar_widget.layout().setCurrentIndex(1))
        self.navigation.Save.connect(lambda: self.saveDialog())
        self.navigation.Import.connect(lambda: self.importDialog())
        self.navigation.About.connect(lambda: self.aboutDialog())
        self.navigation.Help.connect(lambda: self.helpDialog())
        self.navigation.header.HomePage.connect(
            lambda: self.changePage.emit(True))
        self.navigation.ViewSheet.connect(lambda: self.displaySheet())
        self.navigation.ViewPlot.connect(lambda: self.displayPltE())
        self.navigation.New.connect(lambda: self.spreadsheet.addBlankSheet())

        self.p_top_bar.KCarman.connect(
            lambda: self.options_widget.layout().setCurrentIndex(0))
        self.p_top_bar.TCoates.connect(
            lambda: self.options_widget.layout().setCurrentIndex(1))
        self.p_top_bar.Winland.connect(
            lambda: self.options_widget.layout().setCurrentIndex(2))
        self.p_top_bar.DParsons.connect(
            lambda: self.options_widget.layout().setCurrentIndex(5))
        self.p_top_bar.Lucia.connect(
            lambda: self.options_widget.layout().setCurrentIndex(4))
        self.p_top_bar.RF.connect(
            lambda: self.options_widget.layout().setCurrentIndex(3))

        self.s_top_bar.Regr.connect(
            lambda: self.options_widget.layout().setCurrentIndex(6))
        self.s_top_bar.StatDesc.connect(
            lambda: self.options_widget.layout().setCurrentIndex(7))
        self.s_top_bar.Boxplt.connect(
            lambda: self.options_widget.layout().setCurrentIndex(9))
        self.s_top_bar.Hist.connect(
            lambda: self.options_widget.layout().setCurrentIndex(8))

        self.s_options_1.run_button.clicked.connect(self.startRegr)
        self.s_options_2.run_button.clicked.connect(self.startStat)
        self.s_options_3.run_button.clicked.connect(self.startHist)
        self.s_options_4.run_button.clicked.connect(self.startBxpl)

        self.p_options_1.run.clicked.connect(self.startKCoz)
        self.p_options_2.run.clicked.connect(self.startTCoa)
        self.p_options_3.run.clicked.connect(self.startWinl)
        self.p_options_4.run.clicked.connect(self.startFZIR)
        self.p_options_5.run.clicked.connect(self.startLuci)
        self.p_options_6.run.clicked.connect(self.startDyks)

        self.sp_controls.AddColumn.connect(
            lambda: self.spreadsheet.addColumn())
        self.sp_controls.AddRow.connect(lambda: self.spreadsheet.addRow())
        self.sp_controls.DeleteRow.connect(
            lambda: self.spreadsheet.deleteRow())
        self.sp_controls.DeleteColumn.connect(
            lambda: self.spreadsheet.deleteColumn())

        self.plot_controls.run_button.clicked.connect(lambda: self.startPltE())
        self.plot_controls.erasePlot.connect(
            lambda: self.plot_viewer.erasePlot())

        self.plot_viewer.feedPlotControls.connect(
            self.plot_controls.fillFromJson)

    def startPltE(self):
        old_json = self.plot_viewer.json_data
        title = self.plot_controls.modify_title.text()
        xtype = self.plot_controls.modify_x_axis_type.currentText()
        ytype = self.plot_controls.modify_y_axis_type.currentText()
        xlabel = self.plot_controls.modify_x_axis_label.text()
        ylabel = self.plot_controls.modify_y_axis_label.text()

        if len(self.plot_controls.modify_lower_x_range.text()) > 0:
            lxrange = float(self.plot_controls.modify_lower_x_range.text())
        else:
            lxrange = None
        if len(self.plot_controls.modify_upper_x_range.text()) > 0:
            uxrange = float(self.plot_controls.modify_upper_x_range.text())
        else:
            uxrange = None
        if len(self.plot_controls.modify_lower_y_range.text()) > 0:
            lyrange = float(self.plot_controls.modify_lower_y_range.text())
        else:
            lyrange = None
        if len(self.plot_controls.modify_upper_y_range.text()) > 0:
            uyrange = float(self.plot_controls.modify_upper_y_range.text())
        else:
            uyrange = None

        if len(self.plot_controls.add_x_trace.text()) > 0 and len(
                self.plot_controls.add_y_trace.text()):
            trace_type = self.plot_controls.type_of_trace.currentText()
            x_trace = self.spreadsheet.model.input_data[:,
                                                        self.spreadsheet.model.
                                                        header_info.index(
                                                            self.plot_controls.
                                                            add_x_trace.text(
                                                            ))]
            y_trace = self.spreadsheet.model.input_data[:,
                                                        self.spreadsheet.model.
                                                        header_info.index(
                                                            self.plot_controls.
                                                            add_y_trace.text(
                                                            ))]
            trace_name = self.plot_controls.trace_name.text()
        else:
            trace_type = None
            x_trace = None
            y_trace = None
            trace_name = None

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.loadPltE(io_operations_handler.results))
        io_operations_handler.loadParameters(f=pceManifold,
                                             _args=[
                                                 old_json, title, xtype, ytype,
                                                 xlabel, ylabel, lxrange,
                                                 uxrange, lyrange, uyrange,
                                                 trace_type, x_trace, y_trace,
                                                 trace_name
                                             ])
        io_operations_handler.start()

    def loadPltE(self, results, typ='Regressão'):
        self.plot_viewer.loadPlot(results[0], results[1], _type=typ)
        self.displayPltE()

    def displayPltE(self):
        self.stacked_layout_3.setCurrentIndex(1)
        self.docking_options2.setWidget(self.plot_controls)
        #self.removeDockWidget(self.docking_options3)
        #self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.docking_options2)

    def loadSheet(self, df):
        self.spreadsheet.changeModel(data=df, header=list(df.keys()))
        self.displaySheet()

    def displaySheet(self):
        self.stacked_layout_3.setCurrentIndex(0)
        self.docking_options2.setWidget(self.sp_controls)
        self.status_bar.clearMessage()
        #self.removeDockWidget(self.docking_options2)
        #self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.docking_options3)

    def importDialog(self):
        func = None
        file_settings = QFileDialog().getOpenFileName(
            self,
            'Importar Arquivo',
            filter=
            "Todos os arquivos (*);; Arquivo de Texto (*.txt);; Arquivo CSV (*.csv);; "
            "Planilha Excel (*.xlsx)")
        if ".txt" in file_settings[0]:
            func = Data.readTXT
        if ".csv" in file_settings[0]:
            func = Data.readCSV
        if ".xlsx" in file_settings[0]:
            func = Data.readExcel

        self.status_bar.showMessage('Importando arquivo. Aguarde.')
        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.loadSheet(io_operations_handler.results))
        io_operations_handler.loadParameters(f=func,
                                             _args=[
                                                 file_settings[0],
                                             ])
        io_operations_handler.start()

    def saveDialog(self):
        func = None
        file_settings = QFileDialog().getSaveFileName(
            self,
            "Salvar Arquivo",
            filter="Arquivo de Texto (*.txt);; Arquivo CSV (*.csv);; "
            "Planilha Excel (*.xlsx)")
        if ".txt" in file_settings[0]:
            func = Data.toTXT
        if ".csv" in file_settings[0]:
            func = Data.toCSV
        if ".xlsx" in file_settings[0]:
            func = Data.toExcel

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.loadParameters(
            f=func, _args=[self.spreadsheet.retrieveModel(), file_settings[0]])
        io_operations_handler.start()

    def aboutDialog(self):
        icon = QIcon(r'icons/sobre.png')
        text = "O Petro-Explorer é um software desenvolvido no Laboratório de Exploração e Produção de Petróleo (" \
               "LENEP) da UENF para análises petrofísicas e estatísticas de dados de rocha. "
        msg = QMessageBox()
        msg.setWindowTitle('Sobre Petro-Explorer')
        msg.setWindowIcon(icon)
        msg.setText(text)
        msg.exec_()

    def helpDialog(self):
        icon = QIcon(r'icons/ajuda.png')
        text = r"Para utilizar o Petro-Explorer de maneira correta siga os seguintes passos:" \
               "\n1 - Clique no botão Novo na barra de navegação, isto irá limpar quaisquer dados de antigas análises.\n" \
               "2 - Para começar a sua análise é preciso importar os dados, assim, clique no botão Importar para escolher o seu arquivo. Atente para o fato que somente três tipos de arquivo são suportados (i.e. *.txt, *.csv, *.xlsx). Depois da sua escolha, os dados devem aparecer na planilha do software.\n" \
               "3 - Se você desejar realizar uma análise petrofísica, clique no botão de Análise Petrofísica na barra de navegação. Caso queira realizar uma análise estatística, clique no botão de Análise Estatística na barra de navegação.\n" \
               "4 - Selecione na barra superior à planilha, a análise que desejas realizar sobre seus dados.\n" \
               "5 - No canto direito da janela, selecione os parâmetros de sua análise assim como o destino de seus resultados. Clique no botão 'Começar Análise' para continuar.\n" \
               "6 - Analise seus resultados na planilha, e, quando disponível, no gráfico também.\n" \
               "7 - Quando terminar, clique no botão Salvar para salvar os resultados de suas análises no disco.\n" \
               "8 - Caso queira realizar outra análise, clique no botão \"Novo\" e comece novamente.\n"
        msg = QMessageBox()
        msg.setWindowTitle('Ajuda')
        msg.setWindowIcon(icon)
        msg.setText(text)
        msg.exec_()

    def startRegr(self):
        xdata = self.spreadsheet.model.input_data[:,
                                                  self.spreadsheet.model.
                                                  header_info.
                                                  index(self.s_options_1.
                                                        payload['x_column'])]
        ydata = self.spreadsheet.model.input_data[:,
                                                  self.spreadsheet.model.
                                                  header_info.
                                                  index(self.s_options_1.
                                                        payload['y_column'])]
        _type = self.s_options_1.payload['calculate']
        dgr = self.s_options_1.degree.value()

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayRegr(self.s_options_1.payload['output_column'],
                                     io_operations_handler.results))
        io_operations_handler.loadParameters(f=regressionManifold,
                                             _args=[xdata, ydata, dgr, _type])
        io_operations_handler.start()

    def startStat(self):
        xdata = self.spreadsheet.model.input_data[:,
                                                  self.spreadsheet.model.
                                                  header_info.
                                                  index(self.s_options_2.
                                                        payload['x_column'])]
        mthd = self.s_options_2.payload['calculate']
        qtl = self.s_options_2.quartile.value()
        pctl = self.s_options_2.percentile.value()

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayStat(self.s_options_2.payload['output_column'],
                                     io_operations_handler.results))
        io_operations_handler.loadParameters(f=statisticsManifold,
                                             _args=[xdata, mthd, qtl, pctl])
        io_operations_handler.start()

    def startHist(self):
        xdata = self.spreadsheet.model.input_data[:,
                                                  self.spreadsheet.model.
                                                  header_info.
                                                  index(self.s_options_3.
                                                        payload['x_column'])]
        nbins = self.s_options_3.payload['y_column']

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayHist(io_operations_handler.results))
        io_operations_handler.loadParameters(f=histogramManifold,
                                             _args=[xdata, nbins])
        io_operations_handler.start()

    def startBxpl(self):
        indexes = []
        columns = self.s_options_4.payload['column_range']
        for col in columns:
            indexes.append(self.spreadsheet.model.header_info.index(col))
        xdata = self.spreadsheet.model.input_data[:, indexes]
        box_orientation = self.s_options_4.payload['y_column']

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayBxpl(io_operations_handler.results))
        io_operations_handler.loadParameters(f=boxplotManifold,
                                             _args=[xdata, box_orientation])
        io_operations_handler.start()

    def startKCoz(self):
        #you need to ascertain if x corresponds to Permeability, y to Porosity and z to Swir/Svgr

        k = None
        phi = None
        svgr = None

        prp = self.p_options_1.payload['calculate']

        if prp == 'Permeabilidade (mD)':
            phi = self.spreadsheet.model.input_data[:,
                                                    self.spreadsheet.model.
                                                    header_info.
                                                    index(self.p_options_1.
                                                          payload['x_column'])]
            svgr = self.spreadsheet.model.input_data[:,
                                                     self.spreadsheet.model.
                                                     header_info.index(
                                                         self.p_options_1.
                                                         payload['y_column'])]
        elif prp == 'SVgr (cm-1)':
            k = self.spreadsheet.model.input_data[:,
                                                  self.spreadsheet.model.
                                                  header_info.
                                                  index(self.p_options_1.
                                                        payload['x_column'])]
            phi = self.spreadsheet.model.input_data[:,
                                                    self.spreadsheet.model.
                                                    header_info.
                                                    index(self.p_options_1.
                                                          payload['y_column'])]
        else:
            k = self.spreadsheet.model.input_data[:,
                                                  self.spreadsheet.model.
                                                  header_info.
                                                  index(self.p_options_1.
                                                        payload['x_column'])]
            phi = self.spreadsheet.model.input_data[:,
                                                    self.spreadsheet.model.
                                                    header_info.
                                                    index(self.p_options_1.
                                                          payload['y_column'])]

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayKCoz(io_operations_handler.results))
        io_operations_handler.loadParameters(f=kCarmanManifold,
                                             _args=[k, phi, svgr, prp])
        io_operations_handler.start()

    def startTCoa(self):
        #you need to ascertain if x corresponds to Permeability, y to Porosity and z to Swir/Svgr
        prp = self.p_options_2.payload['calculate']

        if prp == "Swir (%)":
            xdata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_2.
                                                          payload['x_column'])]
            ydata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_2.
                                                          payload['y_column'])]
            zdata = None
        else:
            xdata = None
            ydata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_2.
                                                          payload['y_column'])]
            zdata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_2.
                                                          payload['x_column'])]

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayTCoa(io_operations_handler.results))
        io_operations_handler.loadParameters(f=tCoatesManifold,
                                             _args=[xdata, ydata, zdata, prp])
        io_operations_handler.start()

    def startWinl(self):
        #you need to ascertain if x corresponds to Permeability, y to Porosity and z to Swir/Svgr
        if self.p_options_3.payload['x_column'] != '':
            xdata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_3.
                                                          payload['x_column'])]
        else:
            xdata = []
        if self.p_options_3.payload['y_column'] != '':
            ydata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_3.
                                                          payload['y_column'])]
        else:
            ydata = []

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayWinl(io_operations_handler.results))
        io_operations_handler.loadParameters(f=winlandManifold,
                                             _args=[xdata, ydata])
        io_operations_handler.start()

    def startFZIR(self):
        # you need to ascertain if x corresponds to Permeability, y to Porosity and z to Swir/Svgr
        if self.p_options_4.payload['x_column'] != '':
            xdata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_4.
                                                          payload['x_column'])]
        else:
            xdata = None
        if self.p_options_4.payload['y_column'] != '':
            ydata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_4.
                                                          payload['y_column'])]
        else:
            ydata = None
        if self.p_options_4.payload['z_column'] != '':
            zdata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_4.
                                                          payload['z_column'])]
        else:
            zdata = None

        prp = self.p_options_4.payload['calculate']
        un = self.p_options_4.payload['column_range']

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayFZIR(io_operations_handler.results))
        io_operations_handler.loadParameters(
            f=fzManifold, _args=[xdata, ydata, zdata, un, prp])
        io_operations_handler.start()

    def startLuci(self):
        #you need to ascertain if x corresponds to Permeability, y to Porosity and z to Swir/Svgr
        if self.p_options_5.payload['x_column'] != '':
            xdata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_5.
                                                          payload['x_column'])]
        else:
            xdata = []
        if self.p_options_5.payload['y_column'] != '':
            ydata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_5.
                                                          payload['y_column'])]
        else:
            ydata = []

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayLuci(io_operations_handler.results))
        io_operations_handler.loadParameters(f=luciaManifold,
                                             _args=[xdata, ydata])
        io_operations_handler.start()

    def startDyks(self):
        #you need to ascertain if x corresponds to Permeability, y to Porosity and z to Swir/Svgr
        if self.p_options_6.payload['x_column'] != '':
            xdata = self.spreadsheet.model.input_data[:,
                                                      self.spreadsheet.model.
                                                      header_info.index(
                                                          self.p_options_6.
                                                          payload['x_column'])]
        else:
            xdata = []
        prp = self.p_options_6.payload['calculate']

        io_operations_handler = HandlerThread()
        io_operations_handler.messageSent.connect(self.status_bar.showMessage)
        io_operations_handler.daemon = True
        io_operations_handler.hasFinished.connect(
            lambda: self.displayDyks(io_operations_handler.results))
        io_operations_handler.loadParameters(f=dParsonsManifold,
                                             _args=[xdata, prp])
        io_operations_handler.start()

    def displayKCoz(self, content):
        content = list(content)
        content.insert(0, 'Resultados - Kozeny-Carman')
        self.spreadsheet.addColumn(content)

    def displayWinl(self, content):
        self.loadPltE(content[:2])
        content[2].insert(0, 'R35 - Winland')
        content[3].insert(0, 'Ports - Winland')
        self.spreadsheet.addColumn(content[2])
        self.spreadsheet.addColumn(content[3])

    def displayTCoa(self, content):
        content.insert(0, 'Resultados - Timur Coates')
        self.spreadsheet.addColumn(content)

    def displayDyks(self, content):
        content.insert(0, 'Resultados - Dykstra-Parsons')
        self.spreadsheet.addColumn(content)
        self.p_options_6.results.setText(
            'Atenção! Resultado pode ser diferente do coeficiente calculado através do gráfico de probabilidade. \n'
            + str(content[0]) + ': ' + str(content[1]))

    def displayLuci(self, results):
        self.loadPltE(results[:2])
        results[2].insert(0, 'Resultados - RFN/Lucia')
        self.spreadsheet.addColumn(results[2])

    def displayFZIR(self, results):
        results[0].insert(0, 'Resultados - RQI (μm)')
        self.spreadsheet.addColumn(results[0])
        results[1].insert(0, 'Resultados - PhiZ')
        self.spreadsheet.addColumn(results[1])
        self.loadPltE(results[2:])

    def displayRegr(self, display_name, results):
        self.s_options_1.results.setText(str(results[0]))
        self.loadPltE(results[-2:])

    def displayStat(self, display_name, results):
        self.s_options_2.results.setText(str(results))

    def displayHist(self, results):
        self.loadPltE(results, 'Histograma')

    def displayBxpl(self, results):
        self.loadPltE(results, 'Boxplot')
Пример #22
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, ui_demo=False):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.grid_layout = QGridLayout()

        self.btnDetectClient.clicked.connect(self.detectClients)
        self.lineEditIpRange.returnPressed.connect(self.detectClients)
        self.btnSelectAllClients.clicked.connect(self.selectAllCLients)

        shortcut = QShortcut(QKeySequence(self.tr("Ctrl+A")), self)
        shortcut.activated.connect(self.selectAllCLients)

        self.btnUnselectClients.clicked.connect(self.unselectAllCLients)
        self.btnSelectExam.clicked.connect(self.selectExamByWizard)
        self.btnSelectExam.setEnabled(True)

        self.btnPrepareExam.clicked.connect(self.prepareExam)
        self.btnPrepareExam.setEnabled(False)

        self.btnGetExams.clicked.connect(self.retrieveExamFilesByWizard)
        self.btnGetExams.setEnabled(True)
        self.btnSaveExamLog.clicked.connect(self.saveExamLog)
        # self.btnSaveExamLog.setEnabled(False)

        self.actionBearbeiten.triggered.connect(self.openConfigDialog)
        self.actionAlle_Benutzer_benachrichtigen.triggered.connect(
            self.sendMessage)
        self.actionAlle_Clients_zur_cksetzen.triggered.connect(
            self.resetClients)
        self.actionAlle_Clients_rebooten.triggered.connect(
            self.rebootAllClients)
        self.actionAlle_Clients_herunterfahren.triggered.connect(
            self.shutdownAllClients)
        self.actionOnlineHelp.triggered.connect(self.openHelpUrl)
        self.actionOfflineHelp.triggered.connect(self.openHelpUrlOffline)
        self.actionSortClientByCandidateName.triggered.connect(
            self.sortButtonsByCandidateName)
        self.actionSortClientByComputerName.triggered.connect(
            self.sortButtonsByComputerName)
        self.actionVersionInfo.triggered.connect(self.showVersionInfo)
        self.actionDisplayIPs.triggered.connect(self.toggleClientIpDisplay)

        self.btnApplyCandidateNames.clicked.connect(self.applyCandidateNames)
        self.btnNameClients.clicked.connect(self.activateNameTab)

        self.btnBlockUsb.clicked.connect(self.blockUsb)
        self.btnBlockWebAccess.clicked.connect(self.blockWebAccess)

        self.appTitle = 'ECMan - Exam Client Manager'
        self.setWindowTitle(self.appTitle)

        self.logger = Logger(self.textEditLog)

        self.lb_directory = None
        self.advancedUi = False
        self.configure()
        self.show()
        if ui_demo is False:
            self.detectClients()

        else:
            self.clientFrame.setLayout(self.grid_layout)
            for i in range(6):
                self.addTestClient(i + 100)

        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)

    def sortButtonsByCandidateName(self):
        '''
        sort LbClient-widgets by candidate name
        :return: nothing
        '''
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        clients = sorted(clients,
                         key=lambda client: client.computer.candidateName)
        self.arrangeClientButtons(clients)

    def sortButtonsByComputerName(self):
        '''
        supposed to sort LbClient-widgets by candidate name
        :return: nothing
        '''
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        clients = sorted(clients,
                         key=lambda client: client.computer.getHostName())
        self.arrangeClientButtons(clients)

    def toggleClientIpDisplay(self):
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        for client in clients:
            client.toggleShowIp()

    def arrangeClientButtons(self, clients):
        try:
            for i in reversed(range(self.grid_layout.count())):
                self.grid_layout.removeItem(self.grid_layout.itemAt(i))
        except:
            pass

        self.clientFrame.setLayout(self.grid_layout)
        for button in clients:
            self.grid_layout.addWidget(button,
                                       self.grid_layout.count() / 4,
                                       self.grid_layout.count() % 4)

        self.clientFrame.setLayout(self.grid_layout)

    def activateNameTab(self):
        if self.textEditCandidates.toPlainText() == "":
            candidates = "\n".join(
                str(x + 1) for x in range(self.grid_layout.count()))
            self.textEditCandidates.setText(candidates)

        self.tabs.setCurrentWidget(self.tab_candidates)

    def openHelpUrl(self):
        webbrowser.open(
            self.config.get(
                "General",
                "wikiurl",
                fallback="https://github.com/greenorca/ECMan/wiki"))

    def openHelpUrlOffline(self):
        webbrowser.open("file://" + os.getcwd().replace("\\", "/") +
                        "/help/Home.html")

    def checkOldLogFiles(self):
        """
        dummy,
        """
        pass

    def blockUsb(self):
        block = self.btnBlockUsb.text() == "USB blockieren"
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
        ]

        if block:
            for client in clients:
                client.blockUsbAccessThread()
            self.btnBlockUsb.setText("USB freigeben")
        else:
            for client in clients:
                client.allowUsbAccessThread()
            self.btnBlockUsb.setText("USB blockieren")

    def blockWebAccess(self):
        block = self.btnBlockWebAccess.text() == "Web blockieren"
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
        ]
        if block:
            for client in clients:
                client.blockInternetAccessThread()

            self.btnBlockWebAccess.setText("Web freigeben")

        else:
            for client in clients:
                client.allowInternetAccessThread()

            self.btnBlockWebAccess.setText("Web blockieren")

    def resetClients(self):
        """
        resets remote files and configuration for all connected clients
        """

        items = ["Ja, Namen zurücksetzen", "Nein, Namen beibehalten"]
        item, ok = QInputDialog().getItem(
            self, "LB-Status zurücksetzen",
            "USB-Sticks und Internet werden freigeben.\nDaten im Benutzerverzeichnis werden NICHT gelöscht.\nKandidaten-Namen zurücksetzen? ",
            items, 0, False)
        if ok is False:
            return

        resetCandidateName = True if item.startswith("Ja") else False

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]

        progressDialog = EcManProgressDialog(self, "Reset Clients")
        progressDialog.setMaxValue(self.grid_layout.count())
        progressDialog.resetValue()
        progressDialog.open()

        self.worker = ResetClientsWorker(clients, resetCandidateName)
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

    def closeEvent(self, event):
        """
        overrides closeEvent of base class, clean up

        """
        print("cleaning up...")
        #         try:* especially remove shares created on Windows hosts
        #             for share in self.sharenames:
        #                 Thread(target=self.runLocalPowerShellAsRoot("Remove-SmbShare -Name {} -Force".format(share))).start()
        #         except Exception:
        #             pass
        #
        print("done cleaning up...")
        super(MainWindow, self).closeEvent(event)

    def selectAllCLients(self):
        """
        marks / selects all connected client pcs
        """
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().select()

    def unselectAllCLients(self):
        """
        unmarks / unselects all connected client pcs
        """
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().unselect()

    def shutdownAllClients(self):
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().shutdownClient()

    def rebootAllClients(self):
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().computer.reboot()
            self.grid_layout.itemAt(i).widget().deleteLater()
        # TODO TEST sven
        #for i in range(self.grid_layout.count()):
        #    self.grid_layout.removeItem(self.grid_layout.itemAt(i))

    def sendMessage(self):

        message, ok = QInputDialog.getText(self, "Eingabe",
                                           "Nachricht an Kandidaten eingeben")
        if ok:
            for i in range(self.grid_layout.count()):
                QThreadPool.globalInstance().start(
                    SendMessageTask(
                        self.grid_layout.itemAt(i).widget(), message))

    def applyCandidateNames(self):
        """
        reads candidate names from respective textEditField (line by line)
        and applies these names to (random) client pcs
        """
        names = self.textEditCandidates.toPlainText().rstrip().splitlines()
        # cleanup and remove duplicate names
        names = [x.strip() for x in names]
        names = list(set(names))

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]

        # select only the computers without candidate name
        if self.checkBox_OverwriteExisitingNames.checkState(
        ) != Qt.CheckState.Checked:
            clients = [
                x for x in clients if x.computer.getCandidateName() == ""
                or x.computer.getCandidateName() is None
            ]

        if len(names) > len(clients):
            self.showMessageBox(
                "Fehler",
                "Nicht genug Prüfungs-PCs für alle {} Kandidaten".format(
                    str(len(names))),
                messageType=QMessageBox.Warning)
            return

        progressDialog = EcManProgressDialog(
            self, "Fortschritt Kandidatennamen setzen")
        progressDialog.setMaxValue(len(names))
        progressDialog.resetValue()

        self.worker = SetCandidateNamesWorker(clients, names)
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

        progressDialog.open()
        self.tabs.setCurrentWidget(self.tab_pcs)

    def configure(self):
        """
        sets inial values for app
        """
        self.port = 5986
        self.server = None

        self.configFile = Path(str(Path.home()) + "/.ecman.conf")

        if not (self.configFile.exists()):
            result = self.openConfigDialog()

        if self.configFile.exists() or result == 1:
            self.readConfigFile()

        self.result_directory = ""

        self.sharenames = [
        ]  # dump all eventually created local Windows-Shares in this array
        self.debug = True
        # fetch own ip adddress
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            s.connect(('9.9.9.9', 1))  # connect() for UDP doesn't send packets
            self.ip_address = s.getsockname()[0]
            parts = self.ip_address.split(".")[0:-1]
            self.ipRange = ".".join(parts) + ".*"
            self.lineEditIpRange.setText(self.ipRange)
            self.appTitle = self.windowTitle() + " on " + self.ip_address
            self.setWindowTitle(self.appTitle)
        except Exception as ex:
            self.ipRange, ok = QInputDialog.getText(
                self, "Keine Verbindung zum Internet",
                "Möglicherweise gelingt der Sichtflug. Bitte geben Sie die lokale IP-Adresse ein:"
            )

            self.log("no connection to internet:" + str(ex))

    def readConfigFile(self):
        """
        reads config file into class variables
        """
        self.config = ConfigParser()
        self.config.read_file(open(str(self.configFile)))

        self.port = self.config.get("General", "winrm_port", fallback=5986)

        self.client_lb_user = self.config.get("Client",
                                              "lb_user",
                                              fallback="student")
        self.user = self.config.get("Client", "user", fallback="")
        self.passwd = self.config.get("Client", "pwd", fallback="")
        self.maxFiles = int(
            self.config.get("Client", "max_files", fallback="1000"))
        self.maxFileSize = int(
            self.config.get("Client", "max_filesize",
                            fallback="1000")) * 1024 * 1024  # thats MB now...

        self.advancedUi = self.config.get('General',
                                          'advanced_ui',
                                          fallback="False") == "True"
        pass

    def openConfigDialog(self):
        """
        opens configuration dialog
        """
        config_dialog = EcManConfigDialog(self)
        result = config_dialog.exec_()
        if result == 1:
            config_dialog.saveConfig()
            self.readConfigFile()
        return result

    def log(self, message):
        """
        basic logging functionality
        TODO: improve...
        """
        self.logger.log(message)

    def updateProgressBar(self, value):
        self.progressBar.setValue(value)
        if (value == self.progressBar.maximum()):
            self.enableButtons(True)
            self.progressBar.setEnabled(False)

    def enableButtons(self, enable):

        if type(enable) != bool:
            raise Exception("Invalid parameter, must be boolean")
        self.btnNameClients.setEnabled(enable)
        self.btnSelectAllClients.setEnabled(enable)
        self.btnUnselectClients.setEnabled(enable)
        self.btnPrepareExam.setEnabled(enable)
        self.btnGetExams.setEnabled(enable)
        self.btnSaveExamLog.setEnabled(enable)
        self.btnDetectClient.setEnabled(enable)
        self.btnBlockWebAccess.setEnabled(enable)
        self.btnBlockUsb.setEnabled(enable)

    def retrieveExamFilesByWizard(self):
        """
        retrieve exam files for all clients
        """
        # find all suitable clients (required array for later threading)
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
            and self.grid_layout.itemAt(i).widget().computer.state in [
                Computer.State.STATE_DEPLOYED, Computer.State.STATE_FINISHED,
                Computer.State.STATE_RETRIVAL_FAIL
            ]
        ]

        if len(clients) == 0:
            self.showMessageBox("Achtung",
                                "Keine Clients ausgewählt bzw. deployed")
            return

        unknownClients = [
            c for c in clients if c.computer.getCandidateName() == ""
            or c.computer.getCandidateName() == None
        ]

        for unknown in unknownClients:
            unknown.computer.state = Computer.State.STATE_RETRIVAL_FAIL
            unknown._colorizeWidgetByClientState()

        if unknownClients != []:
            choice = QMessageBox.critical(
                self, "Achtung",
                "{} clients ohne gültigen Kandidatennamen.<br>Rückholung für alle anderen fortsetzen?"
                .format(str(len(unknownClients))), QMessageBox.Yes,
                QMessageBox.No)

            if choice == QMessageBox.No:
                return

        clients = [c for c in clients if c not in unknownClients]

        retVal = QMessageBox.StandardButton.Yes
        if self.result_directory != "":

            items = [
                "Ergebnispfad neu auswählen",
                "Weiter mit bisherigem Verzeichnis"
            ]
            item, ok = QInputDialog().getItem(
                self, "Achtung",
                "LB-Ergebnisverzeichnis ist bereits ausgewählt.\nErneutes Abholen kann existierende Ergebnisse überschreiben.\nWas möchten Sie tun?",
                items, 0, False)

            if ok is False:
                return

        if self.result_directory == "" or item == "Ergebnispfad neu auswählen":

            if self.server is None or self.server.connect() is not True:
                self.server = self.getServerCredentialsByWizard()
                if self.server is None:
                    return

            wizard = EcShareWizard(
                parent=self,
                server=self.server,
                wizardType=EcShareWizard.TYPE_RESULT_DESTINATION,
                advanced_Ui=self.advancedUi)

            wizard.setModal(True)
            result = wizard.exec_()
            print("I'm done, wizard result=" + str(result))
            if result == 1:
                print("selected values: %s - %s - %s" %
                      (wizard.field("username"), wizard.field("servername"),
                       wizard.selectedPath))

                self.result_directory = wizard.selectedPath

            else:
                print("Abbruch, kein Zielverzeichnis ausgewählt")
                return

        self.result_directory = self.result_directory.replace("/", "#")
        self.log("save result files into: " +
                 self.result_directory.replace("#", "\\"))

        progressDialog = EcManProgressDialog(
            self, "Fortschritt Ergebnisse kopieren")
        progressDialog.setMaxValue(len(clients))
        progressDialog.resetValue()
        progressDialog.open()

        self.log("starting to retrieve files")

        self.worker = RetrieveResultsWorker(clients, self.server.user,
                                            self.server.password,
                                            self.server.domain,
                                            self.result_directory,
                                            self.maxFiles, self.maxFileSize)
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

        self.btnSaveExamLog.setEnabled(True)

    def prepareExam(self):
        self.copyFilesToClient()
        pass

    def copyFilesToClient(self):
        """
        copies selected exam folder to all connected clients that are selected and not in STATE_DEPLOYED or STATE_FINISHED
        """
        if self.lb_directory == None or self.lb_directory == "":
            self.showMessageBox("Fehler", "Kein Prüfungsordner ausgewählt")
            return

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
            and self.grid_layout.itemAt(i).widget().computer.state not in
            [Computer.State.STATE_DEPLOYED, Computer.State.STATE_FINISHED]
        ]

        if len([x for x in clients if x.computer.candidateName == None]) > 0:
            self.showMessageBox("Warnung",
                                "Bitte Kandidatenname für alle PCs vergeben")
            return

        if len(clients) == 0:
            self.showMessageBox(
                "Warnung",
                "Keine Client-PCs ausgewählt oder Prüfungen bereits aktiv")
            return

        progressDialog = EcManProgressDialog(
            self, "Fortschritt LB-Client-Deployment")
        progressDialog.setMaxValue(len(clients))
        progressDialog.resetValue()
        progressDialog.open()

        self.worker = CopyExamsWorker(
            clients,
            self.server.user,
            self.server.password,
            self.server.domain,
            src=self.lb_directory,
            reset=(self.checkBoxWipeHomedir.checkState() ==
                   Qt.CheckState.Checked))
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

    def detectClients(self):
        """
        starts portscan to search for winrm enabled clients
        """
        ip_range = self.lineEditIpRange.text()
        if not (ip_range.endswith('*')):
            self.showMessageBox(
                'Eingabefehler',
                'Gültiger IP-V4 Bereich endet mit * (z.B. 192.168.0.*)')
            return

        try:
            self.worker.exit()
        except Exception as ex:
            print("crashed on stopping existing scanner thread: " + str(ex))

        self.ipRange = ip_range
        self.progressBar.setEnabled(True)
        self.progressBar.setValue(0)

        self.enableButtons(enable=False)
        self.clientCount = 0
        # clear previous client buttons
        try:
            for i in reversed(range(self.grid_layout.count())):
                self.grid_layout.itemAt(i).widget().close()
                self.grid_layout.itemAt(i).widget().deleteLater()
        except:
            pass
        self.clientFrame.setLayout(self.grid_layout)

        self.progressBar.setMaximum(253)
        self.worker = ScannerWorker(self.ipRange, self.ip_address)
        self.worker.updateProgressSignal.connect(self.updateProgressBar)
        self.worker.addClientSignal.connect(self.addClient)
        self.worker.start()

    def addClient(self, ip):
        """
        populate GUI with newly received client ips
        param ip: only last byte required
        param scan: wether or not scan the Client (set to False only for GUI testing)
        """
        self.log("new client signal received: " + str(ip))
        self.clientCount += 1
        self.statusBar.showMessage(
            str(self.clientCount) + " clients detected", 0)

        clientIp = self.ipRange.replace("*", str(ip))
        button = LbClient(clientIp,
                          remoteAdminUser=self.user,
                          passwd=self.passwd,
                          candidateLogin=self.client_lb_user,
                          parentApp=self)
        button.setMinimumHeight(50)
        # button.installEventFilter(self)
        self.grid_layout.addWidget(button,
                                   self.grid_layout.count() / 4,
                                   self.grid_layout.count() % 4)
        self.clientFrame.setLayout(self.grid_layout)
        # QtGui.qApp.processEvents()

    def addTestClient(self, ip):
        """
        populate GUI with dummy buttons
        param ip: only last byte required
        """
        self.log("new client signal received: " + str(ip))
        clientIp = self.ipRange.replace("*", str(ip))
        button = LbClient(clientIp,
                          remoteAdminUser=self.user,
                          passwd=self.passwd,
                          candidateLogin=self.client_lb_user,
                          parentApp=self,
                          test=True)
        button.setMinimumHeight(50)
        # button.installEventFilter(self)
        self.grid_layout.addWidget(button,
                                   self.grid_layout.count() / 4,
                                   self.grid_layout.count() % 4)
        self.clientFrame.setLayout(self.grid_layout)
        # QtGui.qApp.processEvents()

    def getExamPath(self):
        return self.lb_directory

    def getServerCredentialsByWizard(self):
        """
        open server config and login dialog, returns server object or None
        """
        wizard = EcLoginWizard(parent=self,
                               username=self.config.get("General",
                                                        "username",
                                                        fallback=""),
                               domain=self.config.get("General",
                                                      "domain",
                                                      fallback=""),
                               servername=self.config.get("General",
                                                          "lb_server",
                                                          fallback=""))

        wizard.setModal(True)
        result = wizard.exec_()
        print("I'm done, wizard result=" + str(result))
        if result == 1:
            self.config["General"]["username"] = wizard.field("username")
            self.config["General"]["domain"] = wizard.field("domainname")
            self.config["General"]["servername"] = wizard.server.serverName

            self.saveConfig()
            return wizard.server

        return None

    def selectExamByWizard(self):
        """
        provides ability to select serverName share plus logon credentials and lb directory using a wizard
        """

        if self.server == None or self.server.connect() is False:
            self.server = self.getServerCredentialsByWizard()

        wizard = EcShareWizard(parent=self,
                               server=self.server,
                               wizardType=EcShareWizard.TYPE_LB_SELECTION,
                               advanced_Ui=self.advancedUi)

        wizard.setModal(True)
        result = wizard.exec_()
        print("I'm done, wizard result=" + str(result))
        if result == 1:
            self.lb_directory = wizard.selectedPath
            self.setWindowTitle(self.appTitle + " - LB-Verzeichnis::" +
                                self.lb_directory.split("/")[-1])
            self.log("setup LB directory: " + self.lb_directory.split("/")[-1])
            self.btnPrepareExam.setEnabled(True)
            self.lblExamName.setText(self.lb_directory)
        else:
            self.log("no valid share selected")

    def saveExamLog(self):
        """
        on demand, store all client logs as PDF
        """
        if self.result_directory == None or len(self.result_directory) == 0:
            self.showMessageBox(
                "Fehler",
                "Ergebnispfad für Prüfungsdaten nicht gesetzt.<br>Bitte zuerst Prüfungsdaten abholen.",
                QMessageBox.Error)
            return

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        for client in clients:
            lb_dataDirectory = client.computer.lb_dataDirectory.split("#")[-1]
            pdfFileName = "protocol_" + date.today(
            ).__str__() + "_" + client.computer.getCandidateName().replace(
                " ", "_") + ".pdf"
            LogfileHandler(client.computer.logfile_name, client.computer.getCandidateName()). \
                createPdf(pdfFileName)
            if not (self.server.connect()):
                self.showMessageBox(
                    "Fehler",
                    "Verbindung zum Server kann nicht aufgebaut werden.",
                    QMessageBox.Error)
                return

            with open(pdfFileName, "rb") as file:
                sharename = self.result_directory.replace("##",
                                                          "").split("#")[1]
                destination = "/".join(
                    self.result_directory.replace(
                        "##",
                        "").split("#")[2:]) + "/" + lb_dataDirectory + "/"
                self.server.conn.storeFile(sharename,
                                           destination + pdfFileName, file)

    def __runLocalPowerShellAsRoot(self, command):
        """
        maybe useful at some stage again
        # see https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew#parameters
        :param command:
        :return:
        """

        retval = ctypes.windll.shell32.ShellExecuteW(
            None,
            "runas",  # runas admin,
            "C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\powershell.exe",
            # file to run
            command,  # actual powershell command
            None,
            0)  # last param disables popup powershell window...

        if retval != 42:
            self.log("ReturnCode after creating smbShare: " + str(retval))
            subprocess.run([
                "C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\powershell.exe",
                "Get-SmbShare"
            ])
            raise Exception("ReturnCode after running powershell: " +
                            str(retval))

    def __openFile(self):
        fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')
        if fname[0]:
            f = open(fname[0], 'r')
        with f:
            data = f.read()
            doc = QTextDocument(data, None)
            self.textEditLog.setDocument(doc)

    def saveConfig(self):
        """
        write to file what's currently set in config
        """
        if not (self.configFile.exists()):
            self.configFile.touch()

        self.config.write(open(self.configFile, 'w'))

    def eventFilter(self, currentObject, event):
        """
        unused, define mouseover events (with tooltips) for LbClient widgets
        """
        if event.type() == QEvent.Enter:
            if isinstance(currentObject, LbClient):
                print("Mouseover event catched")
                currentObject.setOwnToolTip()
                return True
            else:
                self.log(str(type(currentObject)) + " not recognized")

                # elif event.type() == QEvent.Leave:
        #    pass
        return False

    def showMessageBox(self,
                       title,
                       message,
                       messageType=QMessageBox.Information):
        """
        convinence wrapper
        """
        msg = QMessageBox(messageType, title, message, parent=self)
        if messageType != QMessageBox.Information:
            msg.setStandardButtons(QMessageBox.Abort)
        return msg.exec_()

    def showVersionInfo(self):
        info = "<b>Offizielles Release:</b><br>" + version
        try:
            # latest release - make sure to save this file before building :-)
            modDate = time.localtime(os.path.getmtime(__file__))
            info = info + "<br><br><b>Diese Version:</b><br>" + time.strftime(
                "%Y-%m-%d", modDate)
        except:
            pass

        self.showMessageBox("ECMan - Version", info)
Пример #23
0
class Main_Window(QMainWindow):
    '''Our Main Window Class'''
    def __init__(self):
        '''Constructure Function'''
        QMainWindow.__init__(self)
        self.setWindowTitle("A Sample Text Editor")
        self.setWindowIcon(QIcon('doc_open.png'))
        self.setGeometry(350, 250, 550, 400)  # self.setGeometry(x,y,w,h)
        #self.setMinimumSize(250,250)# Size -> Width * Height
        #self.setMaximumSize(750,750)
        self.CreateStatusBar()
        #self.CreateProgressBar()
        #self.showProgress()
        self.setUpComponents()

    def setUpComponents(self):
        '''Setting Up Central Widget'''
        self.textEdit = QTextEdit("Enter your Text Here")
        self.setCentralWidget(self.textEdit)

        self.CreateActions()
        self.CreateMenus()
        self.CreateToolBar()

        self.fileMenu.addAction(self.newAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAction)
        self.fileMenu.addAction(self.copyAction)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.pasteAction)
        self.fileMenu.addAction(self.aboutAction)

        self.mainToolbar.addAction(self.newAction)
        self.mainToolbar.addSeparator()
        self.mainToolbar.addAction(self.copyAction)
        self.mainToolbar.addAction(self.pasteAction)

    def CreateToolBar(self):
        '''Function to Create Toolbar'''
        self.mainToolbar = self.addToolBar('Main')

    #Slots Menu When Menu actions are Triggered
    def newFile(self):
        self.textEdit.setText('')

    def exitFile(self):
        self.close()

    def aboutHelp(self):
        QMessageBox.about(self, "About Simple Text Editor",
                          "This Example Demonstrate the Use of Menu Bar")

    def CreateActions(self):
        '''Functions to Create actions for Menus'''
        self.newAction = QAction(QIcon('doc_open.png'),
                                 '&New',
                                 self,
                                 shortcut=QKeySequence.New,
                                 statusTip='Create a New File',
                                 triggered=self.newFile)
        self.exitAction = QAction(QIcon('doc_open.png'),
                                  'E&xit',
                                  self,
                                  shortcut="Ctrl+Q",
                                  statusTip="Exit the Application",
                                  triggered=self.exitFile)
        self.copyAction = QAction(QIcon("doc_open.png"),
                                  'C&opy',
                                  self,
                                  shortcut="Ctrl+c",
                                  statusTip="Copy",
                                  triggered=self.textEdit.copy)
        self.pasteAction = QAction(QIcon('paste.png'),
                                   '&Paste',
                                   self,
                                   shortcut="Ctrl+V",
                                   statusTip="Paste",
                                   triggered=self.textEdit.paste)
        self.aboutAction = QAction(QIcon('about.png'),
                                   'A&bout',
                                   self,
                                   statusTip="Displays info about text editor",
                                   triggered=self.aboutHelp)

    def CreateMenus(self):
        #Actual Menu Bar Item Creation
        '''Function to Create actual menu bar'''
        self.fileMenu = self.menuBar().addMenu("&File")
        self.editMenu = self.menuBar().addMenu("&Edit")
        self.helpMenu = self.menuBar().addMenu("&Help")

    def showProgress(self):
        '''Function to Show Progress'''
        self.CreateProgressBar()
        while (self.progressbar.value() < self.progressbar.maximum()):
            self.progressbar.setValue(self.progressbar.value() + 10)
            time.sleep(0.5)
        self.myProgressMessage.setText("Ready")

    def CreateProgressBar(self):
        '''Function to Create Progrss Bar'''
        self.myProgressMessage = QLabel("In Progress")
        self.progressbar = QProgressBar()
        self.progressbar.setMinimum(0)
        self.progressbar.setMaximum(100)
        self.myProgressStatusBar = QStatusBar()
        self.progressbar.setValue(0)
        self.myProgressStatusBar.addWidget(self.myProgressMessage, 1)
        self.myProgressStatusBar.addWidget(self.progressbar, 2)
        self.setStatusBar(self.myProgressStatusBar)

    def CreateStatusBar(self):
        '''Function to Create Status Bar'''
        self.myStatusBar = QStatusBar()
        self.myStatusBar.showMessage("Getting Ready", 2000)
        self.setStatusBar(self.myStatusBar)
Пример #24
0
class main(QWidget):
    def __init__(self, app: QApplication):
        super().__init__()

        logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s',
                            level=logging.DEBUG,
                            datefmt='%Y-%m-%d %H:%M:%S')
        logging.info("Application started")

        # Generic setup stuff
        self.app = app
        # Load Dark mode because it's 2021
        styleStream = QFile(
            os.path.join(os.path.dirname(__file__),
                         'resources/qdarkstyle/style.qss'))
        styleStream.open(QIODevice.ReadOnly)
        self.ensurePolished()
        self.app.setStyleSheet(QTextStream(styleStream).readAll())
        self.ui = mainwindow.Ui_main()
        self.ui.setupUi(self)

        # Add status bar manually because Qt Creator doesn't seem to support it
        self.statusBar = QStatusBar(self)
        styleString = ("font: Waree; font-size: 32px;")
        self.statusBar.setStyleSheet(styleString)
        self.ui.verticalLayout_2.addWidget(self.statusBar)
        self.statusBar.showMessage("Hello World!", 2000)

        # Static signal connections
        self.ui.button_qt.clicked.connect(self.app.aboutQt)

        # Make list of always visible buttons to pass to everything
        self.buttons = [
            self.ui.button_rhs_0, self.ui.button_rhs_1, self.ui.button_rhs_2,
            self.ui.button_rhs_3, self.ui.button_rhs_4, self.ui.button_rhs_5
        ]

        # Warning Handler
        self.warnings = WarningHandler(self.ui.tab_widget,
                                       self.ui.table_warnings, self.buttons,
                                       self.statusBar)

        # MQTT Stuff
        with MqttHandler(MQTT_ADDRESS, MQTT_PORT,
                         self.warnings) as self.mqtt, NetworkHandler(
                             self.ui.tabWidget_network, self.mqtt,
                             self.warnings, self.buttons) as self.network:
            self.ui.label_mqttAddr.setText(MQTT_ADDRESS)
            self.ui.label_mqttPort.setText(str(MQTT_PORT))

            # Info / Status Handler
            self.info = InfoHandler(self.ui, self.warnings)

            # Add callback for when tab is changed
            self.ui.tab_widget.currentChanged.connect(self.tabChangeDespatcher)

            # VFO Declaration
            self.vfoA = Vfo("A", self.mqtt, self.network, self.warnings,
                            self.ui.button_A_rx, self.ui.button_A_tx,
                            self.ui.label_A_fc, self.ui.label_A_bw,
                            self.ui.button_A_mode, self.ui.button_A_freq)
            self.vfoB = Vfo("B", self.mqtt, self.network, self.warnings,
                            self.ui.button_B_rx, self.ui.button_B_tx,
                            self.ui.label_B_fc, self.ui.label_B_bw,
                            self.ui.button_B_mode, self.ui.button_B_freq)

            # Doesn't count as change on startup so poke to ensure tab gets updated
            self.tabChangeDespatcher(self.ui.tab_widget.currentIndex())

            self.showFullScreen()
            self.app.exec_()

    def keyPressEvent(self, event):
        if event.key() != 16777248:  # Shift key
            logging.debug(f"Key {event.key()} ({event.text()}) pressed")

            if (event.text() == 'a'):
                self.vfoA.increment()
            elif (event.text() == 'A'):
                self.vfoA.decrement()
            elif (event.text() == 'G'):
                self.app.exit()  # DEBUG

    def tabChangeDespatcher(self, tabId):
        logging.debug(f"Changing tab to {tabId}")
        for x in self.buttons:
            x.setText("")
            x.setEnabled(False)
            x.setCheckable(False)
            try:
                x.clicked.disconnect()
            except RuntimeError:
                pass
            try:
                x.toggled.disconnect()
            except RuntimeError:
                pass

        if (tabId == TAB_HOME):
            pass  # @TODO
        elif (tabId == TAB_SETTINGS):
            pass  # @TODO
        elif (tabId == TAB_INFO):
            self.info.tab_enabled()
        elif (tabId == TAB_WARNINGS):
            self.warnings.tab_enabled()
        elif (tabId == TAB_NETWORK):
            self.network.tab_enabled()
        else:
            raise NotImplementedError
Пример #25
0
class View(QMainWindow):
    def __init__(self, model, controller):
        super().__init__()

        self._model = model
        self._controller = controller
        self.segmentcursor = False
        self.togglecolors = {"#1f77b4": "m", "m": "#1f77b4"}

        #################################################################
        # define GUI layout and connect input widgets to external slots #
        #################################################################

        self.setWindowTitle("biopeaks")
        self.setGeometry(50, 50, 1750, 750)
        self.setWindowIcon(QIcon(":/python_icon.png"))

        # figure0 for signal
        self.figure0 = Figure()
        self.canvas0 = FigureCanvas(self.figure0)
        # Enforce minimum height, otherwise resizing with self.splitter causes
        # mpl to throw an error because figure is resized to height 0. The
        # widget can still be fully collapsed with self.splitter-
        self.canvas0.setMinimumHeight(1)  # in pixels
        self.ax00 = self.figure0.add_subplot(1, 1, 1)
        self.ax00.set_frame_on(False)
        self.figure0.subplots_adjust(left=0.04, right=0.98, bottom=0.25)
        self.line00 = None
        self.scat = None
        self.segmentspan = None

        # figure1 for marker
        self.figure1 = Figure()
        self.canvas1 = FigureCanvas(self.figure1)
        self.canvas1.setMinimumHeight(1)
        self.ax10 = self.figure1.add_subplot(1, 1, 1, sharex=self.ax00)
        self.ax10.get_xaxis().set_visible(False)
        self.ax10.set_frame_on(False)
        self.figure1.subplots_adjust(left=0.04, right=0.98)
        self.line10 = None

        # figure2 for statistics
        self.figure2 = Figure()
        self.canvas2 = FigureCanvas(self.figure2)
        self.canvas2.setMinimumHeight(1)
        self.ax20 = self.figure2.add_subplot(3, 1, 1, sharex=self.ax00)
        self.ax20.get_xaxis().set_visible(False)
        self.ax20.set_frame_on(False)
        self.line20 = None
        self.ax21 = self.figure2.add_subplot(3, 1, 2, sharex=self.ax00)
        self.ax21.get_xaxis().set_visible(False)
        self.ax21.set_frame_on(False)
        self.line21 = None
        self.ax22 = self.figure2.add_subplot(3, 1, 3, sharex=self.ax00)
        self.ax22.get_xaxis().set_visible(False)
        self.ax22.set_frame_on(False)
        self.line22 = None
        self.figure2.subplots_adjust(left=0.04, right=0.98)

        # navigation bar
        self.navitools = CustomNavigationToolbar(self.canvas0, self)

        # peak editing
        self.editcheckbox = QCheckBox("editable", self)
        self.editcheckbox.stateChanged.connect(self._model.set_peakseditable)

        # peak saving batch
        self.savecheckbox = QCheckBox("save during batch processing", self)
        self.savecheckbox.stateChanged.connect(self._model.set_savebatchpeaks)

        # peak auto-correction batch
        self.correctcheckbox = QCheckBox("correct during batch processing",
                                         self)
        self.correctcheckbox.stateChanged.connect(
            self._model.set_correctbatchpeaks)

        # selecting stats for saving
        self.periodcheckbox = QCheckBox("period", self)
        self.periodcheckbox.stateChanged.connect(
            lambda: self.select_stats("period"))
        self.ratecheckbox = QCheckBox("rate", self)
        self.ratecheckbox.stateChanged.connect(
            lambda: self.select_stats("rate"))
        self.tidalampcheckbox = QCheckBox("tidal amplitude", self)
        self.tidalampcheckbox.stateChanged.connect(
            lambda: self.select_stats("tidalamp"))

        # channel selection
        self.sigchanmenulabel = QLabel("biosignal")
        self.sigchanmenu = QComboBox(self)
        self.sigchanmenu.addItem("A1")
        self.sigchanmenu.addItem("A2")
        self.sigchanmenu.addItem("A3")
        self.sigchanmenu.addItem("A4")
        self.sigchanmenu.addItem("A5")
        self.sigchanmenu.addItem("A6")
        self.sigchanmenu.currentTextChanged.connect(self._model.set_signalchan)
        # initialize with default value
        self._model.set_signalchan(self.sigchanmenu.currentText())

        self.markerchanmenulabel = QLabel("marker")
        self.markerchanmenu = QComboBox(self)
        self.markerchanmenu.addItem("none")
        self.markerchanmenu.addItem("I1")
        self.markerchanmenu.addItem("I2")
        self.markerchanmenu.addItem("A1")
        self.markerchanmenu.addItem("A2")
        self.markerchanmenu.addItem("A3")
        self.markerchanmenu.addItem("A4")
        self.markerchanmenu.addItem("A5")
        self.markerchanmenu.addItem("A6")
        self.markerchanmenu.currentTextChanged.connect(
            self._model.set_markerchan)
        # initialize with default value
        self._model.set_markerchan(self.markerchanmenu.currentText())

        # processing mode (batch or single file)
        self.batchmenulabel = QLabel("mode")
        self.batchmenu = QComboBox(self)
        self.batchmenu.addItem("single file")
        self.batchmenu.addItem("multiple files")
        self.batchmenu.currentTextChanged.connect(self._model.set_batchmode)
        self.batchmenu.currentTextChanged.connect(self.toggle_options)
        # initialize with default value
        self._model.set_batchmode(self.batchmenu.currentText())
        self.toggle_options(self.batchmenu.currentText())

        # modality selection
        self.modmenulabel = QLabel("modality")
        self.modmenu = QComboBox(self)
        self.modmenu.addItem("ECG")
        self.modmenu.addItem("PPG")
        self.modmenu.addItem("RESP")
        self.modmenu.currentTextChanged.connect(self._model.set_modality)
        self.modmenu.currentTextChanged.connect(self.toggle_options)
        # initialize with default value
        self._model.set_modality(self.modmenu.currentText())
        self.toggle_options(self.modmenu.currentText())

        # segment selection; this widget can be openend / set visible from
        # the menu and closed from within itself (see mapping of segmentermap);
        # it provides utilities to select a segment from the signal
        self.segmentermap = QSignalMapper(self)
        self.segmenter = QDockWidget("select a segment", self)
        # disable closing such that widget can only be closed by confirming
        # selection or custom button
        self.segmenter.setFeatures(QDockWidget.NoDockWidgetFeatures)
        # Limit number of decimals to four.
        regex = QRegExp("[0-9]*\.?[0-9]{4}")
        validator = QRegExpValidator(regex)

        self.startlabel = QLabel("start")
        self.startedit = QLineEdit()
        self.startedit.setValidator(validator)

        self.endlabel = QLabel("end")
        self.endedit = QLineEdit()
        self.endedit.setValidator(validator)

        segmentfromcursor = QAction(QIcon(":/mouse_icon.png"),
                                    "select with mouse", self)
        segmentfromcursor.triggered.connect(self.enable_segmentedit)
        self.startedit.addAction(segmentfromcursor, QLineEdit.TrailingPosition)
        self.endedit.addAction(segmentfromcursor, QLineEdit.TrailingPosition)

        self.previewedit = QPushButton("preview segment")
        lambdafn = lambda: self._model.set_segment(
            [self.startedit.text(), self.endedit.text()])
        self.previewedit.clicked.connect(lambdafn)

        self.confirmedit = QPushButton("confirm segment")
        self.confirmedit.clicked.connect(self._controller.segment_signal)
        self.confirmedit.clicked.connect(self.segmentermap.map)
        self.segmentermap.setMapping(self.confirmedit, 0)

        self.abortedit = QPushButton("abort segmentation")
        self.abortedit.clicked.connect(self.segmentermap.map)
        # reset the segment to None
        self.segmentermap.setMapping(self.abortedit, 2)

        self.segmenterlayout = QFormLayout()
        self.segmenterlayout.addRow(self.startlabel, self.startedit)
        self.segmenterlayout.addRow(self.endlabel, self.endedit)
        self.segmenterlayout.addRow(self.previewedit)
        self.segmenterlayout.addRow(self.confirmedit)
        self.segmenterlayout.addRow(self.abortedit)
        self.segmenterwidget = QWidget()
        self.segmenterwidget.setLayout(self.segmenterlayout)
        self.segmenter.setWidget(self.segmenterwidget)

        self.segmenter.setVisible(False)
        self.segmenter.setAllowedAreas(Qt.RightDockWidgetArea)
        self.addDockWidget(Qt.RightDockWidgetArea, self.segmenter)

        # Set up dialog to gather user input for custom files.

        regex = QRegExp("[1-9][0-9]")
        validator = QRegExpValidator(regex)

        self.signallabel = QLabel("biosignal column")
        self.signaledit = QLineEdit()
        self.signaledit.setValidator(validator)

        self.markerlabel = QLabel("marker column")
        self.markeredit = QLineEdit()
        self.markeredit.setValidator(validator)

        regex = QRegExp("[0-9]{2}")
        validator = QRegExpValidator(regex)

        self.headerrowslabel = QLabel("number of header rows")
        self.headerrowsedit = QLineEdit()
        self.headerrowsedit.setValidator(validator)

        regex = QRegExp("[0-9]{5}")
        validator = QRegExpValidator(regex)

        self.sfreqlabel = QLabel("sampling rate")
        self.sfreqedit = QLineEdit()
        self.sfreqedit.setValidator(validator)

        self.separatorlabel = QLabel("column separator")
        self.separatormenu = QComboBox(self)
        self.separatormenu.addItem("comma")
        self.separatormenu.addItem("tab")
        self.separatormenu.addItem("colon")
        self.separatormenu.addItem("space")

        self.continuecustomfile = QPushButton("continue loading file")
        self.continuecustomfile.clicked.connect(self.set_customheader)

        self.customfiledialog = QDialog()
        self.customfiledialog.setWindowTitle("custom file info")
        self.customfiledialog.setWindowIcon(QIcon(":/file_icon.png"))
        self.customfiledialog.setWindowFlags(
            Qt.WindowCloseButtonHint
        )  # remove help button by only setting close button
        self.customfilelayout = QFormLayout()
        self.customfilelayout.addRow(self.signallabel, self.signaledit)
        self.customfilelayout.addRow(self.markerlabel, self.markeredit)
        self.customfilelayout.addRow(self.separatorlabel, self.separatormenu)
        self.customfilelayout.addRow(self.headerrowslabel, self.headerrowsedit)
        self.customfilelayout.addRow(self.sfreqlabel, self.sfreqedit)
        self.customfilelayout.addRow(self.continuecustomfile)
        self.customfiledialog.setLayout(self.customfilelayout)

        # set up menubar
        menubar = self.menuBar()

        # signal menu
        signalmenu = menubar.addMenu("biosignal")

        openSignal = signalmenu.addMenu("load")
        openEDF = QAction("EDF", self)
        openEDF.triggered.connect(lambda: self._model.set_filetype("EDF"))
        openEDF.triggered.connect(self._controller.get_fpaths)
        openSignal.addAction(openEDF)
        openOpenSignals = QAction("OpenSignals", self)
        openOpenSignals.triggered.connect(
            lambda: self._model.set_filetype("OpenSignals"))
        openOpenSignals.triggered.connect(self._controller.get_fpaths)
        openSignal.addAction(openOpenSignals)
        openCustom = QAction("Custom", self)
        openCustom.triggered.connect(
            lambda: self._model.set_filetype("Custom"))
        openCustom.triggered.connect(lambda: self.customfiledialog.exec_())
        openSignal.addAction(openCustom)

        segmentSignal = QAction("select segment", self)
        segmentSignal.triggered.connect(self.segmentermap.map)
        self.segmentermap.setMapping(segmentSignal, 1)
        signalmenu.addAction(segmentSignal)

        self.segmentermap.mapped.connect(self.toggle_segmenter)

        saveSignal = QAction("save", self)
        saveSignal.triggered.connect(self._controller.get_wpathsignal)
        signalmenu.addAction(saveSignal)

        # peak menu
        peakmenu = menubar.addMenu("peaks")

        findPeaks = QAction("find", self)
        findPeaks.triggered.connect(self._controller.find_peaks)
        peakmenu.addAction(findPeaks)

        autocorrectPeaks = QAction("autocorrect", self)
        autocorrectPeaks.triggered.connect(self._controller.autocorrect_peaks)
        peakmenu.addAction(autocorrectPeaks)

        savePeaks = QAction("save", self)
        savePeaks.triggered.connect(self._controller.get_wpathpeaks)
        peakmenu.addAction(savePeaks)

        loadPeaks = QAction("load", self)
        loadPeaks.triggered.connect(self._controller.get_rpathpeaks)
        peakmenu.addAction(loadPeaks)

        # stats menu
        statsmenu = menubar.addMenu("statistics")

        calculateStats = QAction("calculate", self)
        calculateStats.triggered.connect(self._controller.calculate_stats)
        statsmenu.addAction(calculateStats)

        saveStats = QAction("save", self)
        saveStats.triggered.connect(self._controller.get_wpathstats)
        statsmenu.addAction(saveStats)

        # set up status bar to display error messages and current file path
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.progressBar = QProgressBar(self)
        self.progressBar.setRange(0, 1)
        self.statusBar.addPermanentWidget(self.progressBar)
        self.currentFile = QLabel()
        self.statusBar.addPermanentWidget(self.currentFile)

        # set up the central widget containing the plot and navigationtoolbar
        self.centwidget = QWidget()
        self.setCentralWidget(self.centwidget)

        # connect canvas0 to keyboard and mouse input for peak editing;
        # only widgets (e.g. canvas) that currently have focus capture
        # keyboard input: "You must enable keyboard focus for a widget if
        # it processes keyboard events."
        self.canvas0.setFocusPolicy(Qt.ClickFocus)
        self.canvas0.setFocus()
        self.canvas0.mpl_connect("key_press_event",
                                 self._controller.edit_peaks)
        self.canvas0.mpl_connect("button_press_event", self.get_xcursor)

        # arrange the three figure canvases in splitter object
        self.splitter = QSplitter(Qt.Vertical)
        # setting opaque resizing to false is important, since resizing gets
        # very slow otherwise once axes are populated
        self.splitter.setOpaqueResize(False)
        self.splitter.addWidget(self.canvas0)
        self.splitter.addWidget(self.canvas1)
        self.splitter.addWidget(self.canvas2)
        self.splitter.setChildrenCollapsible(False)

        # define GUI layout
        self.vlayout0 = QVBoxLayout(self.centwidget)
        self.vlayout1 = QVBoxLayout()
        self.vlayoutA = QFormLayout()
        self.vlayoutB = QFormLayout()
        self.vlayoutC = QVBoxLayout()
        self.vlayoutD = QVBoxLayout()
        self.hlayout0 = QHBoxLayout()

        self.optionsgroupA = QGroupBox("processing options")
        self.vlayoutA.addRow(self.modmenulabel, self.modmenu)
        self.vlayoutA.addRow(self.batchmenulabel, self.batchmenu)
        self.optionsgroupA.setLayout(self.vlayoutA)

        self.optionsgroupB = QGroupBox("channels")
        self.vlayoutB.addRow(self.sigchanmenulabel, self.sigchanmenu)
        self.vlayoutB.addRow(self.markerchanmenulabel, self.markerchanmenu)
        self.optionsgroupB.setLayout(self.vlayoutB)

        self.optionsgroupC = QGroupBox("peaks")
        self.vlayoutC.addWidget(self.editcheckbox)
        self.vlayoutC.addWidget(self.savecheckbox)
        self.vlayoutC.addWidget(self.correctcheckbox)
        self.optionsgroupC.setLayout(self.vlayoutC)

        self.optionsgroupD = QGroupBox("select statistics for saving")
        self.vlayoutD.addWidget(self.periodcheckbox)
        self.vlayoutD.addWidget(self.ratecheckbox)
        self.vlayoutD.addWidget(self.tidalampcheckbox)
        self.optionsgroupD.setLayout(self.vlayoutD)

        self.vlayout1.addWidget(self.optionsgroupA)
        self.vlayout1.addWidget(self.optionsgroupB)
        self.vlayout1.addWidget(self.optionsgroupC)
        self.vlayout1.addWidget(self.optionsgroupD)
        self.optionsgroupwidget = QWidget()
        self.optionsgroupwidget.setLayout(self.vlayout1)
        self.optionsgroup = QDockWidget("configurations", self)
        self.optionsgroup.setAllowedAreas(Qt.LeftDockWidgetArea)
        self.toggleoptionsgroup = self.optionsgroup.toggleViewAction()
        self.toggleoptionsgroup.setText("show/hide configurations")
        menubar.addAction(self.toggleoptionsgroup)
        self.optionsgroup.setWidget(self.optionsgroupwidget)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.optionsgroup)

        self.vlayout0.addWidget(self.splitter)

        self.hlayout0.addWidget(self.navitools)
        self.vlayout0.addLayout(self.hlayout0)

        ##############################################
        # connect output widgets to external signals #
        ##############################################
        self._model.signal_changed.connect(self.plot_signal)
        self._model.marker_changed.connect(self.plot_marker)
        self._model.peaks_changed.connect(self.plot_peaks)
        self._model.period_changed.connect(self.plot_period)
        self._model.rate_changed.connect(self.plot_rate)
        self._model.tidalamp_changed.connect(self.plot_tidalamp)
        self._model.path_changed.connect(self.display_path)
        self._model.segment_changed.connect(self.plot_segment)
        self._model.status_changed.connect(self.display_status)
        self._model.progress_changed.connect(self.display_progress)
        self._model.model_reset.connect(self.reset_plot)

    ###########
    # methods #
    ###########

    def plot_signal(self, value):
        self.ax00.clear()
        self.ax00.relim()
        # reset navitools history
        self.navitools.update()
        self.line00 = self.ax00.plot(self._model.sec, value, zorder=1)
        self.ax00.set_xlabel("seconds", fontsize="large", fontweight="heavy")
        self.canvas0.draw()
#        print("plot_signal listening")
#        print(self.ax0.collections, self.ax0.patches, self.ax0.artists)

    def plot_peaks(self, value):
        # self.scat is listed in ax.collections
        if self.ax00.collections:
            self.ax00.collections[0].remove()
        self.scat = self.ax00.scatter(self._model.sec[value],
                                      self._model.signal[value],
                                      c="m",
                                      zorder=2)
        self.canvas0.draw()
#        print("plot_peaks listening")
#        print(self.ax0.collections, self.ax0.patches, self.ax0.artists)

    def plot_segment(self, value):
        # If an invalid signal has been selected reset the segmenter interface.
        if value is None:
            self.toggle_segmenter(1)
            return
        if self.ax00.patches:  # self.segementspan is listed in ax.patches
            self.ax00.patches[0].remove()
        self.segmentspan = self.ax00.axvspan(value[0],
                                             value[1],
                                             color="m",
                                             alpha=0.25)
        self.canvas0.draw()
        self.confirmedit.setEnabled(True)
#        print(self.ax0.collections, self.ax0.patches, self.ax0.artists)

    def plot_marker(self, value):
        self.ax10.clear()
        self.ax10.relim()
        self.line10 = self.ax10.plot(value[0], value[1])
        self.canvas1.draw()
#        print("plot_marker listening")

    def plot_period(self, value):
        self.ax20.clear()
        self.ax20.relim()
        self.navitools.home()
        if self._model.savestats["period"]:
            self.line20 = self.ax20.plot(self._model.sec, value, c="m")
        else:
            self.line20 = self.ax20.plot(self._model.sec, value)
        self.ax20.set_ylim(bottom=min(value), top=max(value))
        self.ax20.set_title("period", pad=0, fontweight="heavy")
        self.ax20.grid(True, axis="y")
        self.navitools.update()
        self.canvas2.draw()
#        print("plot_period listening")

    def plot_rate(self, value):
        self.ax21.clear()
        self.ax21.relim()
        self.navitools.home()
        if self._model.savestats["rate"]:
            self.line21 = self.ax21.plot(self._model.sec, value, c="m")
        else:
            self.line21 = self.ax21.plot(self._model.sec, value)
        self.ax21.set_ylim(bottom=min(value), top=max(value))
        self.ax21.set_title("rate", pad=0, fontweight="heavy")
        self.ax21.grid(True, axis="y")
        self.navitools.update()
        self.canvas2.draw()
#        print("plot_rate listening")

    def plot_tidalamp(self, value):
        self.ax22.clear()
        self.ax22.relim()
        self.navitools.home()
        if self._model.savestats["tidalamp"]:
            self.line22 = self.ax22.plot(self._model.sec, value, c="m")
        else:
            self.line22 = self.ax22.plot(self._model.sec, value)
        self.ax22.set_ylim(bottom=min(value), top=max(value))
        self.ax22.set_title("amplitude", pad=0, fontweight="heavy")
        self.ax22.grid(True, axis="y")
        self.navitools.update()
        self.canvas2.draw()
#        print("plot_tidalamp listening")

    def display_path(self, value):
        self.currentFile.setText(value)

    def display_status(self, status):
        # display status until new status is set
        self.statusBar.showMessage(status)

    def display_progress(self, value):
        # if value is 0, the progressbar indicates a busy state
        self.progressBar.setRange(0, value)

    def toggle_segmenter(self, value):
        if not self._model.loaded:
            return
        # Open segmenter when called from signalmenu or clear segmenter
        # upon selection of invalid segment.
        if value == 1:
            self.segmenter.setVisible(True)
            self.confirmedit.setEnabled(False)
            self.startedit.clear()
            self.endedit.clear()
            if self.ax00.patches:
                self.ax00.patches[0].remove()
                self.canvas0.draw()
        # Close segmenter after segment has been confirmed.
        elif value == 0:
            self.segmenter.setVisible(False)
            if self.ax00.patches:
                self.ax00.patches[0].remove()
                self.canvas0.draw()
        # Close segmenter after segmentation has been aborted (reset
        # segment).
        elif value == 2:
            self._model.set_segment([0,
                                     0])  # This will reset the model to None
            self.segmenter.setVisible(False)
            if self.ax00.patches:
                self.ax00.patches[0].remove()
                self.canvas0.draw()

    def enable_segmentedit(self):
        # disable peak editing to avoid interference
        self.editcheckbox.setChecked(False)
        if self.startedit.hasFocus():
            self.segmentcursor = "start"
        elif self.endedit.hasFocus():
            self.segmentcursor = "end"

    def set_customheader(self):
        """Populate the customheader with inputs from the customfiledialog"""

        # Check if one of the mandatory fields is missing.
        mandatoryfields = self.signaledit.text() and self.headerrowsedit.text(
        ) and self.sfreqedit.text()

        if not mandatoryfields:
            self._model.status = (
                "Please provide values for 'biosignal column'"
                ", 'number of header rows' and 'sampling"
                " rate'.")
            return

        seps = {"comma": ",", "tab": "\t", "colon": ":", "space": " "}
        self._model.customheader = dict.fromkeys(
            self._model.customheader, None
        )  # reset header here since it cannot be reset in controller.get_fpaths()

        self._model.customheader["signalidx"] = int(self.signaledit.text())
        self._model.customheader["skiprows"] = int(self.headerrowsedit.text())
        self._model.customheader["sfreq"] = int(self.sfreqedit.text())
        self._model.customheader["separator"] = seps[
            self.separatormenu.currentText()]
        if self.markeredit.text():  # not mandatory
            self._model.customheader["markeridx"] = int(self.markeredit.text())

        self.customfiledialog.done(QDialog.Accepted)  # close the dialog window
        self._controller.get_fpaths()  # move on to file selection

    def get_xcursor(self, event):
        # event.button 1 corresponds to left mouse button
        if event.button != 1:
            return
        # limit number of decimal places to two
        if self.segmentcursor == "start":
            self.startedit.selectAll()
            self.startedit.insert("{:.2f}".format(event.xdata))
        elif self.segmentcursor == "end":
            self.endedit.selectAll()
            self.endedit.insert("{:.2f}".format(event.xdata))
        # disable segment cursor again after value has been set
        self.segmentcursor = False

    def select_stats(self, event):
        """
        select or deselect statistics to be saved; toggle boolean with xor
        operator ^=, toggle color with dictionary
        """
        self._model.savestats[event] ^= True
        line = None
        if event == "period":
            if self.line20:
                line = self.line20[0]
        elif event == "rate":
            if self.line21:
                line = self.line21[0]
        elif event == "tidalamp":
            if self.line22:
                line = self.line22[0]
        if line:
            line.set_color(self.togglecolors[line.get_color()])
        self.canvas2.draw()

    def toggle_options(self, event):
        if event in ["ECG", "PPG"]:
            self.tidalampcheckbox.setEnabled(False)
            self.tidalampcheckbox.setChecked(False)
            self.ax22.set_visible(False)
            self.canvas2.draw()
        elif event == "RESP":
            self.tidalampcheckbox.setEnabled(True)
            self.ax22.set_visible(True)
            self.canvas2.draw()
        elif event == "multiple files":
            self.editcheckbox.setEnabled(False)
            self.editcheckbox.setChecked(False)
            self.savecheckbox.setEnabled(True)
            self.correctcheckbox.setEnabled(True)
            self.markerchanmenu.setEnabled(False)
        elif event == "single file":
            self.editcheckbox.setEnabled(True)
            self.markerchanmenu.setEnabled(True)
            self.savecheckbox.setEnabled(False)
            self.savecheckbox.setChecked(False)
            self.correctcheckbox.setEnabled(False)
            self.correctcheckbox.setChecked(False)

    def reset_plot(self):
        self.ax00.clear()
        self.ax00.relim()
        self.line00 = None
        self.scat = None
        self.segmentspan = None
        self.ax10.clear()
        self.ax10.relim()
        self.line10 = None
        self.ax20.clear()
        self.ax20.relim()
        self.line20 = None
        self.ax21.clear()
        self.ax21.relim()
        self.line21 = None
        self.ax22.clear()
        self.ax22.relim()
        self.line22 = None
        self.canvas0.draw()
        self.canvas1.draw()
        self.canvas2.draw()
        self.navitools.update()
        self.currentFile.clear()
Пример #26
0
class AddProjectItemWidget(QWidget):
    """A widget to query user's preferences for a new item.

    Attributes:
        toolbox (ToolboxUI): Parent widget
        x (int): X coordinate of new item
        y (int): Y coordinate of new item
    """
    def __init__(self, toolbox, x, y, class_, spec=""):
        """Initialize class."""
        from ..ui.add_project_item import Ui_Form  # pylint: disable=import-outside-toplevel

        super().__init__(parent=toolbox,
                         f=Qt.Window)  # Setting parent inherits stylesheet
        self._toolbox = toolbox
        self._x = x
        self._y = y
        self._project = self._toolbox.project()
        #  Set up the user interface from Designer.
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        # Add status bar to form
        self.statusbar = QStatusBar(self)
        self.statusbar.setFixedHeight(20)
        self.statusbar.setSizeGripEnabled(False)
        self.statusbar.setStyleSheet(STATUSBAR_SS)
        self.ui.horizontalLayout_statusbar_placeholder.addWidget(
            self.statusbar)
        # Init
        if toolbox.supports_specifications(class_.item_type()):
            self.ui.comboBox_specification.setModel(
                toolbox.filtered_spec_factory_models[class_.item_type()])
            if spec:
                self.ui.comboBox_specification.setCurrentText(spec)
                prefix = spec
            else:
                prefix = class_.item_type()
                self.ui.comboBox_specification.setCurrentIndex(-1)
        else:
            prefix = class_.item_type()
            self.ui.comboBox_specification.setEnabled(False)
        self.name = toolbox.propose_item_name(prefix)
        self.ui.lineEdit_name.setText(self.name)
        self.ui.lineEdit_name.selectAll()
        self.description = ""
        self.connect_signals()
        self.ui.lineEdit_name.setFocus()
        # Ensure this window gets garbage-collected when closed
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowTitle(f"Add {class_.item_type()}")

    def connect_signals(self):
        """Connect signals to slots."""
        self.ui.lineEdit_name.textChanged.connect(
            self.handle_name_changed)  # Name -> folder name connection
        self.ui.pushButton_ok.clicked.connect(self.handle_ok_clicked)
        self.ui.pushButton_cancel.clicked.connect(self.close)

    @Slot()
    def handle_name_changed(self):
        """Update label to show upcoming folder name."""
        name = self.ui.lineEdit_name.text()
        default = "Folder:"
        if name == "":
            self.ui.label_folder.setText(default)
        else:
            folder_name = name.lower().replace(" ", "_")
            msg = default + " " + folder_name
            self.ui.label_folder.setText(msg)

    @Slot()
    def handle_ok_clicked(self):
        """Check that given item name is valid and add it to project."""
        self.name = self.ui.lineEdit_name.text()
        self.description = self.ui.lineEdit_description.text()
        if not self.name:  # No name given
            self.statusbar.showMessage("Name missing", 3000)
            return
        # Check for invalid characters for a folder name
        if any((True for x in self.name if x in INVALID_CHARS)):
            self.statusbar.showMessage("Name not valid for a folder name",
                                       3000)
            return
        # Check that name is not reserved
        if self._toolbox.project_item_model.find_item(self.name):
            msg = "Item '{0}' already exists".format(self.name)
            self.statusbar.showMessage(msg, 3000)
            return
        # Check that short name (folder) is not reserved
        short_name = self.name.lower().replace(" ", "_")
        if self._toolbox.project_item_model.short_name_reserved(short_name):
            msg = "Item using folder '{0}' already exists".format(short_name)
            self.statusbar.showMessage(msg, 3000)
            return
        # Create new Item
        self.call_add_item()
        self.close()

    def call_add_item(self):
        """Creates new Item according to user's selections.

        Must be reimplemented by subclasses.
        """
        raise NotImplementedError()

    def keyPressEvent(self, e):
        """Close Setup form when escape key is pressed.

        Args:
            e (QKeyEvent): Received key press event.
        """
        if e.key() == Qt.Key_Escape:
            self.close()
        elif e.key() == Qt.Key_Enter or e.key() == Qt.Key_Return:
            self.handle_ok_clicked()

    def closeEvent(self, event=None):
        """Handle close window.

        Args:
            event (QEvent): Closing event if 'X' is clicked.
        """
        if event:
            event.accept()
            scene = self._toolbox.ui.graphicsView.scene()
            item_shadow = scene.item_shadow
            if item_shadow:
                scene.removeItem(item_shadow)
                scene.item_shadow = None
Пример #27
0
class Chrono(QMainWindow):
    def __init__(self, parent=None):
        super(Chrono, self).__init__(parent)

        self.createMenus()
        self.createSystemTrayIcon()

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.tick)
        self.isRunning = False
        self.refresh_rate = 100  # ms

        self.progressBar = QProgressBar()
        self.progressBar.setValue(0)
        self.begin_time = self.end_time = 0

        self.label = QLabel(" ")
        self.button = QPushButton()
        self.button.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
        self.end_delay = self.begin_delay = 0

        bottomLayout = QHBoxLayout()
        bottomLayout.addWidget(self.progressBar)
        bottomLayout.addWidget(self.button)
        self.button.clicked.connect(self.pause)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.label)
        mainLayout.addLayout(bottomLayout)
        centralWidget = QWidget()
        centralWidget.setLayout(mainLayout)
        self.setCentralWidget(centralWidget)

        self.statusBar = QStatusBar(self)
        self.setStatusBar(self.statusBar)

        self.notification = self.notification_popup = self.notification_tray = self.notification_sound = True
        self.notification_soundfile = os.path.dirname(
            sys.argv[0]) + '/notification.mp3'  # os.path.dirname(__file__) +

        self.setWindowTitle(TITLE)
        self.resize(400, self.sizeHint().height())
        self.setFixedHeight(self.sizeHint().height())

    def createMenus(self):
        menus = QMenuBar()
        fileMenu = menus.addMenu("&Fichier")
        file_newMenu = fileMenu.addMenu(
            self.style().standardIcon(QStyle.SP_FileIcon), "Nouveau")
        file_newMenu.addAction("Date", self.createDateDialog, 'CTRL+D')
        file_newMenu.addAction("Durée", self.createDurationDialog, 'CTRL+N')
        fileMenu.addSeparator()
        fileMenu.addAction(self.style().standardIcon(QStyle.SP_BrowserStop),
                           "Quitter", sys.exit, 'CTRL+Q')

        optionMenu = menus.addMenu("&Options")
        optionMenu.addAction(
            self.style().standardIcon(QStyle.SP_MessageBoxInformation),
            "Évènements", self.createNotificationPopup, 'CTRL+O')
        optionMenu.addAction(
            QAction("Rester au premier plan",
                    optionMenu,
                    triggered=self.stayOnTop,
                    checkable=True))
        aideMenu = menus.addMenu("&Aide")
        aideMenu.addAction(
            self.style().standardIcon(QStyle.SP_DialogHelpButton),
            "À propos", lambda: QMessageBox.information(
                self, "À propos", TITLE + " " + str(VERSION)), 'CTRL+H')
        aideMenu.addSeparator()
        aideMenu.addAction(
            self.style().standardIcon(QStyle.SP_TitleBarMenuButton),
            "À propos de Qt", QApplication.aboutQt, 'CTRL+A')

        self.setMenuBar(menus)

    def createSystemTrayIcon(self):
        self.tray = QSystemTrayIcon()
        self.tray.setIcon(QIcon(os.path.dirname(sys.argv[0]) +
                                '/icon.svg'))  # os.path.dirname(__file__) +
        self.tray.setToolTip(TITLE)
        self.tray.show()

        systemTrayMenu = QMenu()
        pauseAction = QAction(self.style().standardIcon(QStyle.SP_MediaPause),
                              "Pause / Reprendre", systemTrayMenu)
        pauseAction.triggered.connect(self.pause)

        systemTrayMenu.addAction(pauseAction)

        systemTrayMenu.addSeparator()
        systemTrayMenu.addAction(
            self.style().standardIcon(QStyle.SP_BrowserStop), "Quitter",
            sys.exit)
        self.tray.setContextMenu(systemTrayMenu)
        self.tray.activated.connect(self.show)

    def stayOnTop(self):
        self.setWindowFlags(self.windowFlags() ^ Qt.WindowStaysOnTopHint)
        # self.windowFlags() | Qt.CustomizeWindowHint | Qt.Window | Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint)  # Qt.Dialog | Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint)
        self.show()

    def createNotificationPopup(self):
        popup = QDialog(self)
        popup.setFixedSize(popup.sizeHint().height(), popup.sizeHint().width())
        popup.setWindowTitle("Évènements")
        innerLayout = QVBoxLayout()

        GroupBox = QGroupBox("Activer les notifications")
        GroupBox.setCheckable(True)
        GroupBox.setChecked(self.notification)

        checkBox_popup = QCheckBox("Afficher une popup")
        checkBox_notification = QCheckBox("Afficher une notification")
        checkBox_sound = QCheckBox("Jouer un son")

        if self.notification_popup:
            checkBox_popup.setCheckState(Qt.Checked)
        if self.notification_tray:
            checkBox_notification.setCheckState(Qt.Checked)
        if self.notification_sound:
            checkBox_sound.setCheckState(Qt.Checked)

        innerLayout.addWidget(checkBox_popup)
        innerLayout.addWidget(checkBox_notification)
        innerLayout.addWidget(checkBox_sound)
        innerLayout.addStretch(1)
        GroupBox.setLayout(innerLayout)

        button = QPushButton("Ok")
        button.clicked.connect(lambda: self.changeNotificationSettings(
            popup, GroupBox, checkBox_popup, checkBox_notification,
            checkBox_sound))

        outerLayout = QVBoxLayout()
        outerLayout.addWidget(GroupBox)
        outerLayout.addWidget(button)
        popup.setLayout(outerLayout)

        popup.exec_()

    def changeNotificationSettings(self, popup, GroupBox, checkBox_popup,
                                   checkBox_notification, checkBox_sound):
        self.notification, self.notification_popup, self.notification_tray, self.notification_sound = GroupBox.isChecked(
        ), checkBox_popup.isChecked(), checkBox_notification.isChecked(
        ), checkBox_sound.isChecked()
        if not any([
                self.notification_popup, self.notification_tray,
                self.notification_sound
        ]):
            self.notification = False
        popup.close()

    def createDateDialog(self):
        popup = QDialog(self)
        popup.setFixedSize(270, 60)
        popup.setWindowTitle("Nouvelle date")
        layout = QHBoxLayout()

        prefix = QLabel("Heure cible: ")
        layout.addWidget(prefix)

        qline = QTimeEdit()
        qline.setDisplayFormat("hh:mm:ss")
        qline.setTime(QTime.currentTime())
        layout.addWidget(qline)

        button = QPushButton("Ok")
        button.clicked.connect(lambda: self.createDate(popup,
                                                       qline.time().hour(),
                                                       qline.time().minute(),
                                                       qline.time().second()))

        layout.addWidget(button)

        popup.setLayout(layout)
        popup.exec_()

    def createDurationDialog(self):
        popup = QDialog(self)
        popup.setFixedSize(150, 150)
        popup.setWindowTitle("Nouvelle durée")
        layout = QVBoxLayout()

        hourLayout = QHBoxLayout()
        hourLabel = QLabel("Heures:")
        hourSpin = QSpinBox()
        hourLayout.addWidget(hourLabel)
        hourLayout.addWidget(hourSpin)

        minuteLayout = QHBoxLayout()
        minuteLabel = QLabel("Minutes:")
        minuteSpin = QSpinBox()
        minuteLayout.addWidget(minuteLabel)
        minuteLayout.addWidget(minuteSpin)

        secondLayout = QHBoxLayout()
        secondLabel = QLabel("Secondes:")
        secondSpin = QSpinBox()
        secondLayout.addWidget(secondLabel)
        secondLayout.addWidget(secondSpin)

        layout.addLayout(hourLayout)
        layout.addLayout(minuteLayout)
        layout.addLayout(secondLayout)

        button = QPushButton("Ok")
        button.clicked.connect(lambda: self.createDuration(
            popup, hourSpin.value(), minuteSpin.value(), secondSpin.value()))
        layout.addWidget(button)

        popup.setLayout(layout)
        popup.exec_()

    def createDuration(self, popup: QDialog, hours: int, minutes: int,
                       seconds: int):
        popup.close()

        self.begin_time = datetime.timestamp(datetime.now())
        self.end_time = self.begin_time + seconds + minutes * 60 + hours * 3600
        self.progressBar.setRange(0, 100)
        self.progressBar.setValue(0)

        self.isRunning = True
        self.timer.stop()
        self.timer.start(self.refresh_rate)

    def createDate(self, popup: QDialog, hours: int, minutes: int,
                   seconds: int):
        popup.close()

        self.begin_time = datetime.timestamp(datetime.now())

        now = datetime.now().time()
        target = time(hours, minutes, seconds)
        now_delta = timedelta(hours=now.hour,
                              minutes=now.minute,
                              seconds=now.second)
        target_delta = timedelta(hours=target.hour,
                                 minutes=target.minute,
                                 seconds=target.second)

        if target_delta == now_delta:
            self.end_time = self.begin_time + 60 * 60 * 24
        else:
            d = target_delta - now_delta
            self.end_time = self.begin_time + d.seconds

        self.progressBar.setRange(0, 100)
        self.progressBar.setValue(0)

        self.isRunning = True
        self.timer.stop()
        self.timer.start(self.refresh_rate)

    def tick(self):
        self.progressBar.setValue(
            100 * (datetime.timestamp(datetime.now()) - self.begin_time) /
            (self.end_time - self.begin_time))

        seconds = int(
            ceil(self.end_time - datetime.timestamp(datetime.now())) % 60)
        minutes = int(
            ceil(self.end_time - datetime.timestamp(datetime.now())) / 60 % 60)
        hours = int(
            ceil(self.end_time - datetime.timestamp(datetime.now())) / 3600)

        self.label.setText(f'{hours:02}:{minutes:02}:{seconds:02}')
        self.setWindowTitle(f'{TITLE} - {hours:02}:{minutes:02}:{seconds:02}')
        self.tray.setToolTip(f'{hours:02}:{minutes:02}:{seconds:02}')

        if datetime.timestamp(datetime.now()) >= self.end_time:
            self.isRunning = False
            self.timer.stop()
            self.progressBar.setRange(0, 0)
            self.show()
            self.notify()

    def notify(self):
        if not self.notification:
            return
        if self.notification_tray:
            self.tray.showMessage(
                "Finished", "Le décompte est terminé",
                self.style().standardIcon(QStyle.SP_MessageBoxInformation))
        if self.notification_sound:
            test = QMediaPlayer()
            test.setMedia(QUrl.fromLocalFile(self.notification_soundfile))
            test.play()
        if self.notification_popup:
            QMessageBox.information(self, "Finished",
                                    "Le décompte est terminé")

    def pause(self):
        if not self.isRunning:
            return
        self.progressBar.setDisabled(self.timer.isActive())
        if self.timer.isActive():
            self.end_delay = self.end_time - datetime.timestamp(datetime.now())
            self.begin_delay = datetime.timestamp(
                datetime.now()) - self.begin_time
            print(self.begin_time)
            print(self.end_time)
            print(self.end_delay)
            self.statusBar.showMessage("Pause")
            self.tray.setToolTip(self.tray.toolTip() + ' - Pause')
            self.timer.stop()
            self.button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        else:
            self.begin_time = datetime.timestamp(
                datetime.now()) - self.begin_delay
            self.end_time = datetime.timestamp(datetime.now()) + self.end_delay
            print(self.begin_time)
            print(self.end_time)
            self.statusBar.clearMessage()
            self.timer.start()
            self.button.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))

    # Override
    def closeEvent(self, event):
        self.hide()
        event.ignore()
Пример #28
0
class Form(QMainWindow):
    def __init__(self, parent=None):
        super(Form, self).__init__(parent)
        self.setWindowTitle("Style Sheet Example")

        self.add_status_bar()

        # ADD TAB BAR
        self.tabs = QTabWidget()
        self.setCentralWidget(self.tabs)

        self.add_tab_1()
        self.tabs.addTab(QWidget(), 'Tab No.2')
        self.tabs.addTab(QWidget(), 'Tab No.3')

        # DISABLED
        self.tabs.addTab(QWidget(), 'Tab Disabled')
        self.tabs.setTabEnabled(3, False)

    def add_tab_1(self):
        t1 = QWidget()
        layout = QVBoxLayout(t1)

        self.tabs.addTab(t1, "Tab No.1")

        self.add_vertical_splitter(layout)
        self.add_horizontal_splitter(layout)

    def add_status_bar(self):
        self.myStatus = QStatusBar()
        self.myStatus.showMessage("This is a status bar message...")
        self.setStatusBar(self.myStatus)

    def add_vertical_splitter(self, layout):
        splitter = QSplitter()
        layout.addWidget(splitter)

        # ADD LEFT
        left_widget = QWidget(splitter)
        left = QVBoxLayout(left_widget)

        # NORMAL
        let = QLineEdit('Line Edit With Text')
        left.layout().addWidget(let)

        #PLACEHOLDER
        let = QLineEdit()
        let.setPlaceholderText('This is placeholder text...')
        left.layout().addWidget(let)

        # DISABLED
        let = QLineEdit('Line Edit Disabled')
        let.setEnabled(False)
        left.layout().addWidget(let)

        # ADD RIGHT
        right_widget = QWidget(splitter)
        right = QGridLayout(right_widget)

        # GROUP BOX
        grp = QGroupBox("Group Box")
        right.layout().addWidget(grp)

        grp_layout = QGridLayout(grp)

        btn = QPushButton('Set Style')
        btn.pressed.connect(self.set_style)
        grp_layout.addWidget(btn)

    def add_horizontal_splitter(self, layout):
        splitter = QSplitter()
        splitter.setOrientation(Qt.Orientation.Vertical)
        layout.addWidget(splitter)

        # ADD TOP
        top_widget = QWidget(splitter)
        top = QVBoxLayout(top_widget)

        # NORMAL
        btn = FillPushButton('Push Button')
        top.layout().addWidget(btn)

        #DISABLED
        btn = FillPushButton('Push Button Disabled')
        btn.setEnabled(False)
        top.layout().addWidget(btn)

        # ADD BOTTOM
        bottom_widget = QWidget(splitter)
        right = QGridLayout(bottom_widget)

        # NORMAL
        cbx = QCheckBox('Check Box')
        right.addWidget(cbx)

        #DISABLED
        cbx = QCheckBox('Check Box Disabled')
        cbx.setEnabled(False)
        right.addWidget(cbx)

        rob = QRadioButton('Radio Button')
        right.addWidget(rob)

        rob = QRadioButton('Radio Button Disabled')
        rob.setEnabled(False)
        right.addWidget(rob)

    def set_style(self):
        stylesheet_path = os.path.join(os.path.dirname(__file__),
                                       'style_dark.qss')
        with open(stylesheet_path, 'r') as f:
            self.setStyleSheet(f.read())
Пример #29
0
class AddDataConnectionWidget(QWidget):
    """A widget that queries user's preferences for a new item.

    Attributes:
        toolbox (ToolboxUI): toolbox widget
        x (int): X coordinate of new item
        y (int): Y coordinate of new item
    """
    def __init__(self, toolbox, x, y):
        """Initialize class."""
        super().__init__(
            parent=toolbox,
            f=Qt.Window)  # Setting the parent inherits the stylesheet
        self._toolbox = toolbox
        self._x = x
        self._y = y
        self._project = self._toolbox.project()
        #  Set up the user interface from Designer.
        self.ui = ui.add_data_connection.Ui_Form()
        self.ui.setupUi(self)
        # Add status bar to form
        self.statusbar = QStatusBar(self)
        self.statusbar.setFixedHeight(20)
        self.statusbar.setSizeGripEnabled(False)
        self.statusbar.setStyleSheet(STATUSBAR_SS)
        self.ui.horizontalLayout_statusbar_placeholder.addWidget(
            self.statusbar)
        # Class attributes
        self.name = ''
        self.description = ''
        self.connect_signals()
        self.ui.lineEdit_name.setFocus()
        # Ensure this window gets garbage-collected when closed
        self.setAttribute(Qt.WA_DeleteOnClose)

    def connect_signals(self):
        """Connect signals to slots."""
        self.ui.lineEdit_name.textChanged.connect(
            self.name_changed)  # Name -> folder name connection
        self.ui.pushButton_ok.clicked.connect(self.ok_clicked)
        self.ui.pushButton_cancel.clicked.connect(self.close)

    @Slot(name='name_changed')
    def name_changed(self):
        """Update label to show upcoming folder name."""
        name = self.ui.lineEdit_name.text()
        default = "Folder:"
        if name == '':
            self.ui.label_folder.setText(default)
        else:
            folder_name = name.lower().replace(' ', '_')
            msg = default + " " + folder_name
            self.ui.label_folder.setText(msg)

    @Slot(name='ok_clicked')
    def ok_clicked(self):
        """Check that given item name is valid and add it to project."""
        self.name = self.ui.lineEdit_name.text()
        self.description = self.ui.lineEdit_description.text()
        if not self.name:  # No name given
            self.statusbar.showMessage("Name missing", 3000)
            return
        # Check for invalid characters for a folder name
        if any((True for x in self.name if x in INVALID_CHARS)):
            self.statusbar.showMessage("Name not valid for a folder name",
                                       3000)
            return
        # Check that name is not reserved
        if self._toolbox.project_item_model.find_item(self.name):
            msg = "Item '{0}' already exists".format(self.name)
            self.statusbar.showMessage(msg, 3000)
            return
        # Check that short name (folder) is not reserved
        short_name = self.name.lower().replace(' ', '_')
        if self._toolbox.project_item_model.short_name_reserved(short_name):
            msg = "Item using folder '{0}' already exists".format(short_name)
            self.statusbar.showMessage(msg, 3000)
            return
        # Create new Item
        self.call_add_item()
        self.close()

    def call_add_item(self):
        """Creates new Item according to user's selections."""
        self._project.add_data_connection(self.name,
                                          self.description,
                                          list(),
                                          self._x,
                                          self._y,
                                          set_selected=True)

    def keyPressEvent(self, e):
        """Close Setup form when escape key is pressed.

        Args:
            e (QKeyEvent): Received key press event.
        """
        if e.key() == Qt.Key_Escape:
            self.close()
        elif e.key() == Qt.Key_Enter or e.key() == Qt.Key_Return:
            self.ok_clicked()

    def closeEvent(self, event=None):
        """Handle close window.

        Args:
            event (QEvent): Closing event if 'X' is clicked.
        """
        if event:
            event.accept()
            scene = self._toolbox.ui.graphicsView.scene()
            item_shadow = scene.item_shadow
            if item_shadow:
                scene.removeItem(item_shadow)
                scene.item_shadow = None
Пример #30
0
class ViewMainFrame(QMainWindow):
    def __init__(self, sig_quit: Signal, sig_config_mode_changed: Signal,
                 sig_export_csv: Signal):
        """
        Main application's frame

        :param sig_quit: signal to trigger when the application closes
        :param sig_config_mode_changed: signal to trigger when the configuration mode changes
        :param sig_export_csv: signal to emit with the filepath to perform the export to CSV
        """
        QMainWindow.__init__(self)

        self.setWindowTitle(
            f"{tr('app_title')} | {AssetManager.getInstance().config('main', 'version')}"
        )
        self.setContextMenuPolicy(Qt.PreventContextMenu)

        self.bdd_version: str = ""  # For the about box

        self.reboot_requested = False

        # Widgets
        self.status_bar = QStatusBar()
        self.status_bar.setStyleSheet(
            "QStatusBar {background: lightgrey; color: black;}")
        self.maintoolbar = ViewMainToolBar()
        self.sidewidget = SideDockWidget()
        self.central_widget = CentralWidget(self.status_bar.showMessage,
                                            self.__active_tab_changed)

        self.sidewidget.dockLocationChanged.connect(
            self.on_side_widget_docked_state_changed)

        self.__config_mode = False  # Config mode flag, should be initialized to False in all widgets
        self.__init_callbacks()

        # Layout
        self.setCentralWidget(self.central_widget)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.sidewidget)
        self.addToolBar(Qt.RightToolBarArea, self.maintoolbar)
        self.setStatusBar(self.status_bar)

        # Signals
        self.sig_quit = sig_quit
        self.sig_config_mode_changed = sig_config_mode_changed
        self.sig_export_csv = sig_export_csv

        self.setStyleSheet(
            "QMainWindow {" +
            f"background-color: {AssetManager.getInstance().config('colors', 'main_bg')};"
            + "}")

    def restart(self):
        return QtCore.QCoreApplication.exit(EXIT_CODE_REBOOT)

    def set_bdd_version(self, bdd_version: str) -> None:
        """
        Sets the current BDD version. Used in the About box
        """
        self.bdd_version = bdd_version

    def __init_callbacks(self):
        """
        Dispatches the callbacks from the toolbar to the handling widgets. Also init the signal triggered to enable
        or disable some buttons.
        """
        self.maintoolbar.on_btn_perspective_clicked = self.central_widget.on_perspective_changed
        self.maintoolbar.on_btn_shuffle_clicked = self.central_widget.do_shuffle
        self.maintoolbar.on_config_mode = self.on_config_mode
        self.maintoolbar.on_export_csv = self.on_export_csv
        self.maintoolbar.on_edit_config = self.on_edit_config
        self.maintoolbar.open_about_box = lambda: AboutFrame(self.bdd_version
                                                             ).exec_()

        self.central_widget.sig_enable_animation_btns = self.maintoolbar.sig_enable_animation_btns

        self.sidewidget.attributes(
        ).attributes_selection_changed = self.maintoolbar.enable_one_attributes_buttons

    def __active_tab_changed(self, is_view_classroom: bool) -> None:
        """
        Triggered when the active tab changed.
        Updates main toolbar widgets

        :param is_view_classroom: View classroom or view table attributes
        """
        self.maintoolbar.set_widgets(is_view_classroom)

        # Manually update buttons enable state given the number of selected attributes
        self.maintoolbar.enable_one_attributes_buttons(
            self.sidewidget.attributes().get_selected_rows_count() == 1)

        self.sidewidget.sidepanel.tabBar().setTabEnabled(
            0, is_view_classroom)  # Courses
        self.sidewidget.sidepanel.tabBar().setTabEnabled(
            1, is_view_classroom)  # Students

    def on_config_mode(self, is_in_config_mode: bool) -> None:
        """
        Switches the current mode. If in config mode, almost all features are made available, whereas in 'secured'
        mode, some features will be inaccessible to prevent the user from doing critic modification actions.
        """
        self.__config_mode = is_in_config_mode

        self.maintoolbar.lock_buttons(self.__config_mode)
        self.central_widget.classroom_tab.v_canvas.config_mode(
            self.__config_mode)
        self.sidewidget.courses().courses_toolbar.setVisible(
            self.__config_mode)
        self.sidewidget.attributes().attributes_toolbar.setVisible(
            self.__config_mode)
        self.sidewidget.students().students_toolbar.switch_config_mode(
            self.__config_mode)

        self.sig_config_mode_changed.emit()

    def get_config(self) -> bool:
        """
        Getter for the config mode

        :return: True if the user is in Configuration mode
        """
        return self.__config_mode

    def on_side_widget_docked_state_changed(self) -> None:
        """
        Triggered when the side dockable widget has been docked or undocked
        """
        if self.sidewidget.isFloating():
            self.adjustSize()

    def on_export_csv(self) -> None:
        """
        Displays a save dialog to select a file path for the export csv of the attributes table.
        Then signals this path to the controller
        """
        file_path = QFileDialog.getSaveFileName(self,
                                                tr("export_dialog_title"),
                                                "untitled", "(*.csv)")[0]
        if file_path:
            self.sig_export_csv.emit(file_path)

    def on_edit_config(self) -> None:
        """
        Displays the edition dialog for application settings
        """
        dlg = SettingsEditionDialog()

        if dlg.exec_():
            if dlg.restore_default():
                AssetManager.getInstance().restore_default_settings()
            else:
                AssetManager.getInstance().save_config(dlg.new_config())

            self.status_bar.showMessage(tr("acknowledge_changes"), 3000)
            self.repaint()

            if dlg.need_restart():
                restart_confirm = VConfirmDialog(self, "need_restart")
                restart_confirm.ok_btn.setText(tr("restart_now"))
                restart_confirm.ok_btn.setFixedSize(QSize(105, 33))
                restart_confirm.cancel_btn.setText(tr("restart_later"))
                restart_confirm.cancel_btn.setFixedSize(QSize(105, 33))

                if restart_confirm.exec_():
                    self.reboot_requested = True
                    self.close()
                    self.restart()

    def closeEvent(self, event):
        """
        Triggered on a close operation. Signals to the controller the event
        """
        if self.reboot_requested:
            self.reboot_requested = False
            self.sig_quit.emit(EXIT_CODE_REBOOT)
        else:
            self.sig_quit.emit(0)
        event.accept()