Esempio n. 1
0
    def __init__(self, parent):
        super(EditSourceCodeDialog, self).__init__(parent)

        # create UI
        self.main_grid_layout = QGridLayout(self)

        main_vertical_splitter = QSplitter()
        main_vertical_splitter.setOrientation(Qt.Vertical)

        #   instructions and actions layout
        header_widget = QWidget()
        header_layout = QHBoxLayout()

        instructions_text_edit = QTextEdit()
        instructions_text_edit.setText(instructions_text)
        instructions_text_edit.setEnabled(False)

        reset_button = QPushButton()
        reset_button.setText('reset')
        reset_button.clicked.connect(self.reset)

        header_layout.addWidget(instructions_text_edit)
        header_layout.addWidget(reset_button)

        header_widget.setLayout(header_layout)


        main_vertical_splitter.addWidget(header_widget)

        #   code text edit
        self.code_text_edit = CodeEditor()
        self.code_text_edit.static_elements = ['def update(self, input_called=-1, token=None):',
                                               'self.data_outputs_updated()',
                                               'def get_data(self):',
                                               'def set_data(self, data):',
                                               '''	def __init__(self, parent_node: Node, flow, configuration=None):
		super(%NODE_TITLE%_NodeInstance, self).__init__(parent_node, flow, configuration)''',
                                               'class %NODE_TITLE%_NodeInstance']
        self.code_text_edit.components = ['self.main_widget',
                                          'self.outputs',
                                          'self.input',
                                          'self.special_actions']
        main_vertical_splitter.addWidget(self.code_text_edit)



        self.main_grid_layout.addWidget(main_vertical_splitter)


        button_box = QDialogButtonBox()
        button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        self.main_grid_layout.addWidget(button_box)

        self.setWindowTitle('Edit Source Code')
        self.resize(1300, 950)


        self.reset()
Esempio n. 2
0
 def setup_ui(self):
     vertical_layout = QVBoxLayout(self)
     vertical_layout.setSpacing(6)
     vertical_layout.setContentsMargins(11, 11, 11, 11)
     splitter_v = QSplitter(self)
     splitter_v.setOrientation(PySide2.QtCore.Qt.Vertical)
     size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
     size_policy.setHorizontalStretch(0)
     size_policy.setVerticalStretch(0)
     size_policy.setHeightForWidth(
         splitter_v.sizePolicy().hasHeightForWidth())
     splitter_v.setSizePolicy(size_policy)
     splitter_h = QSplitter(splitter_v)
     splitter_h.setOrientation(PySide2.QtCore.Qt.Horizontal)
     vertical_layout_widget = QWidget(splitter_h)
     self.vertical_layout_left = QVBoxLayout(vertical_layout_widget)
     self.vertical_layout_left.setSpacing(6)
     self.vertical_layout_left.setSizeConstraint(
         QLayout.SetDefaultConstraint)
     self.vertical_layout_left.setContentsMargins(0, 0, 0, 0)
     splitter_h.addWidget(vertical_layout_widget)
     vertical_layout_widget2 = QWidget(splitter_h)
     self.vertical_layout_right = QVBoxLayout(vertical_layout_widget2)
     self.vertical_layout_right.setContentsMargins(0, 0, 0, 0)
     splitter_h.addWidget(vertical_layout_widget2)
     splitter_v.addWidget(splitter_h)
     vertical_layout_widget3 = QWidget(splitter_v)
     vertical_layout_bottom = QVBoxLayout(vertical_layout_widget3)
     vertical_layout_bottom.setSpacing(6)
     vertical_layout_bottom.setSizeConstraint(QLayout.SetDefaultConstraint)
     vertical_layout_bottom.setContentsMargins(11, 0, 11, 11)
     text_browser = QTextBrowser(vertical_layout_widget3)
     vertical_layout_bottom.addWidget(text_browser)
     splitter_v.addWidget(vertical_layout_widget3)
     vertical_layout.addWidget(splitter_v)
Esempio n. 3
0
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.scene = None
        self.populateScene()

        self.h1Splitter = QSplitter()
        self.h2Splitter = QSplitter()

        vSplitter = QSplitter()
        vSplitter.setOrientation(Qt.Vertical)
        vSplitter.addWidget(self.h1Splitter)
        vSplitter.addWidget(self.h2Splitter)

        view = View("Top left view", self)
        view.view().setScene(self.scene)
        self.h1Splitter.addWidget(view)

        view = View("Top right view", self)
        view.view().setScene(self.scene)
        self.h1Splitter.addWidget(view)

        view = View("Bottom left view", self)
        view.view().setScene(self.scene)
        self.h2Splitter.addWidget(view)

        view = View("Bottom right view", self)
        view.view().setScene(self.scene)
        self.h2Splitter.addWidget(view)

        layout = QHBoxLayout()
        layout.addWidget(vSplitter)
        self.setLayout(layout)

        self.setWindowTitle(self.tr("Chip Example"))
Esempio n. 4
0
    def __init__(self, model):
        super(_ControlTab, self).__init__()

        self._model = model

        layout = QGridLayout()
        splitter = QSplitter()

        self._bot_table = _BotTable()
        self._responses_tab = _ResponsesTab()
        self._execute_tab = _ExecuteTab(self._responses_tab, model)

        self._tab_widget = QTabWidget()
        self._tab_widget.addTab(self._execute_tab, "Execute")
        self._tab_widget.addTab(self._responses_tab, "Responses")

        splitter.setOrientation(Qt.Vertical)
        splitter.addWidget(self._bot_table)
        splitter.addWidget(self._tab_widget)
        splitter.setSizes([50, 100])

        layout.addWidget(splitter)
        self.setLayout(layout)

        self._register_listeners()
    def _init_widgets(self):

        # Subclass QPlainTextEdit
        class SmartPlainTextEdit(QPlainTextEdit):
            def __init__(self, parent, callback):
                super(SmartPlainTextEdit, self).__init__(parent)
                self._callback = callback

            def keyPressEvent(self, event):
                super(SmartPlainTextEdit, self).keyPressEvent(event)
                if event.key() == Qt.Key_Return:
                    if not (event.modifiers() == Qt.ShiftModifier):
                        self._callback()

        # Gui stuff
        splitter = QSplitter(self)

        output_wid = QWidget(splitter)
        output_wid.setLayout(QVBoxLayout(output_wid))
        self._history_text = QTextEdit(output_wid)
        self._history_text.setCurrentFont(QFont('Times', 10))
        self._history_text.setFontFamily('Source Code Pro')
        output_wid.layout().addWidget(QLabel("History"))
        output_wid.layout().addWidget(self._history_text)

        input_wid = QWidget(splitter)
        input_wid.setLayout(QVBoxLayout(input_wid))
        self._command = SmartPlainTextEdit(input_wid, self._send_command)
        input_wid.layout().addWidget(QLabel("Command"))
        input_wid.layout().addWidget(
            QLabel(
                "Press Enter to send the command. Press Shift + Enter to add a newline."
            ))
        input_wid.layout().addWidget(self._command)

        splitter.setOrientation(Qt.Vertical)
        splitter.addWidget(output_wid)
        splitter.addWidget(input_wid)
        splitter.setSizes([300, 100])

        send_button = QPushButton(self, text="Send")
        clear_history_button = QPushButton(self, text="Clear History")
        interact_button = QPushButton(self, text="Interact")
        send_button.clicked.connect(self._send_command)
        clear_history_button.clicked.connect(self._history_text.clear)
        interact_button.clicked.connect(
            lambda: self.initialize(self.workspace.instance.img_name))
        buttons = QSplitter(self)
        buttons.addWidget(interact_button)
        buttons.addWidget(clear_history_button)
        buttons.addWidget(send_button)
        buttons.setSizes([100, 200, 600])

        self.setLayout(QVBoxLayout(self))
        self.layout().addWidget(splitter)
        self.layout().addWidget(buttons)
Esempio n. 6
0
    def __init__(self, parent):
        super(EditInputWidgetDialog, self).__init__(parent)

        # create UI
        self.main_grid_layout = QGridLayout(self)

        main_vertical_splitter = QSplitter()
        main_vertical_splitter.setOrientation(Qt.Vertical)

        #   instructions and actions layout
        header_widget = QWidget()
        header_layout = QHBoxLayout()

        instructions_text_edit = QTextEdit()
        instructions_text_edit.setText(instructions_text)
        instructions_text_edit.setEnabled(False)

        reset_button = QPushButton()
        reset_button.setText('reset')
        reset_button.clicked.connect(self.reset)

        header_layout.addWidget(instructions_text_edit)
        header_layout.addWidget(reset_button)

        header_widget.setLayout(header_layout)

        main_vertical_splitter.addWidget(header_widget)

        #   code text edit
        self.code_text_edit = CodeEditor()
        self.code_text_edit.static_elements = [
            'def get_data(self):', 'def set_data(self, data):'
        ]
        self.code_text_edit.components = [
            'self.parent_node_instance',
            'self.parent_node_instance.update_shape()',
            'self.parent_port_instance'
        ]
        main_vertical_splitter.addWidget(self.code_text_edit)

        self.main_grid_layout.addWidget(main_vertical_splitter)

        button_box = QDialogButtonBox()
        button_box.setStandardButtons(QDialogButtonBox.Cancel
                                      | QDialogButtonBox.Ok)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        self.main_grid_layout.addWidget(button_box)

        self.setWindowTitle('Edit Source Code')
        self.resize(1300, 950)

        self.reset()
Esempio n. 7
0
    def home(self):
        """
        Add the GUI elements to the window that represent the home state of the application.
        """
        toolbar = self.addToolBar("File")
        save = QAction(QIcon("res/icon_save.png"), "Save", self)
        save.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_S))
        toolbar.addAction(save)
        load = QAction(QIcon("res/icon_load.png"), "Load", self)
        load.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_O))
        toolbar.addAction(load)
        toolbar.addSeparator()
        undo = QAction(QIcon("res/icon_undo.png"), "Undo", self)
        undo.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z))
        toolbar.addAction(undo)
        redo = QAction(QIcon("res/icon_redo.png"), "Redo", self)
        redo.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y))
        toolbar.addAction(redo)
        toolbar.addSeparator()
        zoom_in = QAction(QIcon("res/icon_zoom_in.png"), "Zoom In", self)
        toolbar.addAction(zoom_in)
        zoom_out = QAction(QIcon("res/icon_zoom_out.png"), "Zoom Out", self)
        toolbar.addAction(zoom_out)
        toolbar.addSeparator()
        clear = QAction(QIcon("res/icon_clear.png"), "Clear", self)
        toolbar.addAction(clear)
        toolbar.addSeparator()
        grid = QAction(QIcon("res/icon_grid.png"), "Grid", self)
        toolbar.addAction(grid)
        toolbar.actionTriggered[QAction].connect(self.toolbar_pressed)

        splitter = QSplitter(self)
        splitter.setOrientation(Qt.Vertical)
        splitter.setHandleWidth(16)

        self.tile_ed = TileEd(self.size, self.tile_size, self.pixmap, self)
        scroll_area_tile_ed = QScrollArea()
        scroll_area_tile_ed.setBackgroundRole(QPalette.Dark)
        scroll_area_tile_ed.setWidgetResizable(True)
        scroll_area_tile_ed.setWidget(self.tile_ed)

        splitter.addWidget(scroll_area_tile_ed)

        self.tile_sel = TileSelector(self.tile_size, self.pixmap, self)
        scroll_area_tile_sel = QScrollArea()
        scroll_area_tile_sel.setBackgroundRole(QPalette.Dark)
        scroll_area_tile_sel.setWidgetResizable(True)
        scroll_area_tile_sel.setWidget(self.tile_sel)

        splitter.addWidget(scroll_area_tile_sel)
        self.setCentralWidget(splitter)
Esempio n. 8
0
    def get_menu(self):
        scroll = QScrollArea(self)
        self.forum_view.setStyleSheet("QListView{font: bold 12px;}")
        self.forum_view.clicked.connect(self.listViewClick)
        self.forum_view.setModel(self.forum_model)

        self.date_view.setStyleSheet("QListView{font: bold 12px;}")
        self.date_view.clicked.connect(self.listViewClick)
        self.date_view.setModel(self.day_model)

        menu_splitter = QSplitter(self)
        menu_splitter.setOrientation(Qt.Vertical)
        menu_splitter.addWidget(self.forum_view)
        menu_splitter.addWidget(self.date_view)
        scroll.setWidget(menu_splitter)
        scroll.setWidgetResizable(True)
        self.splitter.addWidget(scroll)
Esempio n. 9
0
    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)

        for i in range(3):
            btn = FillPushButton(f'top_{i}')
            top.layout().addWidget(btn)

        # ADD BOTTOM
        bottom_widget = QWidget(splitter)
        right = QGridLayout(bottom_widget)
        btn = FillPushButton('bottom')
        right.addWidget(btn)
    def _setup(self):
        self.serial_input = SerialInputGui()
        self.serial_device = SerialDevicesGui()
        self.serial_monitor = SerialMonitorGui(self)

        hsplitter = QSplitter()
        hsplitter.setRubberBand(-1)
        hsplitter.setHandleWidth(10)
        hsplitter.setOrientation(Qt.Horizontal)
        hsplitter.addWidget(self.serial_monitor)
        hsplitter.addWidget(self.serial_device)

        vsplitter = QSplitter()
        vsplitter.setRubberBand(-1)
        vsplitter.setHandleWidth(10)
        vsplitter.setOrientation(Qt.Vertical)
        vsplitter.addWidget(hsplitter)
        vsplitter.addWidget(self.serial_input)

        self.layout.addWidget(vsplitter, 0, 0, 1, 1)
