class CustomSplitter(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.splitter = QSplitter(self)
        self.splitter.addWidget(QTextEdit(self))
        self.splitter.addWidget(QTextEdit(self))
        layout = QVBoxLayout(self)
        layout.addWidget(self.splitter)
        handle = self.splitter.handle(1)
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        button = QToolButton(handle)
        button.setArrowType(Qt.LeftArrow)
        button.clicked.connect(lambda: self.handleSplitterButton(True))
        layout.addWidget(button)
        button = QToolButton(handle)
        button.setArrowType(Qt.RightArrow)
        button.clicked.connect(lambda: self.handleSplitterButton(False))
        layout.addWidget(button)
        handle.setLayout(layout)

    def handleSplitterButton(self, left=True):
        if not all(self.splitter.sizes()):
            self.splitter.setSizes([1, 1])
        elif left:
            self.splitter.setSizes([0, 1])
        else:
            self.splitter.setSizes([1, 0])
Exemple #2
0
class MainWidget(QWidget):
    def __init__(self, parent: QWidget, model: Model) -> None:
        super().__init__(parent)

        logger.add(self.log)

        self.mainlayout = QVBoxLayout()
        self.setLayout(self.mainlayout)

        self.splitter = QSplitter(Qt.Vertical)

        self.stack = QStackedWidget()
        self.splitter.addWidget(self.stack)

        # mod list widget

        self.modlistwidget = QWidget()
        self.modlistlayout = QVBoxLayout()
        self.modlistlayout.setContentsMargins(0, 0, 0, 0)
        self.modlistwidget.setLayout(self.modlistlayout)
        self.stack.addWidget(self.modlistwidget)

        # search bar

        self.searchbar = QLineEdit()
        self.searchbar.setPlaceholderText('Search...')
        self.modlistlayout.addWidget(self.searchbar)

        # mod list

        self.modlist = ModList(self, model)
        self.modlistlayout.addWidget(self.modlist)

        self.searchbar.textChanged.connect(lambda e: self.modlist.setFilter(e))

        # welcome message

        welcomelayout = QVBoxLayout()
        welcomelayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        welcomewidget = QWidget()
        welcomewidget.setLayout(welcomelayout)
        welcomewidget.dragEnterEvent = self.modlist.dragEnterEvent  # type: ignore
        welcomewidget.dragMoveEvent = self.modlist.dragMoveEvent  # type: ignore
        welcomewidget.dragLeaveEvent = self.modlist.dragLeaveEvent  # type: ignore
        welcomewidget.dropEvent = self.modlist.dropEvent  # type: ignore
        welcomewidget.setAcceptDrops(True)

        icon = QIcon(str(getRuntimePath('resources/icons/open-folder.ico')))
        iconpixmap = icon.pixmap(32, 32)
        icon = QLabel()
        icon.setPixmap(iconpixmap)
        icon.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        icon.setContentsMargins(4, 4, 4, 4)
        welcomelayout.addWidget(icon)

        welcome = QLabel('''<p><font>
            No mod installed yet.
            Drag a mod into this area to get started!
            </font></p>''')
        welcome.setAttribute(Qt.WA_TransparentForMouseEvents)
        welcome.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        welcomelayout.addWidget(welcome)

        self.stack.addWidget(welcomewidget)

        # output log

        self.output = QTextEdit(self)
        self.output.setTextInteractionFlags(Qt.NoTextInteraction)
        self.output.setReadOnly(True)
        self.output.setContextMenuPolicy(Qt.NoContextMenu)
        self.output.setPlaceholderText('Program output...')
        self.splitter.addWidget(self.output)

        # TODO: enhancement: add a launch game icon
        # TODO: enhancement: show indicator if scripts have to be merged

        self.splitter.setStretchFactor(0, 1)
        self.splitter.setStretchFactor(1, 0)
        self.mainlayout.addWidget(self.splitter)

        if len(model):
            self.stack.setCurrentIndex(0)
            self.splitter.setSizes([self.splitter.size().height(), 50])
        else:
            self.stack.setCurrentIndex(1)
            self.splitter.setSizes([self.splitter.size().height(), 0])
        model.updateCallbacks.append(self.modelUpdateEvent)

    def keyPressEvent(self, event: QKeyEvent) -> None:
        if event.key() == Qt.Key_Escape:
            self.modlist.setFocus()
            self.searchbar.setText('')
        elif event.matches(QKeySequence.Find):
            self.searchbar.setFocus()
        elif event.matches(QKeySequence.Paste):
            self.pasteEvent()
        super().keyPressEvent(event)

    def pasteEvent(self) -> None:
        clipboard = QApplication.clipboard().text().splitlines()
        if len(clipboard) == 1 and isValidNexusModsUrl(clipboard[0]):
            self.parentWidget().showDownloadModDialog()
        else:
            urls = [
                url for url in QApplication.clipboard().text().splitlines()
                if len(str(url.strip()))
            ]
            if all(
                    isValidModDownloadUrl(url) or isValidFileUrl(url)
                    for url in urls):
                asyncio.create_task(self.modlist.checkInstallFromURLs(urls))

    def modelUpdateEvent(self, model: Model) -> None:
        if len(model) > 0:
            if self.stack.currentIndex() != 0:
                self.stack.setCurrentIndex(0)
                self.repaint()
        else:
            if self.stack.currentIndex() != 1:
                self.stack.setCurrentIndex(1)
                self.repaint()

    def unhideOutput(self) -> None:
        if self.splitter.sizes()[1] < 10:
            self.splitter.setSizes([self.splitter.size().height(), 50])

    def unhideModList(self) -> None:
        if self.splitter.sizes()[0] < 10:
            self.splitter.setSizes([50, self.splitter.size().height()])

    def log(self, message: Any) -> None:
        # format log messages to user readable output
        settings = QSettings()

        record = message.record
        message = record['message']
        extra = record['extra']
        level = record['level'].name.lower()

        name = str(extra['name']
                   ) if 'name' in extra and extra['name'] is not None else ''
        path = str(extra['path']
                   ) if 'path' in extra and extra['path'] is not None else ''
        dots = bool(
            extra['dots']
        ) if 'dots' in extra and extra['dots'] is not None else False
        newline = bool(
            extra['newline']
        ) if 'newline' in extra and extra['newline'] is not None else False
        output = bool(
            extra['output']
        ) if 'output' in extra and extra['output'] is not None else bool(
            message)
        modlist = bool(
            extra['modlist']
        ) if 'modlist' in extra and extra['modlist'] is not None else False

        if level in ['debug'
                     ] and settings.value('debugOutput', 'False') != 'True':
            if newline:
                self.output.append(f'')
            return

        n = '<br>' if newline else ''
        d = '...' if dots else ''
        if len(name) and len(path):
            path = f' ({path})'

        if output:
            message = html.escape(message, quote=True)

            if level in ['success', 'error', 'warning']:
                message = f'<strong>{message}</strong>'
            if level in ['success']:
                message = f'<font color="#04c45e">{message}</font>'
            if level in ['error', 'critical']:
                message = f'<font color="#ee3b3b">{message}</font>'
            if level in ['warning']:
                message = f'<font color="#ff6500">{message}</font>'
            if level in ['debug', 'trace']:
                message = f'<font color="#aaa">{message}</font>'
                path = f'<font color="#aaa">{path}</font>' if path else ''
                d = f'<font color="#aaa">{d}</font>' if d else ''

            time = record['time'].astimezone(
                tz=None).strftime('%Y-%m-%d %H:%M:%S')
            message = f'<font color="#aaa">{time}</font> {message}'
            self.output.append(
                f'{n}{message.strip()}{" " if name or path else ""}{name}{path}{d}'
            )
        else:
            self.output.append(f'')

        self.output.verticalScrollBar().setValue(
            self.output.verticalScrollBar().maximum())
        self.output.repaint()

        if modlist:
            self.unhideModList()
        if settings.value('unhideOutput', 'True') == 'True' and output:
            self.unhideOutput()
Exemple #3
0
# ---------------------------
# Splitterの左右の比率を変える
# ---------------------------
import sys
from PySide2.QtWidgets import QApplication, QSplitter, QTextEdit

app = QApplication(sys.argv)

qw_text_edit_left = QTextEdit()
qw_text_edit_left.append('left')

qw_text_edit_right = QTextEdit()
qw_text_edit_right.append('right')

qw_splitter = QSplitter()  # Orientationの初期値は水平
qw_splitter.addWidget(qw_text_edit_left)
qw_splitter.addWidget(qw_text_edit_right)

qw_splitter_size = qw_splitter.size()  # Splitterのサイズを取得する
qw_splitter_size_width = qw_splitter_size.width()  # Splitterの横サイズを取得する
qw_splitter.setSizes(
    [qw_splitter_size_width * 0.1,
     qw_splitter_size_width * 0.9])  # Splitterの横の比率を1:9に変更する
print(qw_splitter.size())  # Splitter全体のサイズ
print(qw_splitter.sizes())  # Splitterの子widgetごとのサイズ

qw_splitter.show()

sys.exit(app.exec_())
Exemple #4
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()


    '''
Exemple #5
0
class TableWidget(QWidget):
    def __init__(self, db, name, select, update_ui):
        super().__init__()
        self.db = db
        self.setWindowTitle(name)
        self.dirty = False
        self.make_widgets(select)
        self.make_layout()
        self.make_connections(update_ui)

    def make_widgets(self, select):
        self.sqlEdit = SQLEdit.SQLEdit(select)
        self.sqlEdit.setTabChangesFocus(True)
        self.tableModel = TableModel.TableModel(self.db,
                                                Sql.uncommented(select))
        self.tableView = QTableView()
        self.tableView.setModel(self.tableModel)
        self.statusLabel = QLabel()
        self.statusLabel.setTextFormat(Qt.RichText)
        self.update_status(select)

    def make_layout(self):
        self.splitter = QSplitter(Qt.Vertical)
        self.splitter.addWidget(self.sqlEdit)
        self.splitter.addWidget(self.tableView)
        self.splitter.setStretchFactor(1, 11)
        vbox = QVBoxLayout()
        vbox.addWidget(self.splitter)
        vbox.addWidget(self.statusLabel)
        self.setLayout(vbox)

    def make_connections(self, update_ui):
        self.sqlEdit.textChanged.connect(update_ui)
        self.sqlEdit.copyAvailable.connect(update_ui)
        self.tableModel.sql_error.connect(self.on_sql_error)

    @property
    def sizes(self):
        return self.splitter.sizes()

    @property
    def is_select(self):
        return Sql.is_select(self.sql)

    @property
    def sql(self):
        return self.sqlEdit.toPlainText()

    def refresh(self):
        if not self.is_select:
            self.statusLabel.setText('<font color=red>Only SELECT '
                                     'statements are supported here</font>')
        else:
            select = Sql.uncommented(self.sqlEdit.toPlainText())
            if re.match(r'\s*SELECT(:?\s+(:?ALL|DISTINCT))?\s+\*', select,
                        re.IGNORECASE | re.DOTALL):
                try:
                    names = ', '.join([
                        Sql.quoted(name)
                        for name in self.db.field_names_for_select(select)
                    ])
                    select = select.replace('*', names, 1)
                except apsw.SQLError as err:
                    self.on_sql_error(str(err))
                    return
            self.tableModel.refresh(select)
            self.update_status(select)

    def on_sql_error(self, err):
        self.statusLabel.setText(f'<font color=red>{err}</font>')

    def update_status(self, select):
        try:
            count = self.db.select_row_count(select)
            s = 's' if count != 1 else ''
            self.statusLabel.setText(f'{count:,} row{s}')
        except (apsw.SQLError, Sql.Error) as err:
            self.on_sql_error(str(err))

    def closeEvent(self, event):
        self.save(closing=True)
        event.accept()

    def save(self, *, closing=False):
        print(f'TableWidget.save dirty={self.dirty} closing={closing}')
        saved = False
        errors = False
        if self.dirty and bool(self.db):
            # TODO save change to list view or form view
            errors = []  # self.db.save_...
            if errors:
                if not closing:
                    error = '\n'.join(errors)
                    QMessageBox.warning(self, f'Save error — {APPNAME}',
                                        f'Failed to save:\n{error}')
            else:
                saved = True
                self.dirty = False
        return saved