Esempio n. 11
0
class FE14ChapterConfigTab(QScrollArea):
    def __init__(self):
        super().__init__()
        self.chapter_data = None
        module_service = locator.get_scoped("ModuleService")
        config_module = module_service.get_common_module_template("Map Config")
        self.module = module_service.get_module("Chapters")
        self.text_data_widget = FE14ChapterTextDataWidget()
        self.header_scroll, self.header_property_form = PropertyForm.create_with_scroll(
            self.module.element_template)
        self.config_scroll, self.config_property_form = PropertyForm.create_with_scroll(
            config_module.element_template)
        self.header_property_form.editors["CID"].setEnabled(False)
        self.header_property_form.editors["Key (CID)"].setEnabled(False)
        self.vertical_layout = QVBoxLayout(parent=self)
        self.splitter = QSplitter(parent=self)
        self.splitter.setOrientation(QtCore.Qt.Vertical)
        self.splitter2 = QSplitter(parent=self)
        self.splitter2.setOrientation(QtCore.Qt.Horizontal)
        self.splitter.addWidget(self.text_data_widget)
        self.splitter.addWidget(self.splitter2)
        self.splitter2.addWidget(self.header_scroll)
        self.splitter2.addWidget(self.config_scroll)
        self.vertical_layout.addWidget(self.splitter)
        self.scroll_content = QWidget()
        self.scroll_content.setLayout(self.vertical_layout)
        self.setWidget(self.scroll_content)
        self.setWidgetResizable(True)

    def update_chapter_data(self, chapter_data):
        self.chapter_data = chapter_data
        chapter_header = chapter_data.chapter if chapter_data else None
        config = chapter_data.config.element if chapter_data else None
        self.text_data_widget.update_chapter_data(chapter_data)
        self.header_property_form.update_target(chapter_header)
        self.config_property_form.update_target(config)
        self.header_scroll.setEnabled(chapter_header is not None)
        self.config_scroll.setEnabled(config is not None)
Esempio n. 12
0
    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)
Esempio n. 13
0
    def _initCentralWidget(self):
        centralView = QWidget()
        centralView.setLayout(QHBoxLayout())
        centralView.layout().setSpacing(0)
        centralView.layout().setContentsMargins(0, 0, 0, 0)

        self.browser = Browser(self.document, self.subject, self.powermode)

        centralView.layout().addWidget(self.browser)

        canvas = QSplitter()
        canvas.setFrameStyle(QFrame.NoFrame | QFrame.Plain)
        canvas.setOrientation(Qt.Vertical)
        canvas.setRubberBand(-1)
        self.editor = Editor(self.subject, self.powermode)
        self.console = Console(self.subject)

        canvas.addWidget(self.editor)
        canvas.addWidget(self.console)

        centralView.layout().addWidget(canvas)
        canvas.setSizes([4, 4, 1, 1])

        self.setCentralWidget(centralView)
Esempio n. 14
0
# ---------------------------
# SplitterでTextEditを垂直に2つ表示する
# ---------------------------
import sys
from PySide2.QtWidgets import QApplication, QSplitter, QTextEdit
from PySide2.QtCore import Qt

app = QApplication(sys.argv)

qw_text_edit_top = QTextEdit()
qw_text_edit_top.append('top')

qw_text_edit_bottom = QTextEdit()
qw_text_edit_bottom.append('bottom')

qw_splitter = QSplitter()
qw_splitter.setOrientation(Qt.Orientation.Vertical)  # Splitterを垂直に設定
print(qw_splitter.orientation())
qw_splitter.addWidget(qw_text_edit_top)
qw_splitter.addWidget(qw_text_edit_bottom)

qw_splitter.show()

sys.exit(app.exec_())
Esempio n. 15
0
class main(QWidget):
    def __init__(self, parent=None):
        super(main, self).__init__(parent)
        self.setup()  # connections, widgets, layouts, etc.

        self.blksize = 2**20  # 1 MB; must be divisible by 16
        self.ext = '.enc'  # extension is appended to encrypted files
        self.path = ''
        self.encrypted = []  # to highlight done files in list
        self.decrypted = []

        self.clipboard = QApplication.clipboard()
        self.timeout = None  # to clear message label, see setMessage

        # this program was just an excuse to play with QprogressBar
        if not hash(os.urandom(11)) % 11:
            QTimer().singleShot(50, self.windDown)

        # various random hints
        hints = [
            'Freshly encrypted files can be renamed in the table!',
            'Clipboard is always cleared on program close!',
            'Keys can contain emoji if you <em>really</em> want: \U0001f4e6',
            'Keys can contain emoji if you <em>really</em> want: \U0001F511',
            'This isn\'t a tip, I just wanted to say hello!',
            'Keys can be anywhere from 8 to 4096 characters long!',
            'This program was just an excuse to play with the progress bars!',
            'Select \'Party\' in the hash button for progress bar fun!',
            ('Did you know you can donate one or all of your vital organs to '
             'the Aperture Science Self-Esteem Fund for Girls? It\'s true!'),
            ('It\'s been {:,} days since Half-Life 2: Episode '
             'Two'.format(int((time.time() - 1191988800) / 86400))),
            'I\'m version {}!'.format(VERSION),
            'I\'m version {}.whatever!'.format(VERSION.split('.')[0]),
            ('Brought to you by me, I\'m <a href="https://orthallelous.word'
             'press.com/">Orthallelous!</a>'),
            #'Brought to you by me, I\'m Htom Sirveaux!',
            'I wonder if there\'s beer on the sun',
            'Raspberry World: For all your raspberry needs. Off the beltline',
            #'I\'ve plummented to my death and I can\'t get up',
            '<em>NOT</em> compatible with the older version!',
            ('Hello there, fellow space travellers! Until somebody gives me '
             'some new lines in KAS, that is all I can say. - Bentusi Exchange'
             )
        ]
        if not hash(os.urandom(9)) % 4:
            self.extraLabel.setText(random.choice(hints))

    def genKey(self):
        "generate a random key"
        n = self.keySizeSB.value()
        char = string.printable.rstrip()  #map(chr, range(256))
        while len(char) < n:
            char += char
        key = ''.join(random.sample(char, n))
        self.keyInput.setText(key)

    def showKey(self, state=None):
        "hide/show key characters"
        if state is None: state = bool(self.showKeyCB.checkState())
        else: state = bool(state)
        if state: self.keyInput.setEchoMode(QLineEdit.Normal)
        else: self.keyInput.setEchoMode(QLineEdit.PasswordEchoOnEdit)

    def getFolder(self):
        "open file dialog and fill file table"
        path = QFileDialog(directory=self.path).getExistingDirectory()
        if not path: return
        self.path = str(path)
        self.populateTable(self.path)
        self.encrypted, self.decrypted = [], []
        return

    def resizeEvent(self, event):
        self.showFolder(self.path)  # update how the folder is shown

    def splitterChanged(self, pos):
        self.showFolder(self.path)  # likewise

    def showFolder(self, path):
        "displays current path, truncating as needed"
        if not path: return

        ell, sl = '\u2026', os.path.sep  # ellipsis, slash chars
        lfg, rfg = Qt.ElideLeft, Qt.ElideRight
        lst, wdh = os.path.basename(path), self.folderLabel.width()

        path = path.replace(os.path.altsep or '\\', sl)
        self.folderLabel.setToolTip(path)

        # truncate folder location
        fnt = QFontMetrics(self.folderLabel.font())
        txt = str(fnt.elidedText(path, lfg, wdh))

        if len(txt) <= 1:  # label is way too short
            self.folderLabel.setText('\u22ee' if txt != sl else txt)
            return  # but when would this happen?

        # truncate some more (don't show part of a folder name)
        if len(txt) < len(path) and txt[1] != sl:
            txt = ell + sl + txt.split(sl, 1)[-1]

            # don't truncate remaining folder name from the left
            if txt[2:] != lst and len(txt[2:]) < len(lst) + 2:
                txt = str(fnt.elidedText(ell + sl + lst, rfg, wdh))
        # you'd think len(txt) < len(lst) would work, but no; you'd be wrong

        self.folderLabel.setText(txt)

    def populateTable(self, path):
        "fill file table with file names"
        self.showFolder(path)

        names = []
        for n in os.listdir(path):
            if os.path.isdir(os.path.join(path, n)): continue  # folder
            names.append(n)

        self.folderTable.clearContents()
        self.folderTable.setRowCount(len(names))
        self.folderTable.setColumnCount(1)

        if not names:  # no files in this folder, inform user
            self.setMessage('This folder has no files')
            return

        self.folderTable.blockSignals(True)
        selEnab = Qt.ItemIsSelectable | Qt.ItemIsEnabled
        for i, n in enumerate(names):
            item = QTableWidgetItem()
            item.setText(n)
            item.setToolTip(n)
            item.setFlags(selEnab)

            # color code encrypted/decrypted files
            if n in self.encrypted:
                item.setTextColor(QColor(211, 70, 0))
                # allowed encrypted filenames to be changed
                item.setFlags(selEnab | Qt.ItemIsEditable)
            if n in self.decrypted:
                item.setForeground(QColor(0, 170, 255))
            self.folderTable.setItem(i, 0, item)
        if len(names) > 5:
            self.setMessage('{:,} files'.format(len(names)), 7)
        self.folderTable.blockSignals(False)
        return

    def editFileName(self, item):
        "change file name"
        new, old = str(item.text()), str(item.toolTip())

        result = QMessageBox.question(
            self, 'Renaming?',
            ("<p align='center'>Do you wish to rename<br>" +
             '<span style="color:#d34600;">{}</span>'.format(old) +
             "<br>to<br>" +
             '<span style="color:#ef4b00;">{}</span>'.format(new) +
             '<br>?</p>'))

        self.folderTable.blockSignals(True)
        if any(i in new for i in '/?<>:*|"^'):
            self.setMessage('Invalid character in name', 7)
            item.setText(old)
        elif result == QMessageBox.Yes:
            oold = os.path.join(self.path, old)
            try:
                os.rename(oold, os.path.join(self.path, new))
                self.encrypted.remove(old)
                self.encrypted.append(new)
                item.setToolTip(new)
            except Exception as err:
                self.setMessage(str(err), 9)
                item.setText(old)
                item.setToolTip(old)
                self.encrypted.remove(new)
                self.encrypted.append(old)
        else:
            item.setText(old)
        self.folderTable.blockSignals(False)

    def setMessage(self, message, secs=4, col=None):
        "show a message for a few seconds - col must be rgb triplet tuple"
        if self.timeout:  # https://stackoverflow.com/a/21081371
            self.timeout.stop()
            self.timeout.deleteLater()

        if col is None: color = 'rgb(255, 170, 127)'
        else:
            try:
                color = 'rgb({}, {}, {})'.format(*col)
            except:
                color = 'rgb(255, 170, 127)'

        self.messageLabel.setStyleSheet('background-color: {};'.format(color))
        self.messageLabel.setText(message)
        self.messageLabel.setToolTip(message)

        self.timeout = QTimer()
        self.timeout.timeout.connect(self.clearMessage)
        self.timeout.setSingleShot(True)
        self.timeout.start(secs * 1000)

    def clearMessage(self):
        self.messageLabel.setStyleSheet('')
        self.messageLabel.setToolTip('')
        self.messageLabel.setText('')

    def getName(self):
        "return file name of selected"
        items = self.folderTable.selectedItems()
        names = [str(i.text()) for i in items]
        if names: return names[0]  # only the first selected file
        else: return ''

    def showKeyLen(self, string):
        "displays a tooltip showing length of key"
        s = len(string)
        note = '{:,} character{}'.format(s, '' if s == 1 else 's')
        tip = QToolTip
        pos = self.genKeyButton.mapToGlobal(QPoint(0, 0))

        if s < self.minKeyLen:
            note = '<span style="color:#c80000;">{}</span>'.format(note)
        else:
            note = '<span style="color:#258f22;">{}</span>'.format(note)
        tip.showText(pos, note)

    def lock(self, flag=True):
        "locks buttons if True"
        stuff = [
            self.openButton,
            self.encryptButton,
            self.decryptButton,
            self.genKeyButton,
            self.hashButton,
            self.showKeyCB,
            self.copyButton,
            self.keyInput,
            self.keySizeSB,
            self.folderTable,
        ]
        for i in stuff:
            i.blockSignals(flag)
            i.setEnabled(not flag)
        return

    def _lerp(self, v1, v2, numPts=10):
        "linearly interpolate from v1 to v2\nFrom Orthallelous"
        if len(v1) != len(v2): raise ValueError("different dimensions")
        D, V, n = [], [], abs(numPts)
        for i, u in enumerate(v1):
            D.append(v2[i] - u)
        for i in range(n + 1):
            vn = []
            for j, u in enumerate(v1):
                vn.append(u + D[j] / float(n + 2) * i)
            V.append(tuple(vn))
        return V

    def weeeeeee(self):
        "party time"
        self.lock()
        self.setMessage('Party time!', 2.5)
        a, b, c = self.encryptPbar, self.decryptPbar, self.hashPbar
        process, sleep = app.processEvents, time.sleep

        am, bm, cm = a.minimum(), b.minimum(), c.minimum()
        ax, bx, cx = a.maximum(), b.maximum(), c.maximum()
        a.reset()
        b.reset()
        c.reset()

        loops = self._lerp((am, bm, cm), (ax, bx, cx), 100)
        ivops = loops[::-1]

        # up and up!
        for i in range(3):
            for j, k, l in loops:
                a.setValue(int(j))
                b.setValue(int(k))
                c.setValue(int(l))
                process()
                sleep(0.01)

        a.setValue(ax)
        b.setValue(bx)
        c.setValue(cx)
        sleep(0.25)
        a.setValue(am)
        b.setValue(bm)
        c.setValue(cm)

        # snake!
        self.setMessage('Snake time!')
        self.messageLabel.setStyleSheet('background-color: rgb(127,170,255);')
        for i in range(2):
            for j, k, l in loops:
                a.setValue(int(j))
                process()
                sleep(0.002)
            process()
            a.setInvertedAppearance(True)
            process()
            for j, k, l in ivops:
                a.setValue(int(j))
                process()
                sleep(0.002)

            for j, k, l in loops:
                b.setValue(int(k))
                process()
                sleep(0.002)
            process()
            b.setInvertedAppearance(False)
            process()
            for j, k, l in ivops:
                b.setValue(int(k))
                process()
                sleep(0.002)

            for j, k, l in loops:
                c.setValue(int(l))
                process()
                sleep(0.002)
            process()
            c.setInvertedAppearance(True)
            process()
            for j, k, l in ivops:
                c.setValue(int(l))
                process()
                sleep(0.002)

            process()
            b.setInvertedAppearance(True)
            process()
            for j, k, l in loops:
                b.setValue(int(k))
                process()
                sleep(0.002)
            process()
            b.setInvertedAppearance(False)
            process()
            for j, k, l in ivops:
                b.setValue(int(k))
                process()
                sleep(0.002)
            process()

            a.setInvertedAppearance(False)
            b.setInvertedAppearance(True)
            c.setInvertedAppearance(False)
        for j, k, l in loops:
            a.setValue(int(j))
            process()
            sleep(0.002)
        process()
        a.setInvertedAppearance(True)
        process()
        for j, k, l in ivops:
            a.setValue(int(j))
            process()
            sleep(0.002)

        # bars
        sleep(0.5)
        self.setMessage('Bars!')
        process()
        self.messageLabel.setStyleSheet('background-color: rgb(127,255,170);')
        for i in range(2):
            a.setValue(ax)
            time.sleep(0.65)
            a.setValue(am)
            sleep(0.25)
            process()
            b.setValue(bx)
            time.sleep(0.65)
            b.setValue(bm)
            sleep(0.25)
            process()
            c.setValue(cx)
            time.sleep(0.65)
            c.setValue(cm)
            sleep(0.25)
            process()
            b.setValue(bx)
            time.sleep(0.65)
            b.setValue(bm)
            sleep(0.25)
            process()

        # okay, enough
        process()
        a.setValue(ax)
        b.setValue(bx)
        c.setValue(cx)
        #a.setValue(am); b.setValue(bm); c.setValue(cm)
        a.setInvertedAppearance(False)
        b.setInvertedAppearance(True)
        c.setInvertedAppearance(False)
        self.lock(False)
        return

    def windDown(self, note=None):
        "silly deload on load"
        if note is None: note = 'Loading...'
        self.lock()
        self.setMessage(note)
        self.messageLabel.setStyleSheet('background-color: rgb(9, 190, 130);')
        a, b, c = self.encryptPbar, self.decryptPbar, self.hashPbar
        am, bm, cm = a.minimum(), b.minimum(), c.minimum()
        ax, bx, cx = a.maximum(), b.maximum(), c.maximum()
        a.reset()
        b.reset()
        c.reset()
        loops = self._lerp((ax, bx, cx), (am, bm, cm), 100)
        for j, k, l in loops:
            a.setValue(int(j))
            b.setValue(int(k))
            c.setValue(int(l))
            app.processEvents()
            time.sleep(0.02)
        a.reset()
        b.reset()
        c.reset()
        self.lock(False)
        self.clearMessage()

    def genHash(self, action):
        "generate hash of selected file and display it"
        name, t0 = self.getName(), time.perf_counter()

        # mark what hash was used in the drop-down menu
        for i in self.hashButton.menu().actions():
            if i == action: i.setIconVisibleInMenu(True)
            else: i.setIconVisibleInMenu(False)

        if str(action.text()) == 'Party':
            self.weeeeeee()
            self.windDown('Winding down...')
            return
        if not name:
            self.setMessage('No file selected')
            return
        if not os.path.exists(os.path.join(self.path, name)):
            self.setMessage('File does not exist')
            return

        self.lock()
        hsh = self.hashFile(os.path.join(self.path, name),
                            getattr(hashlib, str(action.text())))
        self.lock(False)
        #hsh = str(action.text()) + ': ' + hsh
        self.hashLabel.setText(hsh)
        self.hashLabel.setToolTip(hsh)
        self.extraLabel.setText(
            str(action.text()) + ' hash took ' +
            self.secs_fmt(time.perf_counter() - t0))

    def setCancel(self):
        "cancel operation"
        self._requestStop = True

    def showCancelButton(self, state=False):
        "show/hide cancel button"
        self.cancelButton.blockSignals(not state)
        self.cancelButton.setEnabled(state)
        if state:
            self.cancelButton.show()
            self.keyInput.hide()
            self.genKeyButton.hide()
            self.keySizeSB.hide()
        else:
            self.cancelButton.hide()
            self.keyInput.show()
            self.genKeyButton.show()
            self.keySizeSB.show()

    def hashFile(self, fn, hasher):
        "returns the hash value of a file"
        hsh, blksize = hasher(), self.blksize
        fsz, csz = os.path.getsize(fn), 0.0

        self.hashPbar.reset()
        self.showCancelButton(True)
        prog, title = '(# {:.02%}) {}', self.windowTitle()
        with open(fn, 'rb') as f:
            while 1:
                blk = f.read(blksize)
                if not blk: break
                hsh.update(blk)

                csz += blksize
                self.hashPbar.setValue(int(round(csz * 100.0 / fsz)))
                app.processEvents()
                self.setWindowTitle(prog.format(csz / fsz, title))
                if self._requestStop: break

        self.hashPbar.setValue(self.hashPbar.maximum())
        self.setWindowTitle(title)
        self.showCancelButton(False)

        if self._requestStop:
            self.setMessage('Hashing canceled!')
            self.hashPbar.setValue(self.hashPbar.minimum())
            self._requestStop = False
            return
        return hsh.hexdigest()

    def hashKey(self, key, salt=b''):
        "hashes a key for encrypting/decrypting file"
        salt = salt.encode() if type(salt) != bytes else salt
        key = key.encode() if type(key) != bytes else key
        p = app.processEvents
        self.setMessage('Key Hashing...', col=(226, 182, 249))
        p()
        key = hashlib.pbkdf2_hmac('sha512', key, salt, 444401)
        p()
        self.clearMessage()
        p()
        return hashlib.sha3_256(key).digest()  # AES requires a 32 char key

    def encrypt(self):
        "encrypt selected file with key"
        name, t0 = self.getName(), time.perf_counter()
        if not name:
            self.setMessage('No file selected')
            return
        if not os.path.exists(os.path.join(self.path, name)):
            self.setMessage('File does not exist')
            return
        key = str(self.keyInput.text())
        if len(key) < self.minKeyLen:
            self.setMessage(('Key must be at least '
                             '{} characters long').format(self.minKeyLen))
            return

        self.lock()
        gn = self.encryptFile(key, os.path.join(self.path, name))
        if not gn:
            self.lock(False)
            return
        self.encrypted.append(os.path.basename(gn))
        self.lock(False)

        self.populateTable(self.path)  # repopulate folder list
        bn, tt = os.path.basename(gn), time.perf_counter() - t0
        self.setMessage('Encrypted, saved "{}"'.format(bn, 13))
        self.extraLabel.setText('Encrypting took ' + self.secs_fmt(tt))

    def encryptFile(self, key, fn):
        "encrypts a file using AES (MODE_GCM)"
        chars = ''.join(map(chr, range(256))).encode()
        chk = AES.block_size
        sample = random.sample
        iv = bytes(sample(chars, chk * 2))
        salt = bytes(sample(chars * 2, 256))

        vault = AES.new(self.hashKey(key, salt), AES.MODE_GCM, iv)
        fsz = os.path.getsize(fn)
        del key
        blksize = self.blksize
        gn = fn + self.ext

        fne = os.path.basename(fn).encode()
        fnz = len(fne)
        if len(fne) % chk: fne += bytes(sample(chars, chk - len(fne) % chk))

        csz = 0.0  # current processed value
        self.encryptPbar.reset()
        prog, title = '({:.02%}) {}', self.windowTitle()
        self.showCancelButton(True)

        with open(fn, 'rb') as src, open(gn, 'wb') as dst:
            dst.write(bytes([0] * 16))  # spacer for MAC written at end
            dst.write(iv)
            dst.write(salt)  # store iv, salt
            # is it safe to store MAC, iv, salt plain right in file?
            # can't really store them encrypted,
            # or elsewhere in this model of single file encryption?
            # can't have another file for the file to lug around

            # store file size, file name length
            dst.write(vault.encrypt(struct.pack('<2Q', fsz, fnz)))
            dst.write(vault.encrypt(fne))  # store filename

            while 1:
                dat = src.read(blksize)
                if not dat: break
                elif len(dat) % chk:  # add padding
                    fil = chk - len(dat) % chk
                    dat += bytes(sample(chars, fil))
                dst.write(vault.encrypt(dat))

                csz += blksize  # show progress
                self.encryptPbar.setValue(int(round(csz * 100.0 / fsz)))
                self.setWindowTitle(prog.format(csz / fsz, title))
                app.processEvents()

                if self._requestStop: break
            if not self._requestStop:
                stuf = random.randrange(23)  # pack in more stuffing
                fing = b''.join(bytes(sample(chars, 16)) for i in range(stuf))
                dst.write(vault.encrypt(fing))  # and for annoyance

                dst.seek(0)
                dst.write(vault.digest())  # write MAC
                self.hashLabel.setText('MAC: ' + vault.hexdigest())

        self.encryptPbar.setValue(self.encryptPbar.maximum())
        self.setWindowTitle(title)
        self.showCancelButton(False)

        if self._requestStop:
            self.setMessage('Encryption canceled!')
            self.encryptPbar.setValue(self.encryptPbar.minimum())
            self._requestStop = False
            os.remove(gn)
            return
        return gn

    def decrypt(self):
        "encrypt selected file with key"
        name, t0 = self.getName(), time.perf_counter()
        if not name:
            self.setMessage('No file selected')
            return
        if not os.path.exists(os.path.join(self.path, name)):
            self.setMessage('File does not exist')
            return
        key = str(self.keyInput.text())
        if len(key) < self.minKeyLen:
            self.setMessage(('Key must be at least '
                             '{} characters long').format(self.minKeyLen))
            return

        self.lock()
        gn = self.decryptFile(key, os.path.join(self.path, name))
        if not gn:
            self.lock(False)
            return
        self.decrypted.append(os.path.basename(gn))
        self.lock(False)

        self.populateTable(self.path)  # repopulate folder list
        bn, tt = os.path.basename(gn), time.perf_counter() - t0
        self.setMessage('Decrypted, saved "{}"'.format(bn, 13))
        self.extraLabel.setText('Decrypting took ' + self.secs_fmt(tt))

    def decryptFile(self, key, fn):
        "decrypts a file using AES (MODE_GCM)"
        blksize = self.blksize
        gn = hashlib.md5(os.path.basename(fn).encode()).hexdigest()
        gn = os.path.join(self.path, gn)  # temporary name
        if os.path.exists(gn):
            self.setMessage('file already exists')
            return

        self.decryptPbar.reset()
        csz = 0.0  # current processed value
        chk, fnsz = AES.block_size, os.path.getsize(fn)
        prog, title = '({:.02%}) {}', self.windowTitle()
        try:
            with open(fn, 'rb') as src, open(gn, 'wb') as dst:
                # extract iv, salt
                MAC = src.read(16)
                iv = src.read(AES.block_size * 2)
                salt = src.read(256)
                vault = AES.new(self.hashKey(key, salt), AES.MODE_GCM, iv)
                self.showCancelButton(True)

                # extract file size, file name length
                sizes = src.read(struct.calcsize('<2Q'))
                fsz, fnz = struct.unpack('<2Q', vault.decrypt(sizes))

                # extract filename; round up fnz to nearest chk
                rnz = fnz if not fnz % chk else fnz + chk - fnz % chk
                rfn = vault.decrypt(src.read(rnz))[:fnz].decode()
                self.setMessage('Found "{}"'.format(rfn), 13, (255, 211, 127))

                while 1:
                    dat = src.read(blksize)
                    if not dat: break
                    dst.write(vault.decrypt(dat))

                    csz += blksize  # show progress
                    self.decryptPbar.setValue(int(round(csz * 100.0 / fnsz)))
                    self.setWindowTitle(prog.format(1 - (csz / fnsz), title))
                    app.processEvents()
                    if self._requestStop: break

                if not self._requestStop: dst.truncate(fsz)  # remove padding
            if not self._requestStop:
                vault.verify(MAC)
                self.hashLabel.setText('')

        except (ValueError, KeyError) as err:
            os.remove(gn)
            self.setMessage('Invalid decryption!')
            self.setWindowTitle(title)
            self.showCancelButton(False)
            return
        except Exception as err:
            os.remove(gn)
            self.setMessage('Invalid key or file!')
            self.setWindowTitle(title)
            self.showCancelButton(False)
            return
        self.decryptPbar.setValue(self.decryptPbar.maximum())
        self.setWindowTitle(title)
        self.showCancelButton(False)

        if self._requestStop:
            self.setMessage('Decryption canceled!')
            self.decryptPbar.setValue(self.decryptPbar.minimum())
            self._requestStop = False
            os.remove(gn)
            return

        # restore original file name
        name, ext = os.path.splitext(rfn)
        count = 1
        fn = os.path.join(self.path, name + ext)
        while os.path.exists(fn):
            fn = os.path.join(self.path, name + '_{}'.format(count) + ext)
            count += 1
        os.rename(gn, fn)  # restore original name
        return fn  # saved name

    def copyKeyHash(self, action):
        "copies either the key or the hash to clipboard"
        act = str(action.text()).lower()

        if 'key' in act: txt = str(self.keyInput.text())
        elif 'hash' in act: txt = str(self.hashLabel.text())
        else:
            self.setMessage('Invalid copy selection')
            return

        if not txt:
            self.setMessage('Empty text; Nothing to copy')
            return

        if 'key' in act: self.setMessage('Key copied to clipboard')
        elif 'hash' in act: self.setMessage('Hash copied to clipboard')
        else:
            self.setMessage('Invalid copy selection')
            return

        self.clipboard.clear()
        self.clipboard.setText(txt)

    def secs_fmt(self, s):
        "6357 -> '1h 45m 57s'"
        Y, D, H, M = 31556952, 86400, 3600, 60
        y = int(s // Y)
        s -= y * Y
        d = int(s // D)
        s -= d * D
        h = int(s // H)
        s -= h * H
        m = int(s // M)
        s -= m * M

        r = (str(int(s)) if int(s) == s else str(round(s, 3))) + 's'

        if m: r = str(m) + 'm ' + r
        if h: r = str(h) + 'h ' + r
        if d: r = str(d) + 'd ' + r
        if y: r = str(y) + 'y ' + r
        return r.strip()

    def closeEvent(self, event):
        self.clipboard.clear()

    def setup(self):
        "constructs the gui"
        Fixed = QSizePolicy()
        MinimumExpanding = QSizePolicy(QSizePolicy.MinimumExpanding,
                                       QSizePolicy.MinimumExpanding)
        self.minKeyLen = 8
        self.maxKeyLen = 4096

        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Horizontal)
        self.splitter.splitterMoved.connect(self.splitterChanged)

        # left column
        self.leftColumn = QWidget()
        self.vl01 = QVBoxLayout()

        # left column - first item (0; horizonal layout 0)
        self.hl00 = QHBoxLayout()
        self.hl00.setSpacing(5)

        self.openButton = QPushButton('&Open')
        self.openButton.setToolTip('Open folder')
        self.openButton.setMinimumSize(60, 20)
        self.openButton.setMaximumSize(60, 20)
        self.openButton.setSizePolicy(Fixed)
        self.openButton.clicked.connect(self.getFolder)
        #ico = self.style().standardIcon(QStyle.SP_DirIcon)
        #self.openButton.setIcon(ico)

        self.folderLabel = QLabel()
        self.folderLabel.setMinimumSize(135, 20)
        self.folderLabel.setMaximumSize(16777215, 20)
        self.folderLabel.setSizePolicy(MinimumExpanding)
        self.hl00.insertWidget(0, self.openButton)
        self.hl00.insertWidget(1, self.folderLabel)

        # left column - second item (1)
        self.folderTable = QTableWidget()
        self.folderTable.setMinimumSize(200, 32)
        self.folderTable.horizontalHeader().setVisible(False)
        self.folderTable.horizontalHeader().setStretchLastSection(True)
        self.folderTable.verticalHeader().setVisible(False)
        self.folderTable.verticalHeader().setDefaultSectionSize(15)
        self.folderTable.itemChanged.connect(self.editFileName)

        # left column - third item (2)
        self.extraLabel = QLabel()
        self.extraLabel.setMinimumSize(200, 20)
        self.extraLabel.setMaximumSize(16777215, 20)
        self.extraLabel.setSizePolicy(MinimumExpanding)
        self.extraLabel.setTextInteractionFlags(Qt.LinksAccessibleByMouse)

        # finalize left column
        self.vl01.insertLayout(0, self.hl00)
        self.vl01.insertWidget(1, self.folderTable)
        self.vl01.insertWidget(2, self.extraLabel)
        self.leftColumn.setLayout(self.vl01)

        # right column
        self.rightColumn = QWidget()
        self.vl02 = QVBoxLayout()

        # right column - first item (0)
        self.messageLabel = QLabel()
        self.messageLabel.setMinimumSize(290, 20)
        self.messageLabel.setMaximumSize(16777215, 20)
        self.messageLabel.setSizePolicy(MinimumExpanding)
        self.messageLabel.setAlignment(Qt.AlignCenter)

        # right column - second item (2; horizontal layout 1)
        self.hl01 = QHBoxLayout()
        self.hl01.setSpacing(5)

        self.encryptButton = QPushButton('&Encrypt')  #\U0001F512
        self.encryptButton.setToolTip('Encrypt selected file')
        self.encryptButton.setMinimumSize(60, 20)
        self.encryptButton.setMaximumSize(60, 20)
        self.encryptButton.setSizePolicy(Fixed)
        self.encryptButton.clicked.connect(self.encrypt)

        self.encryptPbar = QProgressBar()
        self.encryptPbar.setMinimumSize(225, 20)
        self.encryptPbar.setMaximumSize(16777215, 20)
        self.encryptPbar.setSizePolicy(MinimumExpanding)
        self.encryptPbar.setTextVisible(False)

        palette = self.encryptPbar.palette()  # color of progress bar
        color = QColor(211, 70, 0)
        palette.setColor(QPalette.Highlight, color)
        self.encryptPbar.setPalette(palette)

        self.hl01.insertWidget(0, self.encryptButton)
        self.hl01.insertWidget(1, self.encryptPbar)

        # right column - third item (3; horizontal layout 2)
        self.hl02 = QHBoxLayout()
        self.hl02.setSpacing(5)

        self.cancelButton = QPushButton('C&ANCEL')
        self.cancelButton.setToolTip('Cancels current operation')
        self.cancelButton.setMinimumSize(70, 24)
        self.cancelButton.setMaximumSize(70, 24)
        self.cancelButton.setSizePolicy(Fixed)
        self.cancelButton.clicked.connect(self.setCancel)
        font = self.cancelButton.font()
        font.setBold(True)
        self.cancelButton.setFont(font)
        self.cancelButton.blockSignals(True)
        self.cancelButton.setEnabled(False)
        self.cancelButton.hide()
        self._requestStop = False

        self.keyInput = QLineEdit()
        self.keyInput.setMinimumSize(225, 20)
        self.keyInput.setMaximumSize(16777215, 20)
        self.keyInput.setSizePolicy(MinimumExpanding)
        self.keyInput.setPlaceholderText('key')
        self.keyInput.setMaxLength(self.maxKeyLen)
        self.keyInput.setAlignment(Qt.AlignCenter)
        self.keyInput.textEdited.connect(self.showKeyLen)

        self.genKeyButton = QPushButton('&Gen Key')  #\U0001F511
        self.genKeyButton.setToolTip('Generate a random key')
        self.genKeyButton.setMinimumSize(60, 20)
        self.genKeyButton.setMaximumSize(60, 20)
        self.genKeyButton.setSizePolicy(Fixed)
        self.genKeyButton.clicked.connect(self.genKey)

        self.keySizeSB = QSpinBox()
        self.keySizeSB.setToolTip('Length of key to generate')
        self.keySizeSB.setRange(32, 1024)
        self.keySizeSB.setMinimumSize(40, 20)
        self.keySizeSB.setMaximumSize(40, 20)
        self.keySizeSB.setSizePolicy(Fixed)
        self.keySizeSB.setAlignment(Qt.AlignCenter)
        self.keySizeSB.setButtonSymbols(QSpinBox.NoButtons)
        self.keySizeSB.setWrapping(True)

        self.hl02.insertWidget(0, self.cancelButton)
        self.hl02.insertWidget(1, self.keyInput)
        self.hl02.insertWidget(2, self.genKeyButton)
        self.hl02.insertWidget(3, self.keySizeSB)

        # right column - fourth item (4; horizontal layout 3)
        self.hl03 = QHBoxLayout()
        self.hl03.setSpacing(5)

        self.decryptButton = QPushButton('&Decrypt')  #\U0001F513
        self.decryptButton.setToolTip('Decrypt selected file')
        self.decryptButton.setMinimumSize(60, 20)
        self.decryptButton.setMaximumSize(60, 20)
        self.decryptButton.setSizePolicy(Fixed)
        self.decryptButton.clicked.connect(self.decrypt)

        self.decryptPbar = QProgressBar()
        self.decryptPbar.setMinimumSize(225, 20)
        self.decryptPbar.setMaximumSize(16777215, 20)
        self.decryptPbar.setSizePolicy(MinimumExpanding)
        self.decryptPbar.setTextVisible(False)
        self.decryptPbar.setInvertedAppearance(True)

        palette = self.decryptPbar.palette()  # color of progress bar
        color = QColor(0, 170, 255)
        palette.setColor(QPalette.Highlight, color)
        self.decryptPbar.setPalette(palette)

        self.hl03.insertWidget(0, self.decryptButton)
        self.hl03.insertWidget(1, self.decryptPbar)

        # right column - fifth item (7; horizontal layout 4)
        self.hl04 = QHBoxLayout()
        self.hl04.setSpacing(5)

        self.showKeyCB = QCheckBox('&Show Key')
        self.showKeyCB.setToolTip('Show/Hide key value')
        self.showKeyCB.setMinimumSize(75, 20)
        self.showKeyCB.setMaximumSize(75, 20)
        self.showKeyCB.setSizePolicy(Fixed)
        self.showKeyCB.clicked.connect(self.showKey)
        self.showKeyCB.setChecked(True)

        self.hashPbar = QProgressBar()
        self.hashPbar.setMinimumSize(150, 20)
        self.hashPbar.setMaximumSize(16777215, 20)
        self.hashPbar.setSizePolicy(MinimumExpanding)
        self.hashPbar.setTextVisible(False)

        palette = self.hashPbar.palette()  # color of progress bar
        color = QColor(31, 120, 73)
        palette.setColor(QPalette.Highlight, color)
        self.hashPbar.setPalette(palette)

        self.hashButton = QPushButton('&Hash')
        self.hashButton.setToolTip('Determine file hash')
        self.hashButton.setMinimumSize(60, 20)
        self.hashButton.setMaximumSize(60, 20)
        self.hashButton.setSizePolicy(Fixed)

        menu = QMenu(self.hashButton)
        ico = self.style().standardIcon(QStyle.SP_DialogYesButton)
        for alg in sorted(
                filter(lambda x: 'shake' not in x,
                       hashlib.algorithms_guaranteed),
                key=lambda n:
            (len(n), sorted(hashlib.algorithms_guaranteed).index(n))):
            menu.addAction(
                ico, alg
            )  # drop shake algs as their .hexdigest requires an argument - the rest don't
        menu.addAction(ico, 'Party')
        for i in menu.actions():
            i.setIconVisibleInMenu(False)
        self.hashButton.setMenu(menu)
        menu.triggered.connect(self.genHash)

        self.hl04.insertWidget(0, self.showKeyCB)
        self.hl04.insertWidget(1, self.hashPbar)
        self.hl04.insertWidget(2, self.hashButton)

        # right column - sixth item (8; horizontal layout 5)
        self.hl05 = QHBoxLayout()
        self.hl05.setSpacing(5)

        self.copyButton = QPushButton('&Copy')  #\U0001F4CB
        self.copyButton.setToolTip('Copy key or hash to clipboard')
        self.copyButton.setMinimumSize(60, 20)
        self.copyButton.setMaximumSize(60, 20)
        self.copyButton.setSizePolicy(Fixed)

        menu2 = QMenu(self.copyButton)
        menu2.addAction('Copy Key')
        menu2.addAction('Copy Hash')
        self.copyButton.setMenu(menu2)
        menu2.triggered.connect(self.copyKeyHash)

        self.hashLabel = QLabel()
        self.hashLabel.setMinimumSize(225, 20)
        self.hashLabel.setMaximumSize(16777215, 20)
        self.hashLabel.setSizePolicy(MinimumExpanding)
        self.hashLabel.setTextFormat(Qt.PlainText)
        self.hashLabel.setAlignment(Qt.AlignCenter)
        self.hashLabel.setTextInteractionFlags(Qt.TextSelectableByMouse)

        self.hl05.insertWidget(0, self.copyButton)
        self.hl05.insertWidget(1, self.hashLabel)

        # finalize right column
        self.vl02.insertWidget(0, self.messageLabel)
        self.vl02.insertSpacerItem(1, QSpacerItem(0, 0))
        self.vl02.insertLayout(2, self.hl01)
        self.vl02.insertLayout(3, self.hl02)
        self.vl02.insertLayout(4, self.hl03)
        self.vl02.insertSpacerItem(5, QSpacerItem(0, 0))
        self.vl02.insertWidget(6, QFrame())
        self.vl02.insertLayout(7, self.hl04)
        self.vl02.insertLayout(8, self.hl05)
        self.rightColumn.setLayout(self.vl02)

        # finalize main window
        self.splitter.insertWidget(0, self.leftColumn)
        self.splitter.insertWidget(1, self.rightColumn)

        layout = QHBoxLayout(self)
        layout.addWidget(self.splitter)
        self.setLayout(layout)

        self.setWindowTitle('Simple File Encryptor/Decryptor')
        self.resize(self.sizeHint())
Esempio n. 16
0
class KaitaiView(QScrollArea, View):
    def __init__(self, parent, binaryView):
        QScrollArea.__init__(self, parent)

        View.__init__(self)
        View.setBinaryDataNavigable(self, True)
        self.setupView(self)

        # BinaryViewType
        self.binaryView = binaryView

        self.rootSelectionStart = 0
        self.rootSelectionEnd = 1

        self.ioRoot = None
        self.ioCurrent = None

        # top half = treeWidget + structPath
        self.treeWidget = MyQTreeWidget()
        self.treeWidget.setColumnCount(4)
        self.treeWidget.setHeaderLabels(['label', 'value', 'start', 'end'])
        self.treeWidget.itemSelectionChanged.connect(self.onTreeSelect)

        self.structPath = QLineEdit("root")
        self.structPath.setReadOnly(True)

        topHalf = QWidget(self)
        layout = QVBoxLayout()
        layout.addWidget(self.treeWidget)
        layout.addWidget(self.structPath)
        topHalf.setLayout(layout)

        # bottom half = hexWidget
        self.hexWidget = HexEditor(binaryView,
                                   ViewFrame.viewFrameForWidget(self), 0)

        # splitter = top half, bottom half
        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.splitter.addWidget(topHalf)
        self.splitter.addWidget(self.hexWidget)

        self.setWidgetResizable(True)
        self.setWidget(self.splitter)

        self.kaitaiParse()

    # parse the file using Kaitai, construct the TreeWidget
    def kaitaiParse(self, ksModuleName=None):
        log.log_debug('kaitaiParse() with len(bv)=%d and bv.file.filename=%s' %
                      (len(self.binaryView), self.binaryView.file.filename))

        if len(self.binaryView) == 0:
            return

        kaitaiIO = kshelpers.KaitaiBinaryViewIO(self.binaryView)
        parsed = kshelpers.parseIo(kaitaiIO, ksModuleName)
        if not parsed:
            return

        # it SEEMS as if parsing is finished at this moment, but some parsing
        # is postponed until attributes are accessed, so we must try/catch here
        tree = None
        if True:
            try:
                tree = kshelpers.buildQtree(parsed)
            except Exception as e:
                log.log_error(
                    'kaitai module %s threw exception, check file type' %
                    ksModuleName)
                tree = None
        else:
            tree = kshelpers.buildQtree(parsed)

        if not tree:
            return

        self.ioRoot = tree.ksobj._io
        self.ioCurrent = tree.ksobj._io

        self.treeWidget.clear()
        self.treeWidget.setSortingEnabled(False)  # temporarily, for efficiency
        # two options with how we create the hierarchy
        if False:
            # treat root as top level "file" container
            tree.setLabel('file')
            tree.setValue(None)
            tree.setStart(0)
            tree.setEnd(0)
            self.treeWidget.insertTopLevelItem(0, tree)
        else:
            # add root's children as top level items
            self.treeWidget.insertTopLevelItems(0, tree.takeChildren())

        # enable sorting
        self.treeWidget.setSortingEnabled(True)
        self.treeWidget.sortByColumn(2, Qt.AscendingOrder)

        # TODO: select first item, maybe expand a few things
        self.rootSelectionStart = 0
        self.rootSelectionEnd = 1

        self.treeWidget.setUniformRowHeights(True)
        self.treeWidget.queueInitialPresentation = True

    # binja callbacks
    def getData(self):
        return self.binaryView

    def getStart(self):
        result = self.binaryView.start
        #log.log_debug('getStart() returning '+str(result))
        return result

    def getEnd(self):
        result = self.binaryView.end
        #log.log_debug('getEnd() returning '+str(result))
        return result

    def getLength(self):
        result = len(self.binaryView)
        #log.log_debug('getLength() returning '+str(result))
        return result

    def getCurrentOffset(self):
        result = self.rootSelectionStart + int(
            (self.rootSelectionEnd - self.rootSelectionStart) / 2)
        #result = self.rootSelectionStart
        #log.log_debug('getCurrentOffset() returning '+str(result))
        return result

    def getSelectionOffsets(self):
        result = None
        if self.hexWidget:
            result = self.hexWidget.getSelectionOffsets()
        else:
            result = (self.rootSelectionStart, self.rootSelectionStart)
        #log.log_debug('getSelectionOffsets() returning '+str(result))
        return result

    def setCurrentOffset(self, offset):
        #log.log_debug('setCurrentOffset(0x%X)' % offset)
        self.rootSelectionStart = offset
        UIContext.updateStatus(True)

    def getFont(self):
        return binaryninjaui.getMonospaceFont(self)

    def navigate(self, addr):
        self.rootSelectionStart = addr
        self.rootSelectionEnd = addr + 1
        self.hexWidget.setSelectionRange(addr, addr + 1)
        return True

    def navigateToFileOffset(self, offset):
        #log.log_debug('navigateToFileOffset()')
        return False

    def onTreeSelect(self, wtf=None):
        # get KaitaiTreeWidgetItem
        items = self.treeWidget.selectedItems()
        if not items or len(items) < 1:
            return
        item = items[0]

        # build path, inform user
        structPath = item.label
        itemTmp = item
        while itemTmp.parent():
            itemTmp = itemTmp.parent()
            label = itemTmp.label
            if label.startswith('_m_'):
                label = label[3:]
            structPath = label + '.' + structPath
        self.structPath.setText('root.' + structPath)

        #
        (start, end) = (item.start, item.end)
        if start == None or end == None:
            return

        # determine current IO we're in (the Kaitai input/output abstraction)
        _io = None
        # if the tree item is linked to a KaitaiNode, simply read the IO
        if item.ksobj:
            _io = item.ksobj._parent._io
        else:
            # else we're a leaf
            parent = item.parent()
            if parent:
                # a leaf with a parent -> read parent's IO
                _io = parent.ksobj._io
            else:
                # a leaf without a parent -> we must be at root -> use root IO
                _io = self.ioRoot

        # if the selection is in the root view, store the interval so that upon
        # getCurrentOffset() callback, we return the middle and feature map is
        # updated
        if _io == self.ioRoot:
            self.rootSelectionStart = start
            self.rootSelectionEnd = end

        # current kaitai object is on a different io? then swap HexEditor
        if _io != self.ioCurrent:
            # delete old view
            self.hexWidget.hide()
            self.hexWidget.setParent(None)
            self.hexWidget.deleteLater()
            self.hexWidget = None

            # if it's the original file IO, wrap the already-open file binary view
            if _io == self.ioRoot:
                self.hexWidget = HexEditor(self.binaryView,
                                           ViewFrame.viewFrameForWidget(self),
                                           0)
            # otherwise delete old view, create a temporary view
            else:
                # create new view
                length = _io.size()
                _io.seek(0)
                data = _io.read_bytes(length)
                bv = binaryview.BinaryView.new(data)
                self.hexWidget = HexEditor(bv,
                                           ViewFrame.viewFrameForWidget(self),
                                           0)

            self.splitter.addWidget(self.hexWidget)
            self.ioCurrent = _io

        # now position selection in whatever HexEditor is current
        #log.log_debug('selecting to [0x%X, 0x%X)' % (start, end))
        self.hexWidget.setSelectionRange(start, end)

        # set hex group title to reflect current selection
        #self.hexGroup.setTitle('Hex View @ [0x%X, 0x%X)' % (start, end))

    def getStatusBarWidget(self):
        return menu.KaitaiStatusBarWidget(self)
Esempio n. 17
0
class Widget:
    def __init__(self, instance: QObject = None):
        self._layout = QVBoxLayout()
        if instance is not None:
            self._instance = instance
        self._enabled_dependencies = []
        self._state_saver = None
        self._layouts = [QVBoxLayout()]
        self._cur_tab_widget = None
        self._cur_splitter = None

    def set_state_saver(self, saver: StateSaver):
        self._state_saver = saver

    def get_layout(self):
        """
        Return layout of widget
        :return: layout, contains Widget instance
        @:rtype: QLayout
        """
        self._layout.addLayout(self._layouts[-1])
        return self._layout

    def get_instance(self):
        """
        Get Qt instance of widget
        :return: instance of widget (PySide object)
        :rtype QWidget
        """
        return self._instance

    def _may_be_enabled(self, is_enabled):
        if is_enabled:
            for depends in self._enabled_dependencies:
                if not depends.get_value():
                    return False
        return is_enabled

    def set_enabled(self, is_enabled: bool = True):
        """
        Set widget enabled
        :param is_enabled: state of widget: enabled or not
        :return: Widget object (self)
        :rtype: Widget
        """
        self._instance.setEnabled(self._may_be_enabled(is_enabled))
        return self

    def add_enabled_dependency(self, dependency: Checkable):
        self.set_enabled(dependency.get_value())
        self._enabled_dependencies.append(dependency)
        dependency.add_clicked_callback(self.set_enabled)

    def add_widget(self,
                   widget: "Widget instance",
                   need_store=False,
                   need_stretch=True):
        """
        Add widget to window layout
        :param widget: Widget unit
        :param need_store: is need to store state  of specified widget
        :param need_stretch: is need to insert stretch around widget
        :return: widget instance
        """
        if need_stretch:
            self.get_current_layout().addStretch()
        self.get_current_layout().addLayout(widget.get_layout())
        if need_stretch:
            self.get_current_layout().addStretch()

        if need_store and self._state_saver is not None:
            self._state_saver.add_widget(widget)

        return widget

    def add_widgets(self, widgets: "list of Widget instances"):
        """
        Add list of widgets to window layout
        :param widgets: Widget units
        :return: None
        """
        for widget in widgets:
            self.get_current_layout().addLayout(widget.get_layout())

    def start_horizontal(self):
        """
        Start horizontal components insertion
        :return: None
        """
        if isinstance(self.get_current_layout(), QHBoxLayout):
            return
        layout = QHBoxLayout()
        self.get_current_layout().addLayout(layout)
        self._layouts.append(layout)

    def start_vertical(self):
        """
        Start vertical components insertion
        :return: None
        """
        if isinstance(self.get_current_layout(), QVBoxLayout):
            return
        layout = QVBoxLayout()
        self.get_current_layout().addLayout(layout)
        self._layouts.append(layout)

    def group_horizontal(self, widgets: list):
        """
        Place list of widgets horizontal
        :param widgets: list of widgets
        :return: None
        """
        self.start_horizontal()
        self.add_widgets(widgets)
        self.cancel()

    def group_vertical(self, widgets: "list of Widget instances"):
        """
        Place list of widgets vertical
        :param widgets: list of widgets
        :return: None
        """
        self.start_vertical()
        self.add_widgets(widgets)
        self.cancel()

    def start_splitter(self, orientation='vertical'):
        self._cur_splitter = QSplitter()
        self._cur_splitter.setOrientation(
            Qt.Orientation.Horizontal if orientation ==
            'horizontal' else Qt.Orientation.Vertical)
        self.get_current_layout().addWidget(self._cur_splitter)

    def add_splitter_space(self):
        self._layouts.append(QVBoxLayout())
        widget = QWidget()
        widget.setLayout(self.get_current_layout())
        self._cur_splitter.addWidget(widget)

    def get_current_layout(self):
        """
        Return current layout
        :return: layout of type QLayout
        """
        return self._layouts[-1]

    def add_to_group_box(self, group_name: str,
                         widgets: "list of Widget instances"):
        """
        Place layout to group box
        :param group_name: name of group
        :param widgets: list of widgets, that been placed to group
        :return: None
        """
        self.start_group_box(group_name)
        for widget in widgets:
            self.add_widget(widget)
        self.cancel()

    def start_group_box(self, name: str):
        """
        Start group box
        :return:
        """
        group_box = QGroupBox(name)
        group_box_layout = QVBoxLayout()
        group_box.setLayout(group_box_layout)
        self.get_current_layout().addWidget(group_box)
        self._layouts.append(group_box_layout)

    def cancel(self):
        """
        Cnacel last format append
        :return: None
        """
        del self._layouts[-1]

    def insert_text_label(self, text, is_link=False):
        widget = QLabel(text)
        widget.setOpenExternalLinks(is_link)
        self.get_current_layout().addWidget(widget)

    def insert_tab_space(self):
        self._cur_tab_widget = QTabWidget()
        self.get_current_layout().addWidget(self._cur_tab_widget)

    def add_tab(self, name):
        self._layouts.append(QVBoxLayout())
        widget = QWidget()
        widget.setLayout(self.get_current_layout())
        self._cur_tab_widget.addTab(widget, name)
Esempio n. 18
0
class Ui_FE14MapEditor(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.toolbar = QToolBar()
        self.toggle_coordinate_type_action = QAction("Toggle Coordinate Type")
        self.refresh_action = QAction("Refresh")
        self.refresh_action.setShortcut(QKeySequence("Ctrl+R"))
        self.copy_spawn_action = QAction("Copy Spawn")
        self.copy_spawn_action.setShortcut(QKeySequence("Ctrl+C"))
        self.paste_spawn_action = QAction("Paste Spawn")
        self.paste_spawn_action.setShortcut(QKeySequence("Ctrl+V"))
        self.add_spawn_action = QAction("Add Spawn")
        self.delete_spawn_action = QAction("Delete Spawn")
        self.add_group_action = QAction("Add Group")
        self.delete_group_action = QAction("Delete Group")
        self.add_tile_action = QAction("Add Tile")
        self.toggle_mode_action = QAction("Toggle Mode")
        self.undo_action = QAction("Undo")
        self.undo_action.setShortcut(QKeySequence("Ctrl+Z"))
        self.redo_action = QAction("Redo")
        self.redo_action.setShortcut(QKeySequence("Ctrl+Shift+Z"))
        self.toolbar.addActions(
            [self.toggle_coordinate_type_action, self.refresh_action])
        self.toolbar.addSeparator()
        self.toolbar.addActions([
            self.copy_spawn_action, self.paste_spawn_action,
            self.add_spawn_action, self.delete_spawn_action,
            self.add_group_action, self.delete_group_action
        ])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.add_tile_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.toggle_mode_action)
        self.toolbar.addSeparator()
        self.toolbar.addActions([self.undo_action, self.redo_action])
        self.addToolBar(self.toolbar)

        self.model_view = QTreeView()
        self.model_view.setHeaderHidden(True)
        self.grid = FE14MapGrid()
        self.grid_scroll = QScrollArea()
        self.grid_scroll.setWidgetResizable(True)
        self.grid_scroll.setWidget(self.grid)
        self.tile_list = QListView()
        self.terrain_pane = FE14TerrainEditorPane()
        self.spawn_pane = FE14SpawnEditorPane()

        self.model_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.model_view_context_menu = QMenu()
        self.model_view_context_menu.addActions(
            [self.toggle_coordinate_type_action, self.refresh_action])
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addActions([
            self.copy_spawn_action, self.paste_spawn_action,
            self.add_spawn_action, self.delete_spawn_action,
            self.add_group_action, self.delete_group_action
        ])
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addAction(self.add_tile_action)
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addAction(self.toggle_mode_action)
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addActions(
            [self.undo_action, self.redo_action])

        self.status_bar = QStatusBar()
        self.coordinate_type_label = QLabel()
        self.status_bar.addPermanentWidget(self.coordinate_type_label)
        self.setStatusBar(self.status_bar)

        self.main_widget = QSplitter()
        self.main_widget.setOrientation(QtCore.Qt.Horizontal)
        self.main_widget.addWidget(self.model_view)
        self.main_widget.addWidget(self.grid_scroll)
        self.main_widget.addWidget(self.spawn_pane)
        self.main_widget.addWidget(self.terrain_pane)
        self.setCentralWidget(self.main_widget)
Esempio n. 19
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("Lab-Data-Converter")

        self.create_actions()
        self.setup_menu()

        self.settings = QSettings("UCB", "Lab-Data-Converter")
        self.data_sets = {}
        self.output_widget = OutputWidget(self)
        self.data_table = DataTabel(self)
        self.splitter1 = QSplitter()
        self.splitter1.setOrientation(Qt.Vertical)
        self.splitter1.addWidget(self.data_table)
        self.splitter1.addWidget(self.output_widget)
        self.splitter1.setSizes([200, 100])
        #self.splitter1.setStretchFactor(0, 8)
        #self.splitter1.setStretchFactor(1, 4)
        self.setCentralWidget(self.splitter1)

        QDir.setCurrent(QStandardPaths.standardLocations(
            QStandardPaths.DocumentsLocation)[-1])
        if (self.settings.value("work_dir")):
            try:
                QDir.setCurrent(self.settings.value("work_dir"))
            except:
                pass

    def create_actions(self):
        self.open_act = QAction("Open", self)
        self.open_act.setShortcuts(QKeySequence.Open) 
        self.open_act.setStatusTip("Open one or multiple files");
        self.open_act.triggered.connect(self.open_file)

        self.close_act = QAction("Close", self)
        self.close_act.setShortcuts(QKeySequence.Close)
        self.close_act.setStatusTip("Close selected files");
        self.close_act.triggered.connect(self.close_file)
        
    def setup_menu(self):
        self.menuBar().addAction(self.open_act)
        self.menuBar().addAction(self.close_act)

    def open_file(self):
        files = QFileDialog.getOpenFileNames(self, "Open file", QDir.currentPath(),
                                             "Files (*.csv *.txt)")
        for file in files[0]:
            if (file not in self.data_sets):
                data = read_file(file)
                if (data is not None):
                    self.data_sets[file] = data
                    self.settings.setValue("work_dir", QFileInfo(file).absolutePath())
                    self.output_widget.add_to_list(file)
                else:
                    QMessageBox.critical(self, "Main window", "File " + file[0] +
                                        " has an unknown format!")
        self.data_table.initialize(self.data_sets)

    def close_file(self):
        files_to_close = self.output_widget.files_selected()
        for file_to_close in files_to_close:
            self.data_sets.pop(file_to_close, None)
            if (not self.output_widget.remove_from_list(file_to_close)):
                raise Exception("{} doesn't exist in checkboxes!".format(file_to_close))
        self.data_table.initialize(self.data_sets)

    def on_convert(self):
        export_dialog = QFileDialog()
        outfile = export_dialog.getSaveFileName(self, "Export to csv file",
                                                QDir.currentPath() + "/preview.csv",
                                                "csv (*.csv)")
        info = self.output_widget.output_info()
        min_rows = min([data.shape[0] for data in self.data_sets.values()])
        if (info["first_line"] < 1 or info["first_line"] > min_rows or
            info["last_line"] < 1 or info["last_line"] > min_rows or
            info["first_line"] > info["last_line"]):
            QMessageBox.critical(self, "Row range", "Invalid row range!")
        else:
            self.write_file(outfile[0], info["files"],
                            info["first_line"], info["last_line"])

    def write_file(self, out_file, in_files, first, last):
        res = pd.DataFrame()
        for in_file in in_files:
            data = self.data_sets[in_file]
            base_name = QFileInfo(in_file).baseName()
            if base_name in res:
                QMessageBox.warning(self, "Export",
                    "Identical file name! Use full path instead.")
                base_name = in_file
            for col in data.columns:
                res[base_name+":"+col] = data[col][first-1:last]
        res.to_csv(out_file, index=False)
        QMessageBox.information(self, "Export",
            "Selected rows have been written to {}.".format(out_file))
Esempio n. 20
0
    def __init__(self, path_to_rom=""):
        super(MainWindow, self).__init__()

        self.setWindowIcon(icon("foundry.ico"))

        file_menu = QMenu("File")

        open_rom_action = file_menu.addAction("&Open ROM")
        open_rom_action.triggered.connect(self.on_open_rom)
        self.open_m3l_action = file_menu.addAction("&Open M3L")
        self.open_m3l_action.triggered.connect(self.on_open_m3l)

        file_menu.addSeparator()

        self.save_rom_action = file_menu.addAction("&Save ROM")
        self.save_rom_action.triggered.connect(self.on_save_rom)
        self.save_rom_as_action = file_menu.addAction("&Save ROM as ...")
        self.save_rom_as_action.triggered.connect(self.on_save_rom_as)
        """
        file_menu.AppendSeparator()
        """
        self.save_m3l_action = file_menu.addAction("&Save M3L")
        self.save_m3l_action.triggered.connect(self.on_save_m3l)
        """
        file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "")
        """
        file_menu.addSeparator()
        settings_action = file_menu.addAction("&Settings")
        settings_action.triggered.connect(show_settings)
        file_menu.addSeparator()
        exit_action = file_menu.addAction("&Exit")
        exit_action.triggered.connect(lambda _: self.close())

        self.menuBar().addMenu(file_menu)
        """
        edit_menu = wx.Menu()

        edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "")
        edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "")
        edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "")
        edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "")
        edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "")
        edit_menu.AppendSeparator()
        edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "")
        edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "")
        """

        self.level_menu = QMenu("Level")

        self.select_level_action = self.level_menu.addAction("&Select Level")
        self.select_level_action.triggered.connect(self.open_level_selector)

        self.reload_action = self.level_menu.addAction("&Reload Level")
        self.reload_action.triggered.connect(self.reload_level)
        self.level_menu.addSeparator()
        self.edit_header_action = self.level_menu.addAction("&Edit Header")
        self.edit_header_action.triggered.connect(self.on_header_editor)
        self.edit_autoscroll = self.level_menu.addAction("Edit Autoscrolling")
        self.edit_autoscroll.triggered.connect(self.on_edit_autoscroll)

        self.menuBar().addMenu(self.level_menu)

        self.object_menu = QMenu("Objects")

        view_blocks_action = self.object_menu.addAction("&View Blocks")
        view_blocks_action.triggered.connect(self.on_block_viewer)
        view_objects_action = self.object_menu.addAction("&View Objects")
        view_objects_action.triggered.connect(self.on_object_viewer)
        self.object_menu.addSeparator()
        view_palettes_action = self.object_menu.addAction(
            "View Object Palettes")
        view_palettes_action.triggered.connect(self.on_palette_viewer)

        self.menuBar().addMenu(self.object_menu)

        self.view_menu = QMenu("View")
        self.view_menu.triggered.connect(self.on_menu)

        action = self.view_menu.addAction("Mario")
        action.setProperty(ID_PROP, ID_MARIO)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_mario"])

        action = self.view_menu.addAction("&Jumps on objects")
        action.setProperty(ID_PROP, ID_JUMP_OBJECTS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jump_on_objects"])

        action = self.view_menu.addAction("Items in blocks")
        action.setProperty(ID_PROP, ID_ITEM_BLOCKS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_items_in_blocks"])

        action = self.view_menu.addAction("Invisible items")
        action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_invisible_items"])

        action = self.view_menu.addAction("Autoscroll Path")
        action.setProperty(ID_PROP, ID_AUTOSCROLL)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_autoscroll"])

        self.view_menu.addSeparator()

        action = self.view_menu.addAction("Jump Zones")
        action.setProperty(ID_PROP, ID_JUMPS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jumps"])

        action = self.view_menu.addAction("&Grid lines")
        action.setProperty(ID_PROP, ID_GRID_LINES)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_grid"])

        action = self.view_menu.addAction("Resize Type")
        action.setProperty(ID_PROP, ID_RESIZE_TYPE)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_expansion"])

        self.view_menu.addSeparator()

        action = self.view_menu.addAction("&Block Transparency")
        action.setProperty(ID_PROP, ID_TRANSPARENCY)
        action.setCheckable(True)
        action.setChecked(SETTINGS["block_transparency"])

        self.view_menu.addSeparator()
        self.view_menu.addAction(
            "&Save Screenshot of Level").triggered.connect(self.on_screenshot)
        """
        self.view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "")
        self.view_menu.Append(ID_TOOLBAR, "&Toolbar", "")
        self.view_menu.AppendSeparator()
        self.view_menu.Append(ID_ZOOM, "&Zoom", "")
        self.view_menu.AppendSeparator()
        self.view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "")
        self.view_menu.Append(ID_PALETTE, "&Palette", "")
        self.view_menu.AppendSeparator()
        self.view_menu.Append(ID_MORE, "&More", "")
        """

        self.menuBar().addMenu(self.view_menu)

        help_menu = QMenu("Help")
        """
        help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "")
        help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "")
        help_menu.AppendSeparator()
        help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "")
        help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "")
        help_menu.AppendSeparator()
        """
        update_action = help_menu.addAction("Check for updates")
        update_action.triggered.connect(self.on_check_for_update)

        help_menu.addSeparator()

        video_action = help_menu.addAction("Feature Video on YouTube")
        video_action.triggered.connect(lambda: open_url(feature_video_link))

        github_action = help_menu.addAction("Github Repository")
        github_action.triggered.connect(lambda: open_url(github_link))

        discord_action = help_menu.addAction("SMB3 Rom Hacking Discord")
        discord_action.triggered.connect(lambda: open_url(discord_link))

        help_menu.addSeparator()

        about_action = help_menu.addAction("&About")
        about_action.triggered.connect(self.on_about)

        self.menuBar().addMenu(help_menu)

        self.block_viewer = None
        self.object_viewer = None

        self.level_ref = LevelRef()
        self.level_ref.data_changed.connect(self._on_level_data_changed)

        self.context_menu = ContextMenu(self.level_ref)
        self.context_menu.triggered.connect(self.on_menu)

        self.level_view = LevelView(self, self.level_ref, self.context_menu)

        self.scroll_panel = QScrollArea()
        self.scroll_panel.setWidgetResizable(True)
        self.scroll_panel.setWidget(self.level_view)

        self.setCentralWidget(self.scroll_panel)

        self.spinner_panel = SpinnerPanel(self, self.level_ref)
        self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in)
        self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out)
        self.spinner_panel.object_change.connect(self.on_spin)

        self.object_list = ObjectList(self, self.level_ref, self.context_menu)

        self.object_dropdown = ObjectDropdown(self)
        self.object_dropdown.object_selected.connect(
            self._on_placeable_object_selected)

        self.level_size_bar = LevelSizeBar(self, self.level_ref)
        self.enemy_size_bar = EnemySizeBar(self, self.level_ref)

        self.jump_list = JumpList(self, self.level_ref)
        self.jump_list.add_jump.connect(self.on_jump_added)
        self.jump_list.edit_jump.connect(self.on_jump_edit)
        self.jump_list.remove_jump.connect(self.on_jump_removed)

        splitter = QSplitter(self)
        splitter.setOrientation(Qt.Vertical)

        splitter.addWidget(self.object_list)
        splitter.setStretchFactor(0, 1)
        splitter.addWidget(self.jump_list)

        splitter.setChildrenCollapsible(False)

        level_toolbar = QToolBar("Level Info Toolbar", self)
        level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        level_toolbar.setOrientation(Qt.Horizontal)
        level_toolbar.setFloatable(False)

        level_toolbar.addWidget(self.spinner_panel)
        level_toolbar.addWidget(self.object_dropdown)
        level_toolbar.addWidget(self.level_size_bar)
        level_toolbar.addWidget(self.enemy_size_bar)
        level_toolbar.addWidget(splitter)

        level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea)

        self.addToolBar(Qt.RightToolBarArea, level_toolbar)

        self.object_toolbar = ObjectToolBar(self)
        self.object_toolbar.object_selected.connect(
            self._on_placeable_object_selected)

        object_toolbar = QToolBar("Object Toolbar", self)
        object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        object_toolbar.setFloatable(False)

        object_toolbar.addWidget(self.object_toolbar)
        object_toolbar.setAllowedAreas(Qt.LeftToolBarArea
                                       | Qt.RightToolBarArea)

        self.addToolBar(Qt.LeftToolBarArea, object_toolbar)

        self.menu_toolbar = QToolBar("Menu Toolbar", self)
        self.menu_toolbar.setOrientation(Qt.Horizontal)
        self.menu_toolbar.setIconSize(QSize(20, 20))

        self.menu_toolbar.addAction(
            icon("settings.svg"),
            "Editor Settings").triggered.connect(show_settings)
        self.menu_toolbar.addSeparator()
        self.menu_toolbar.addAction(
            icon("folder.svg"), "Open ROM").triggered.connect(self.on_open_rom)
        self.menu_toolbar.addAction(
            icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom)
        self.menu_toolbar.addSeparator()

        self.undo_action = self.menu_toolbar.addAction(icon("rotate-ccw.svg"),
                                                       "Undo Action")
        self.undo_action.triggered.connect(self.level_ref.undo)
        self.undo_action.setEnabled(False)
        self.redo_action = self.menu_toolbar.addAction(icon("rotate-cw.svg"),
                                                       "Redo Action")
        self.redo_action.triggered.connect(self.level_ref.redo)
        self.redo_action.setEnabled(False)

        self.menu_toolbar.addSeparator()
        play_action = self.menu_toolbar.addAction(icon("play-circle.svg"),
                                                  "Play Level")
        play_action.triggered.connect(self.on_play)
        play_action.setWhatsThis(
            "Opens an emulator with the current Level set to 1-1.\nSee Settings."
        )
        self.menu_toolbar.addSeparator()
        self.menu_toolbar.addAction(icon("zoom-out.svg"),
                                    "Zoom Out").triggered.connect(
                                        self.level_view.zoom_out)
        self.menu_toolbar.addAction(icon("zoom-in.svg"),
                                    "Zoom In").triggered.connect(
                                        self.level_view.zoom_in)
        self.menu_toolbar.addSeparator()
        header_action = self.menu_toolbar.addAction(icon("tool.svg"),
                                                    "Edit Level Header")
        header_action.triggered.connect(self.on_header_editor)
        header_action.setWhatsThis(
            "<b>Header Editor</b><br/>"
            "Many configurations regarding the level are done in its header, like the length of "
            "the timer, or where and how Mario enters the level.<br/>")

        self.jump_destination_action = self.menu_toolbar.addAction(
            icon("arrow-right-circle.svg"), "Go to Jump Destination")
        self.jump_destination_action.triggered.connect(
            self._go_to_jump_destination)
        self.jump_destination_action.setWhatsThis(
            "Opens the level, that can be reached from this one, e.g. by entering a pipe."
        )

        self.menu_toolbar.addSeparator()

        whats_this_action = QWhatsThis.createAction()
        whats_this_action.setWhatsThis(
            "Click on parts of the editor, to receive help information.")
        whats_this_action.setIcon(icon("help-circle.svg"))
        whats_this_action.setText("Starts 'What's this?' mode")
        self.menu_toolbar.addAction(whats_this_action)

        self.menu_toolbar.addSeparator()
        self.warning_list = WarningList(self, self.level_ref)

        warning_action = self.menu_toolbar.addAction(
            icon("alert-triangle.svg"), "Warning Panel")
        warning_action.setWhatsThis("Shows a list of warnings.")
        warning_action.triggered.connect(self.warning_list.show)
        warning_action.setDisabled(True)

        self.warning_list.warnings_updated.connect(warning_action.setEnabled)

        self.addToolBar(Qt.TopToolBarArea, self.menu_toolbar)

        self.status_bar = ObjectStatusBar(self, self.level_ref)
        self.setStatusBar(self.status_bar)

        self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self,
                                         self.remove_selected_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self,
                  self.level_ref.redo)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self,
                  self.level_view.zoom_in)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self,
                  self.level_view.zoom_out)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self,
                  self.level_view.select_all)

        self.on_open_rom(path_to_rom)

        self.showMaximized()
Esempio n. 21
0
class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, appctxt=None):
        super().__init__()
        
        self.appcontext = appctxt
        self.BASE_TITLE = 'PyMarkup'
        self.REFRESH_DELAY_MS = 200
        self.modified = False
        self.refresh_timer = QtCore.QTimer()
        self.refresh_timer.setSingleShot(True)
        self.refresh_timer.setInterval(self.REFRESH_DELAY_MS)
        self.refresh_timer.timeout.connect(self.Refresh)

        self.settings = self.LoadSettings()
        
        self.init_widgets()
        self.init_layout()
        self.init_mainmenu()

        self.python_path = None

        self.save_html = True # Always true unless disabled in debug mode
        
        try:
            self.css = load_css(self.GetPath('template_css'))
        except KeyError:
            self.css = ''

        self.LoadResources()

        # print('Singles:',len(self.singles.keys()))
        # self.singles = {}
        
        # print('Macros:',len(self.macros.keys()))
        self.macros = {}

        # print('Images:',len(self.images.keys()))
        self.images = {}

        self.subitems = {}

        try: self.LoadFileFromPath(self.settings['paths']['lastopened'])
        except KeyError: pass

        self.Refresh()


    def GetPath(self,key):

        default_names = {
            'template_css': 'template.css',
            'template_html': 'template.html',
            'singles': 'singles.csv',
            'table_products': 'singles.csv',
            'table_macros': 'macros.csv',
            'img_folder': 'img',
        }

        try:
            path = self.settings['paths'][key]
            if os.path.exists(path):
                # print('Returning path for {}: {}'.format(key,self.settings['paths'][key]))
                return path
        except KeyError:
            pass

        try: return os.path.join(self.settings['paths']['sysfolder'],default_names[key])
        except KeyError: pass

        return None


    # Load two tables and images
    def LoadResources(self):
        self.singles = read_csv(self.GetPath('table_products'))
        return
        self.subitems = get_subitems(self.singles)
        self.macros = read_csv_adv(self.GetPath('table_macros'))
        self.images = get_images(self.GetPath('img_folder'))
        self.ConsoleLog('Database aggiornato.')

    
    def ScheduleRefresh(self):
        # print('Refresh timer started for 100 ms')
        self.modified = True
        self.UpdateTitle()
        self.refresh_timer.start()


    def init_widgets(self):

        if ADD_WEBENGINE:
            self.browser = Browser(self)
            # self.browser.setZoomFactor(1.5) # Valid values: 0.25 to 5.0
            self.browser.setZoomFactor(self.settings['browserzoomfactor'])

        # self.texteditor = QtWidgets.QPlainTextEdit()
        self.texteditor = QCodeEditor()
        self.texteditor.setPlainText('cliente = "Asd"')
        # self.texteditor.setPlainText(self.settings['texteditor'])
        self.texteditor.zoomIn(self.settings['zoom_texteditor'])
        self.texteditor.textChanged.connect(self.ScheduleRefresh)

        self.console = QtWidgets.QPlainTextEdit()
        self.console.setReadOnly(True)
        self.console.zoomIn(self.settings['zoom_console'])


    def init_mainmenu(self):
        menubar = self.menuBar()
        # filemenu = menubar.addMenu('File')
        # viewmenu = menubar.addMenu('Visualizza')
        
        menus = {
            'File': [
                ['Nuovo file', 'Ctrl+N', self.NewFile],
                ['Apri...', 'Ctrl+O', self.ChooseFile],
                ['Salva', 'Ctrl+S', self.Save],
                ['Salva con nome...', 'Ctrl+Shift+S', self.SaveAs],
                [],
                ['Esporta PDF (preventivo)', 'Ctrl+E', self.RenderPDF_estimate],
                ['Esporta PDF (proforma)', 'Ctrl+T', self.RenderPDF_proforma],
                [],
                ['Chiudi', 'Ctrl+Shift+Q',          self.close],
            ],
            'Visualizza': [
                ['Ingrandisci editor',None,self.ZoomInEditor],
                ['Riduci editor',None,self.ZoomOutEditor],
                [],
                ['Ingrandisci anteprima',None,self.ZoomInBrowser],
                ['Riduci anteprima',None,self.ZoomOutBrowser],
            ],
            'Strumenti': [
                ['Aggiorna anteprima', 'Ctrl+R',    self.Refresh],
                ['Aggiorna database', 'Ctrl+L',     self.LoadResources],
                ['Impostazioni...', 'Ctrl+I',       self.OpenSettingsDialog],
            ]
        }

        if DEBUG:
            menus['Debug'] = [
                # ['Print pwd', 'Ctrl+P',          self.PrintPwd],
                ['Which Python',        None,   self.RunSubprocess],
                ['Print this folder',   None,   self.PrintThisFolder],
                ['Save html on',        None,   self.EnableSaveHTML],
                ['Save html off',       None,   self.DisableSaveHTML],
                ['Print settings',      None,   self.PrintSettings],
            ]

        for menu_name,entries in menus.items():
            menu = menubar.addMenu(menu_name)
            for data in entries:
                if len(data)>0:
                    label,shortcut,function = data
                    # label,shortcut,function = item
                    new_action = QAction(label,self)
                    if shortcut:
                        new_action.setShortcut(shortcut)
                    new_action.triggered.connect(function)
                    menu.addAction(new_action)
                else:
                    menu.addSeparator()

    def EnableSaveHTML(self):
        self.save_html = True;

    def DisableSaveHTML(self):
        self.save_html = False;

    def PrintSettings(self):
        settings = json.dumps(self.settings,indent=4)
        self.ConsoleLog(settings)

        paths = [
            'template_css',
            'template_html',
            'singles',
            'table_products',
            'table_macros',
            'img_folder',
        ]
        print('Paths:')
        for path in paths:
            print(self.GetPath(path))
        print('\nSettings:')
        print(settings)

    def PrintPwd(self):
        path = os.path.abspath('./')
        self.ConsoleLog(path)

    def PrintThisFolder(self):
        self.ConsoleLog(THIS_FOLDER);

    def ZoomInEditor(self):
        self.texteditor.zoomIn()

    def ZoomOutEditor(self):
        self.texteditor.zoomOut()

    def ZoomInBrowser(self):
        self.browser.setZoomFactor(self.browser.zoomFactor() + 0.1)

    def ZoomOutBrowser(self):
        self.browser.setZoomFactor(self.browser.zoomFactor() - 0.1)


    def Dummy(self):
        return


    def GetPythonPath(self):
        cmd = 'which python3'
        response = run(cmd, shell=True, stdout=PIPE, stderr=PIPE)
        return response.stdout.decode('utf-8')


    def RunSubprocess(self):

        self.ConsoleLog(self.GetPythonPath())

        '''
        cmd1 = '/Users/pc/Dev/pymarkup_installer_redux/local/sys_folder/xhtml2pdf.command'
        cmd2 = 'pwd > /Users/pc/Dev/pymarkup_installer_redux/local/sys_folder/pwd.txt'
        cmd = 'which python3'

        response = run(cmd, shell=True, stdout=PIPE, stderr=PIPE)
        #self.ConsoleLog(str(response))

        log = [cmd, response.returncode, response.stdout.decode('utf-8'), response.stderr]
        #log = [cmd, response]
        self.ConsoleLog('\n'.join([str(x) for x in log]))
        '''
        '''
        process = os.popen("which python")
        result = process.read()
        print(result)
        self.ConsoleLog(result)
        '''


    def ConfirmClose(self):
        # print('ConfirmClose')
        if self.modified:
            dialog = QMessageBox()
            # dialog.setIcon(QMessageBox.Question)
            dialog.setWindowTitle('Modifiche non salvate')
            dialog.setInformativeText(u"Abbandonare le modifiche in corso?")
            dialog.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
            choice = dialog.exec_()
            return choice == QMessageBox.Ok
        return True


    def ChooseFile(self):
        if self.ConfirmClose():
            chosen_path = QtWidgets.QFileDialog.getOpenFileName(self, 'Apri file', '', '*.toml',)
            if chosen_path[0] != '':
                self.settings['paths']['lastopened'] = chosen_path[0]
                self.LoadFileFromPath(chosen_path[0])
                self.modified = False
                self.UpdateTitle()


    def LoadFileFromPath(self,path):
        if os.path.exists(path):
            with open(path, 'r+') as file:
                self.texteditor.setPlainText(file.read())
            self.modified = False
            self.UpdateTitle()


    def NewFile(self):
        if self.ConfirmClose():
            self.modified = False
            self.settings['paths']['lastopened'] = ''
            self.setWindowTitle(self.BASE_TITLE+' - senza titolo')
            self.texteditor.setPlainText('')
            self.Refresh()
            self.DisplayHTML('')
            self.ConsoleLog('Nuovo file creato.')


    def Save(self):
        path = self.settings['paths']['lastopened']
        if os.path.exists(path):
            try:
                with open(path,'w+') as file:
                    file.write(self.texteditor.toPlainText())
                self.ConsoleLog('File salvato:\n'+path)
                self.modified = False
                self.UpdateTitle()
            except Exception as e:
                self.ConsoleLog(e)


    def SaveAs(self):

        path,_ = QtWidgets.QFileDialog().getSaveFileName(self, "Salva file", '', "Toml (*.toml)")
        if path != '':
            try:
                with open(path,'w+') as file:
                    file.write(self.texteditor.toPlainText())
                self.settings['paths']['lastopened'] = path
                self.modified = False
                self.ConsoleLog('File salvato:\n'+path)
                self.UpdateTitle()

            except Exception as e:
                print(str(e))
                self.ConsoleLog(str(e))


    def UpdateTitle(self):
        path = self.settings['paths']['lastopened']
        new_title = '{} - {}'.format(self.BASE_TITLE,path)
        if self.modified:
            new_title += ' (modificato)'
        self.setWindowTitle(new_title)


    def init_layout(self):
        self.setWindowTitle(self.BASE_TITLE)

        '''
        left_layout = QtWidgets.QVBoxLayout()
        left_layout.addWidget(self.texteditor)
        left_layout.addWidget(self.console)
        left_layout_widget = QtWidgets.QWidget()
        left_layout_widget.setLayout(left_layout)
        '''
        
        self.layout_left_splitter = QSplitter()
        self.layout_left_splitter.setOrientation(QtCore.Qt.Vertical)
        self.layout_left_splitter.addWidget(self.texteditor)
        self.layout_left_splitter.addWidget(self.console)

        self.layout_panes = QSplitter()
        self.layout_panes.addWidget(self.layout_left_splitter)

        if ADD_WEBENGINE:
            self.layout_panes.addWidget(self.browser)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.layout_panes)

        # Set size for left splitter
        for settings_key,splitter in [
            ['left_splitter_sizes', self.layout_left_splitter],
            ['splitter_sizes',      self.layout_panes],
        ]:
            splitter_size = self.settings.get(settings_key)
            if splitter_size is not None:
                splitter.setSizes(splitter_size)

        # Set size for main splitter
        # splitter_sizes = self.settings.get('splitter_sizes')
        # if splitter_sizes is not None:
        #     self.layout_panes.setSizes(splitter_sizes)

        centralWidget = QtWidgets.QWidget()
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)

        self.setGeometry(*self.settings['geometry'])


    def RenderHTML(self, **kwargs):
        
        toml_text = self.texteditor.toPlainText()
        toml_model = read_toml(toml_text, css=self.css)
        order_model = merge(toml_model, self.singles, self.subitems, self.macros, self.images)
        # print('TOML merge OK')
        
        template_path = 'template.html'
        # print('Template path:',template_path)
        path_html  = self.GetPath('template_html')
        path_css  = self.GetPath('template_css')
        return render_html(order_model, path_html=path_html, path_css=path_css, **kwargs)


    def RenderPDF(self, **kwargs):

        folder,filename = os.path.split(self.settings['paths']['lastopened'])
        name,_ = os.path.splitext(filename)
        defaultpath = os.path.join(folder,name+'.pdf')
        # print('Last opened folder/filename:',folder)

        savepath,_ = QtWidgets.QFileDialog().getSaveFileName(self, "Salva PDF", defaultpath, "PDF (*.pdf)")
        if savepath == '':
            return
        
        path_sys = self.settings['paths']['sysfolder']
        html = self.RenderHTML(**kwargs)

        htmlpath = os.path.join(path_sys,'tmp.html')

        # Save 'tmp.html'
        # MAY BE DISABLED FOR DEBUGGING
        if self.save_html:
            with open(htmlpath, 'w+', encoding='UTF-8') as file:
                file.write(html)

        sys_folder = self.settings['paths']['sysfolder']
        #path_script = os.path.join(self.settings['paths']['renderpdf'], 'Contents/MacOS/renderpdf')
        path_script = os.path.join(sys_folder, 'renderpdf.py')
        #self.ConsoleLog(html)

        print('-----')
        print('RenderPDF paths:')
        print(path_script, os.path.exists(path_script))
        print(htmlpath, os.path.exists(htmlpath))
        print(savepath)
        print('-----')

        #render_pdf(path_script,htmlpath,savepath)
        #python_path = '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3'

        python_path = self.settings['paths']['python']
        command = f'{python_path} "{path_script}" --args "{savepath}"'
        
        #command = f'xhtml2pdf {htmlpath} {savepath}'

        '''
        cmd_lines = [
            'from xhtml2pdf import pisa',
            f'resultFile = open("{savepath}", "w+b")',
            f'htmlFile = open("{htmlpath}","r+")',
            f'pisa.CreatePDF(htmlFile.read(),dest=resultFile)',
            'resultFile.close()',
            'htmlFile.close()',
        ]
        command = "python3 -c '" + ';'.join(cmd_lines) + "'"
        '''

        #command = f'python3 "{path_script}" "{savepath}"'

        # Path to renderpdf.command (as a proxy to execute renderpdf.py)
        #command = os.path.join(sys_folder, 'xhtml2pdf.command')

        print('Calling command:')
        print(command)
        print('-----')
        try:
            #result = call(command,shell=True) # shell=True is needed because subprocess.call is just a wrapper for Popen
            result = call(command, shell=True)
            self.ConsoleLog(command)
            #self.ConsoleLog('Xhtml2 call result: '+str(result))
            #self.ConsoleLog('PDF salvato: '+savepath)
        except FileNotFoundError as e:
            print('Subprocess.call raised an exception:')
            print(e)
            self.ConsoleLog(str(e))
        except Exception as e:
            self.ConsoleLog(str(e))
            with open(os.path.join(sys_folder,'applog.txt'),'r+') as file:
                file.write(str(e))

        # os.startfile('rendered.pdf')


    def RenderPDF_estimate(self):
        self.RenderPDF(preview=False,render_estimate=True)

        
    def RenderPDF_proforma(self):
        self.RenderPDF(preview=False,render_proforma=True)


    def DisplayHTML(self, html):
        # print('Display HTML')
        self.browser.SetHTML(html)


    def Refresh(self):
        # print('Refresh')
        try:
            html = self.RenderHTML(preview=True, render_estimate=True)
            self.DisplayHTML(html)
            self.ConsoleLog('OK')
            # self.ConsoleLog('Render HTML OK. Html length: {}\n{}'.format(len(html),html))
        except Exception as e:
            print(e)
            self.ConsoleLog(str(e))


    def ConsoleLog(self,msg):
        self.console.setPlainText(msg)


    def LoadSettings(self):
        default_settings = {
            'geometry': [500,300,1400,1000],
            # 'texteditor': '',
            'splitter_sizes': None,
            'left_splitter_sizes': None,
            'browserzoomfactor': 1,
            'paths': {
                'lastopened': '',
                'python': '/Library/Frameworks/Python.framework/Versions/3.6/bin/python3',
            },
            'zoom_texteditor': 2,
            'zoom_console': 2,
        }

        qsettings = QtCore.QSettings('Company','Appname')
        jsontext = qsettings.value('settings',None)
        # print('Json settings from QSettings:',jsontext)

        settings = json.loads(jsontext) if jsontext else {}
        for key in default_settings.keys():
            if key in settings:
                default_settings[key] = settings[key]

        # Check all paths and delete them if they are broken
        for key,path in default_settings['paths'].items():
            if not os.path.exists(path):
                default_settings['paths'][key] = ''

        return default_settings


    def SaveSettings(self):
        # print('Saving settings')
        
        g = self.geometry()
        self.settings['geometry'] = [g.x(), g.y() ,g.width(), g.height()]
        # self.settings['texteditor'] = self.texteditor.toPlainText()
        self.settings['splitter_sizes'] = self.layout_panes.sizes()
        self.settings['left_splitter_sizes'] = self.layout_left_splitter.sizes()
        self.settings['browserzoomfactor'] = self.browser.zoomFactor()

        qsettings = QtCore.QSettings('Company','Appname')
        qsettings.setValue('settings', json.dumps(self.settings))


    def OpenSettingsDialog(self):
        dialog = SettingsDialog(self)
        dialog.exec_()


    def closeEvent(self,event):
        # print('closeEvent')
        if self.ConfirmClose():
            event.ignore()
            self.SaveSettings()
            # print('Closing program (settings saved)')
            event.accept()
        else:
            event.ignore()


    '''
Esempio n. 22
0
    def __init__(self, path_to_rom=""):
        super(MainWindow, self).__init__()

        self.setWindowIcon(icon("foundry.ico"))

        file_menu = QMenu("File")

        open_rom_action = file_menu.addAction("&Open ROM")
        open_rom_action.triggered.connect(self.on_open_rom)
        self.open_m3l_action = file_menu.addAction("&Open M3L")
        self.open_m3l_action.triggered.connect(self.on_open_m3l)

        file_menu.addSeparator()

        save_rom_action = file_menu.addAction("&Save ROM")
        save_rom_action.triggered.connect(self.on_save_rom)
        save_rom_as_action = file_menu.addAction("&Save ROM as ...")
        save_rom_as_action.triggered.connect(self.on_save_rom_as)
        """
        file_menu.AppendSeparator()
        """
        self.save_m3l_action = file_menu.addAction("&Save M3L")
        self.save_m3l_action.triggered.connect(self.on_save_m3l)
        """
        file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "")
        """
        file_menu.addSeparator()
        settings_action = file_menu.addAction("&Settings")
        settings_action.triggered.connect(show_settings)
        file_menu.addSeparator()
        exit_action = file_menu.addAction("&Exit")
        exit_action.triggered.connect(lambda _: self.close())

        self.menuBar().addMenu(file_menu)
        """
        edit_menu = wx.Menu()

        edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "")
        edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "")
        edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "")
        edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "")
        edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "")
        edit_menu.AppendSeparator()
        edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "")
        edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "")
        """

        level_menu = QMenu("Level")

        select_level_action = level_menu.addAction("&Select Level")
        select_level_action.triggered.connect(self.open_level_selector)
        """
        level_menu.Append(ID_GOTO_NEXT_AREA, "&Go to next Area", "")
        level_menu.AppendSeparator()
        """
        self.reload_action = level_menu.addAction("&Reload Level")
        self.reload_action.triggered.connect(self.reload_level)
        level_menu.addSeparator()
        self.edit_header_action = level_menu.addAction("&Edit Header")
        self.edit_header_action.triggered.connect(self.on_header_editor)
        """
        level_menu.Append(ID_EDIT_POINTERS, "&Edit Pointers", "")
        """

        self.menuBar().addMenu(level_menu)

        object_menu = QMenu("Objects")

        view_blocks_action = object_menu.addAction("&View Blocks")
        view_blocks_action.triggered.connect(self.on_block_viewer)
        view_objects_action = object_menu.addAction("&View Objects")
        view_objects_action.triggered.connect(self.on_object_viewer)
        """
        object_menu.AppendSeparator()
        object_menu.Append(ID_CLONE_OBJECT_ENEMY, "&Clone Object/Enemy", "")
        object_menu.AppendSeparator()
        object_menu.Append(ID_ADD_3_BYTE_OBJECT, "&Add 3 Byte Object", "")
        object_menu.Append(ID_ADD_4_BYTE_OBJECT, "&Add 4 Byte Object", "")
        object_menu.Append(ID_ADD_ENEMY, "&Add Enemy", "")
        object_menu.AppendSeparator()
        object_menu.Append(ID_DELETE_OBJECT_ENEMY, "&Delete Object/Enemy", "")
        object_menu.Append(ID_DELETE_ALL, "&Delete All", "")
        """

        self.menuBar().addMenu(object_menu)

        view_menu = QMenu("View")
        view_menu.triggered.connect(self.on_menu)

        action = view_menu.addAction("Mario")
        action.setProperty(ID_PROP, ID_MARIO)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_mario"])

        action = view_menu.addAction("&Jumps on objects")
        action.setProperty(ID_PROP, ID_JUMP_OBJECTS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jump_on_objects"])

        action = view_menu.addAction("Items in blocks")
        action.setProperty(ID_PROP, ID_ITEM_BLOCKS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_items_in_blocks"])

        action = view_menu.addAction("Invisible items")
        action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_invisible_items"])

        view_menu.addSeparator()

        action = view_menu.addAction("Jump Zones")
        action.setProperty(ID_PROP, ID_JUMPS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jumps"])

        action = view_menu.addAction("&Grid lines")
        action.setProperty(ID_PROP, ID_GRID_LINES)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_grid"])

        action = view_menu.addAction("Resize Type")
        action.setProperty(ID_PROP, ID_RESIZE_TYPE)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_expansion"])

        view_menu.addSeparator()

        action = view_menu.addAction("&Block Transparency")
        action.setProperty(ID_PROP, ID_TRANSPARENCY)
        action.setCheckable(True)
        action.setChecked(SETTINGS["block_transparency"])

        view_menu.addSeparator()
        view_menu.addAction("&Save Screenshot of Level").triggered.connect(
            self.on_screenshot)
        """
        view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "")
        view_menu.Append(ID_TOOLBAR, "&Toolbar", "")
        view_menu.AppendSeparator()
        view_menu.Append(ID_ZOOM, "&Zoom", "")
        view_menu.AppendSeparator()
        view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "")
        view_menu.Append(ID_PALETTE, "&Palette", "")
        view_menu.AppendSeparator()
        view_menu.Append(ID_MORE, "&More", "")
        """

        self.menuBar().addMenu(view_menu)

        help_menu = QMenu("Help")
        """
        help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "")
        help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "")
        help_menu.AppendSeparator()
        help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "")
        help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "")
        help_menu.AppendSeparator()
        """
        update_action = help_menu.addAction("Check for updates")
        update_action.triggered.connect(self.on_check_for_update)

        help_menu.addSeparator()

        video_action = help_menu.addAction("Feature Video on YouTube")
        video_action.triggered.connect(lambda: open_url(feature_video_link))

        discord_action = help_menu.addAction("SMB3 Rom Hacking Discord")
        discord_action.triggered.connect(lambda: open_url(discord_link))

        help_menu.addSeparator()

        about_action = help_menu.addAction("&About")
        about_action.triggered.connect(self.on_about)

        self.menuBar().addMenu(help_menu)

        self.level_selector = LevelSelector(parent=self)

        self.block_viewer = None
        self.object_viewer = None

        self.level_ref = LevelRef()
        self.level_ref.data_changed.connect(self._on_level_data_changed)

        self.context_menu = ContextMenu(self.level_ref)
        self.context_menu.triggered.connect(self.on_menu)

        self.level_view = LevelView(self, self.level_ref, self.context_menu)

        self.scroll_panel = QScrollArea()
        self.scroll_panel.setWidgetResizable(True)
        self.scroll_panel.setWidget(self.level_view)

        self.setCentralWidget(self.scroll_panel)

        self.spinner_panel = SpinnerPanel(self, self.level_ref)
        self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in)
        self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out)
        self.spinner_panel.object_change.connect(self.on_spin)

        self.object_list = ObjectList(self, self.level_ref, self.context_menu)

        self.object_dropdown = ObjectDropdown(self)
        self.object_dropdown.object_selected.connect(
            self._on_placeable_object_selected)

        self.level_size_bar = LevelSizeBar(self, self.level_ref)
        self.enemy_size_bar = EnemySizeBar(self, self.level_ref)

        self.jump_list = JumpList(self, self.level_ref)
        self.jump_list.add_jump.connect(self.on_jump_added)
        self.jump_list.edit_jump.connect(self.on_jump_edit)
        self.jump_list.remove_jump.connect(self.on_jump_removed)

        splitter = QSplitter(self)
        splitter.setOrientation(Qt.Vertical)

        splitter.addWidget(self.object_list)
        splitter.setStretchFactor(0, 1)
        splitter.addWidget(self.jump_list)

        splitter.setChildrenCollapsible(False)

        level_toolbar = QToolBar("Level Info Toolbar", self)
        level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        level_toolbar.setOrientation(Qt.Horizontal)
        level_toolbar.setFloatable(False)

        level_toolbar.addWidget(self.spinner_panel)
        level_toolbar.addWidget(self.object_dropdown)
        level_toolbar.addWidget(self.level_size_bar)
        level_toolbar.addWidget(self.enemy_size_bar)
        level_toolbar.addWidget(splitter)

        level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea)

        self.addToolBar(Qt.RightToolBarArea, level_toolbar)

        self.object_toolbar = ObjectToolBar(self)
        self.object_toolbar.object_selected.connect(
            self._on_placeable_object_selected)

        object_toolbar = QToolBar("Object Toolbar", self)
        object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        object_toolbar.setFloatable(False)

        object_toolbar.addWidget(self.object_toolbar)
        object_toolbar.setAllowedAreas(Qt.LeftToolBarArea
                                       | Qt.RightToolBarArea)

        self.addToolBar(Qt.LeftToolBarArea, object_toolbar)

        menu_toolbar = QToolBar("Menu Toolbar", self)
        menu_toolbar.setOrientation(Qt.Horizontal)
        menu_toolbar.setIconSize(QSize(20, 20))

        menu_toolbar.addAction(
            icon("settings.svg"),
            "Editor Settings").triggered.connect(show_settings)
        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("folder.svg"),
                               "Open ROM").triggered.connect(self.on_open_rom)
        menu_toolbar.addAction(
            icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom)
        menu_toolbar.addSeparator()

        self.undo_action = menu_toolbar.addAction(icon("rotate-ccw.svg"),
                                                  "Undo Action")
        self.undo_action.triggered.connect(self.level_ref.undo)
        self.undo_action.setEnabled(False)
        self.redo_action = menu_toolbar.addAction(icon("rotate-cw.svg"),
                                                  "Redo Action")
        self.redo_action.triggered.connect(self.level_ref.redo)
        self.redo_action.setEnabled(False)

        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("play-circle.svg"),
                               "Play Level").triggered.connect(self.on_play)
        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("zoom-out.svg"),
                               "Zoom Out").triggered.connect(
                                   self.level_view.zoom_out)
        menu_toolbar.addAction(icon("zoom-in.svg"),
                               "Zoom In").triggered.connect(
                                   self.level_view.zoom_in)
        menu_toolbar.addSeparator()
        menu_toolbar.addAction(icon("tool.svg"),
                               "Edit Level Header").triggered.connect(
                                   self.on_header_editor)
        self.jump_destination_action = menu_toolbar.addAction(
            icon("arrow-right-circle.svg"), "Go to Jump Destination")
        self.jump_destination_action.triggered.connect(
            self._go_to_jump_destination)
        menu_toolbar.addSeparator()
        # menu_toolbar.addAction(icon("help-circle.svg"), "What's this?")

        self.addToolBar(Qt.TopToolBarArea, menu_toolbar)

        self.status_bar = ObjectStatusBar(self, self.level_ref)
        self.setStatusBar(self.status_bar)

        self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self,
                                         self.remove_selected_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self,
                  self.level_ref.redo)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self,
                  self.level_view.zoom_in)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self,
                  self.level_view.zoom_out)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self,
                  self.level_view.select_all)

        if not self.on_open_rom(path_to_rom):
            self.deleteLater()

        self.showMaximized()
Esempio n. 23
0
    webView = MyWebView(tabWdgt)
    webView.load(QUrl("http://www.ifeng.com"))
    tabWdgt.createTabItem(webView, '首页')

    # 嵌入surpac界面
    pname = 'surpac2'
    cmd = "C:/Program Files (x86)/GEOVIA/Surpac/69/nt_i386/bin/surpac2.exe"
    spTitle = 'GEOVIA Surpac'
    pids = MySurpac.getPidsFromPName(pname)
    MySurpac.killProcess(pids)
    pid = MySurpac.startProcess(cmd)
    hwnd = MySurpac.getTheMainWindow(pid, spTitle)
    ports = MySurpac.getPortsFromPid(pid)
    surpacWdgt = MySurpac.convertWndToWidget(hwnd)

    # 嵌入Tree界面
    treeWdgt = MyTreeWidget(ports[0])

    # 分割窗口
    splitter = QSplitter()
    splitter.setOrientation(Qt.Horizontal)
    splitter.addWidget(surpacWdgt)
    splitter.addWidget(treeWdgt)
    tabWdgt.createTabItem(splitter, '三维设计')

    mainWindow.setCentralWidget(tabWdgt)
    mainWindow.showMaximized()

    # 退出应用
    sys.exit(app.exec_())