Add initial configuration window

Julien LepillerSat Dec 12 21:53:51+0100 2020

9d6955e

Add initial configuration window

offlate/core/manager.py

136136
    def getProject(self, name):
137137
        return self.project_list[name]
138138
139+
    def _mergeData(self, data, update):
140+
        if isinstance(update, dict):
141+
            for k in update:
142+
                if k in data:
143+
                    data[k] = self._mergeData(data[k], update[k])
144+
                else:
145+
                    data[k] = update[k]
146+
            return data
147+
        else:
148+
            return update
149+
139150
    def updateSettings(self, data=None):
140151
        if data == None:
141-
            self.settings.conf = data
142152
            self.settings.update()
143153
        else:
154+
            self.settings.conf = self._mergeData(self.settings.conf, data)
144155
            self.settings.write()
145156
146-
    def getConf(self):
147-
        return self.settings.conf
157+
    def getConf(self, keys):
158+
        conf = self.settings.conf
159+
        for k in keys[:-1]:
160+
            if not k in conf:
161+
                conf[k] = {}
162+
            conf = conf[k]
163+
        if not keys[-1] in conf:
164+
            return None
165+
        return conf[keys[-1]]
148166
149167
    def remove(self, name):
150168
        rmdir(self.basedir + '/' + name)

170188
        for s in self.settings.conf['Generic'].keys():
171189
            settings[s] = self.settings.conf['Generic'][s]
172190
        return system['system'].isConfigured(settings)
191+
192+
    def isNew(self):
193+
        if not 'Generic' in self.settings.conf:
194+
            self.settings.conf['Generic'] = {}
195+
        if not 'new' in self.settings.conf['Generic']:
196+
            self.settings.conf['Generic']['new'] = 'True'
197+
        return self.settings.conf['Generic']['new'] == 'True'
198+
199+
    def setNotNew(self):
200+
        self.settings.conf['Generic']['new'] = 'False'
201+
        self.settings.write()

offlate/ui/config/settings.py unknown status 1

1+
#   Copyright (c) 2019 Julien Lepiller <julien@lepiller.eu>
2+
#
3+
#   This program is free software: you can redistribute it and/or modify
4+
#   it under the terms of the GNU Affero General Public License as
5+
#   published by the Free Software Foundation, either version 3 of the
6+
#   License, or (at your option) any later version.
7+
#
8+
#   This program is distributed in the hope that it will be useful,
9+
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11+
#   GNU Affero General Public License for more details.
12+
#
13+
#   You should have received a copy of the GNU Affero General Public License
14+
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
15+
####
16+
17+
import os
18+
import re
19+
import sys
20+
21+
from PyQt5.QtWidgets import *
22+
from PyQt5.QtGui import *
23+
from PyQt5.QtCore import *
24+
25+
from ...systems.list import systems
26+
from ...core.manager import ProjectManager
27+
from ...core.config import *
28+
from ..listsettingsedit import ListSettingsEdit
29+
30+
class SettingsLineEdit(QLineEdit):
31+
    def content(self):
32+
        return self.text()
33+
34+
    def setContent(self, value):
35+
        self.setText(value)
36+
37+
class SettingsWindow(QMainWindow):
38+
    def __init__(self, manager):
39+
        super().__init__()
40+
        self.manager = manager
41+
        self.initUI()
42+
43+
    def initUI(self):
44+
        center = QDesktopWidget().availableGeometry().center()
45+
        self.setGeometry(center.x()-200, center.y()-400, 400, 600)
46+
        self.setWindowTitle(self.tr('Offlate Settings'))
47+
        self.welcomeWidget = SettingsWidget(self, self.manager)
48+
        self.setCentralWidget(self.welcomeWidget)
49+
50+
class SystemSettingsWindow(QDialog):
51+
    def __init__(self, manager, system = -1, parent = None):
52+
        super().__init__(parent)
53+
        self.system = systems[system]
54+
        self.manager = manager
55+
        self.initUI()
56+
57+
    def initUI(self):
58+
        vbox = QVBoxLayout()
59+
        name = self.system['name']
60+
        key = self.system['key']
61+
        system = self.system['system']
62+
        spec = system.getSystemConfigSpec()
63+
64+
        self.data = self.manager.settings.conf
65+
        if not key in self.data:
66+
            self.data[key] = {}
67+
        self.widgets = {key: {}}
68+
69+
        formBox = QGroupBox(self.tr(name))
70+
        formLayout = QFormLayout()
71+
72+
        for s in spec:
73+
            label = QLabel(self.tr(s.description))
74+
            label.setWordWrap(True)
75+
            label.setOpenExternalLinks(True)
76+
            widget = None
77+
            if isinstance(s, StringConfigSpec):
78+
                widget = SettingsLineEdit()
79+
            elif isinstance(s, ListConfigSpec):
80+
                widget = ListSettingsEdit(s)
81+
            else:
82+
                raise Exception('Unknown spec type ' + str(s))
83+
84+
            try:
85+
                widget.setContent(self.data[key][s.key])
86+
            except Exception:
87+
                pass
88+
            widget.textChanged.connect(self.update)
89+
            formLayout.addRow(QLabel(self.tr(s.name)), widget)
90+
            formLayout.addRow(label)
91+
            self.widgets[key][s.key] = widget
92+
93+
        formBox.setLayout(formLayout)
94+
        vbox.addWidget(formBox)
95+
96+
        buttons = QHBoxLayout()
97+
        buttons.addStretch(1)
98+
        okbutton = QPushButton(self.tr("OK"))
99+
        buttons.addWidget(okbutton)
100+
        okbutton.clicked.connect(self.close)
101+
        vbox.addLayout(buttons)
102+
        self.setLayout(vbox)
103+
104+
    def update(self):
105+
        key = self.system['key']
106+
        system = self.system['system']
107+
        spec = system.getSystemConfigSpec()
108+
        for s in spec:
109+
            widget = self.widgets[key][s.key]
110+
            self.data[key][s.key] = widget.content()
111+
112+
class SettingsWidget(QWidget):
113+
    def __init__(self, parent=None, manager=None, welcome=False):
114+
        super().__init__(parent)
115+
        self.parent = parent
116+
        self.manager = manager
117+
        self.welcome = welcome
118+
        self.initUI()
119+
120+
    def initUI(self):
121+
        vbox = QVBoxLayout()
122+
        if self.welcome:
123+
            filename = os.path.dirname(__file__) + '/../data/icon.png'
124+
            icon = QPixmap(filename)
125+
            label = QLabel(self)
126+
            label.setPixmap(icon)
127+
            label.setAlignment(Qt.AlignCenter)
128+
            name = QLabel(self)
129+
            name.setText("Offlate")
130+
            name.setAlignment(Qt.AlignCenter)
131+
            font = name.font()
132+
            font.setPointSize(64)
133+
            font.setBold(True)
134+
            name.setFont(font)
135+
            titlebox = QHBoxLayout()
136+
            titlebox.addWidget(label)
137+
            titlebox.addWidget(name)
138+
            vbox.addLayout(titlebox)
139+
            line = QFrame(self)
140+
            line.setFrameShape(QFrame.HLine)
141+
            line.setFrameShadow(QFrame.Sunken)
142+
            vbox.addWidget(line)
143+
144+
            explain = QLabel(self)
145+
            explain.setText(self.tr("Welcome to Offlate! Offlate is a tool for \
146+
software translators who don't want to use dozens of different interfaces to \
147+
get their job done. This first screen will help you configure Offlate for your \
148+
first use!"))
149+
            explain.setWordWrap(True)
150+
            vbox.addWidget(explain)
151+
152+
        formExplain = QLabel(self)
153+
        formExplain.setText(self.tr("First, we need to know your name and email \
154+
address, so we can use this information to give you copyright credit on your \
155+
work, when this is posible. The copyright line will look like ???Copyright ?? \
156+
YEAR NAME <EMAIL>???"))
157+
        formExplain.setWordWrap(True)
158+
        vbox.addWidget(formExplain)
159+
160+
        form = QFormLayout()
161+
        self.nameWidget = QLineEdit()
162+
        self.nameWidget.setPlaceholderText(self.tr("John Doe"))
163+
        self.nameWidget.setText(self.manager.getConf(['Generic', 'name']))
164+
        self.emailWidget = QLineEdit()
165+
        self.emailWidget.setPlaceholderText(self.tr("john@doe.me"))
166+
        self.emailWidget.setText(self.manager.getConf(['Generic', 'email']))
167+
        form.addRow(QLabel(self.tr("Your name:")), self.nameWidget)
168+
        form.addRow(QLabel(self.tr("Your email address:")), self.emailWidget)
169+
        vbox.addLayout(form)
170+
171+
        if self.welcome:
172+
            systemExplain = QLabel(self)
173+
            systemExplain.setText(self.tr("Offlate is able to connect to multiple \
174+
translation service interfaces. Each service requires its own configuration \
175+
settings that you can configure later. For now, we're done with the basics! \
176+
When you select a new project, you will be able to configure the system if it \
177+
requires any additional information. You can configure each system you need now \
178+
+or come back later by clicking on the setting button in offlate."))
179+
            systemExplain.setWordWrap(True)
180+
            vbox.addWidget(systemExplain)
181+
182+
        systemsbox = QGridLayout()
183+
        x = 0
184+
        y = 0
185+
        widthScale = QApplication.desktop().logicalDpiX() / 96.0
186+
        heightScale = QApplication.desktop().logicalDpiY() / 96.0
187+
        i = 0
188+
        for system in systems:
189+
            name = system['name']
190+
            key = system['key']
191+
            system = system['system']
192+
            systembox = QVBoxLayout()
193+
            filename = os.path.dirname(__file__) + '/../data/' + key + '.png'
194+
            icon = QPixmap(filename)
195+
            iconLabel = QLabel(self)
196+
            iconLabel.setPixmap(icon.scaled(48*widthScale, 48*heightScale,
197+
                Qt.KeepAspectRatio, Qt.SmoothTransformation))
198+
            iconLabel.setAlignment(Qt.AlignCenter)
199+
200+
            button = QPushButton(self.tr("Configure me"))
201+
            button.clicked.connect(self.getSettingsOpener(i))
202+
203+
            nameLabel = QLabel(self)
204+
            nameLabel.setText(name)
205+
            nameLabel.setAlignment(Qt.AlignCenter)
206+
            systembox.addWidget(iconLabel)
207+
            systembox.addWidget(nameLabel)
208+
            systembox.addWidget(button)
209+
210+
            systemsbox.addLayout(systembox, x, y)
211+
            x += 1
212+
            if x == 2:
213+
                x = 0
214+
                y += 1
215+
            i += 1
216+
        vbox.addLayout(systemsbox)
217+
218+
        vbox.addStretch(1)
219+
        buttonbox = QHBoxLayout()
220+
        buttonbox.addStretch(1)
221+
        closeButton = QPushButton(self.tr("Cancel"))
222+
        closeButton.clicked.connect(self.quit)
223+
        doneButton = QPushButton(self.tr("Done!"))
224+
        doneButton.clicked.connect(self.done)
225+
        buttonbox.addWidget(closeButton)
226+
        buttonbox.addWidget(doneButton)
227+
        vbox.addLayout(buttonbox)
228+
229+
        self.setLayout(vbox)
230+
231+
    def done(self):
232+
        name = self.nameWidget.text()
233+
        email = self.emailWidget.text()
234+
        if name == None or name == "":
235+
            return
236+
        if email == None or email == "":
237+
            return
238+
239+
        self.manager.updateSettings({'Generic': {'name': name, 'email': email, 'new': 'False'}})
240+
        self.quit()
241+
242+
    def quit(self):
243+
        if self.welcome:
244+
            qApp.quit()
245+
        else:
246+
            self.parent.hide()
247+
248+
    def openSettings(self, system):
249+
        w = SystemSettingsWindow(self.manager, system, self)
250+
        w.exec_()
251+
252+
    def getSettingsOpener(self, system):
253+
        return lambda : self.openSettings(system)

offlate/ui/config/welcome.py unknown status 1

1+
#   Copyright (c) 2019 Julien Lepiller <julien@lepiller.eu>
2+
#
3+
#   This program is free software: you can redistribute it and/or modify
4+
#   it under the terms of the GNU Affero General Public License as
5+
#   published by the Free Software Foundation, either version 3 of the
6+
#   License, or (at your option) any later version.
7+
#
8+
#   This program is distributed in the hope that it will be useful,
9+
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11+
#   GNU Affero General Public License for more details.
12+
#
13+
#   You should have received a copy of the GNU Affero General Public License
14+
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
15+
####
16+
17+
import os
18+
import re
19+
import sys
20+
21+
from PyQt5.QtWidgets import *
22+
from PyQt5.QtGui import *
23+
from PyQt5.QtCore import *
24+
25+
from ...systems.list import systems
26+
from ...core.manager import ProjectManager
27+
from .settings import *
28+
29+
class WelcomeWindow(QMainWindow):
30+
    def __init__(self, manager):
31+
        super().__init__()
32+
        self.manager = manager
33+
        self.initUI()
34+
35+
    def initUI(self):
36+
        center = QDesktopWidget().availableGeometry().center()
37+
        self.setGeometry(center.x()-150, center.y()-400, 300, 800)
38+
        self.setWindowTitle(self.tr('Welcome to Offlate'))
39+
        self.welcomeWidget = SettingsWidget(self, self.manager, True)
40+
        self.setCentralWidget(self.welcomeWidget)

offlate/ui/data/Github.png unknown status 1

Binary data

offlate/ui/data/Gitlab.png unknown status 1

Binary data

offlate/ui/data/TP.png unknown status 1

Binary data

offlate/ui/data/Transifex.png unknown status 1

Binary data

offlate/ui/main.py

1919
from PyQt5.QtCore import *
2020
2121
from .manager import ProjectManagerWindow
22+
from .config.welcome import WelcomeWindow
2223
2324
import sys
2425
import os

3132
        app.installTranslator(translator);
3233
3334
    w = ProjectManagerWindow.getInstance()
35+
36+
    if w.projectManagerWidget.manager.isNew():
37+
        welcome = WelcomeWindow(w.projectManagerWidget.manager)
38+
        welcome.show()
39+
        app.exec_()
40+
41+
    if w.projectManagerWidget.manager.isNew():
42+
        return
43+
44+
    w.projectManagerWidget.manager.setNotNew()
3445
    w.show()
3546
3647
    sys.exit(app.exec_())

offlate/ui/manager.py

2424
2525
from .about import AboutWindow
2626
from .new import NewWindow
27-
from .settings import SettingsWindow
27+
from .config.settings import SettingsWindow, SystemSettingsWindow
2828
from .editor import EditorWindow
2929
from .parallel import RunnableCallback, RunnableSignals
3030

6666
        super().__init__(parent)
6767
        self.manager = ProjectManager()
6868
        self.editor = EditorWindow(parent, self.manager)
69+
        self.settingsWindow = SettingsWindow(self.manager)
6970
        self.threadpool = QThreadPool()
7071
        self.initUI()
7172

195196
        self.threadpool.start(worker)
196197
197198
    def configureSystem(self, system):
198-
        w = SettingsWindow(self.manager.getConf(), system)
199+
        w = SystemSettingsWindow(self.manager, system)
199200
        w.exec_()
200201
        if w.done:
201202
            self.manager.updateSettings(w.data)

227228
        self.editor.actionProgress.setEnabled(False)
228229
229230
    def settings(self):
230-
        w = SettingsWindow(self.manager.getConf())
231-
        w.exec_()
232-
        if w.done:
233-
            self.manager.updateSettings(w.data)
231+
        self.settingsWindow.show()
234232
235233
    def filter(self):
236234
        search = self.searchfield.text()

offlate/ui/settings.py unknown status 2

1-
#   Copyright (c) 2019 Julien Lepiller <julien@lepiller.eu>
2-
#
3-
#   This program is free software: you can redistribute it and/or modify
4-
#   it under the terms of the GNU Affero General Public License as
5-
#   published by the Free Software Foundation, either version 3 of the
6-
#   License, or (at your option) any later version.
7-
#
8-
#   This program is distributed in the hope that it will be useful,
9-
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10-
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11-
#   GNU Affero General Public License for more details.
12-
#
13-
#   You should have received a copy of the GNU Affero General Public License
14-
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
15-
####
16-
17-
from PyQt5.QtWidgets import *
18-
from PyQt5.QtGui import *
19-
from PyQt5.QtCore import *
20-
21-
#from .gitlabedit import GitlabEdit
22-
from .listsettingsedit import ListSettingsEdit
23-
from ..core.config import *
24-
from ..systems.list import systems
25-
26-
class SettingsLineEdit(QLineEdit):
27-
    def content(self):
28-
        return self.text()
29-
30-
    def setContent(self, value):
31-
        self.setText(value)
32-
33-
class SettingsWindow(QDialog):
34-
    def __init__(self, preferences, system = -1, parent = None):
35-
        super().__init__(parent)
36-
        self.data = preferences
37-
        self.done = False
38-
        self.system = system
39-
        self.initUI()
40-
41-
    def initUI(self):
42-
        vbox = QVBoxLayout()
43-
44-
        tab = QTabWidget()
45-
46-
        self.widgets = {}
47-
        for system in systems:
48-
            name = system['name']
49-
            key = system['key']
50-
            system = system['system']
51-
            spec = system.getSystemConfigSpec()
52-
53-
            if not key in self.data:
54-
                self.data[key] = {}
55-
            if not key in self.widgets:
56-
                self.widgets[key] = {}
57-
58-
            formBox = QGroupBox(self.tr(name))
59-
            formLayout = QFormLayout()
60-
61-
            for s in spec:
62-
                label = QLabel(self.tr(s.description))
63-
                label.setWordWrap(True)
64-
                label.linkActivated.connect(self.linkOpener(s.link))
65-
                widget = None
66-
                if isinstance(s, StringConfigSpec):
67-
                    widget = SettingsLineEdit()
68-
                elif isinstance(s, ListConfigSpec):
69-
                    widget = ListSettingsEdit(s)
70-
                else:
71-
                    raise Exception('Unknown spec type ' + str(s))
72-
73-
                try:
74-
                    widget.setContent(self.data[key][s.key])
75-
                except Exception:
76-
                    pass
77-
                widget.textChanged.connect(self.update)
78-
                formLayout.addRow(QLabel(self.tr(s.name)), widget)
79-
                formLayout.addRow(label)
80-
                self.widgets[key][s.key] = widget
81-
82-
            formBox.setLayout(formLayout)
83-
            tab.addTab(formBox, name)
84-
85-
        buttonbox = QHBoxLayout()
86-
        cancel = QPushButton(self.tr("Cancel"))
87-
        ok = QPushButton(self.tr("OK"))
88-
        buttonbox.addWidget(cancel)
89-
        buttonbox.addWidget(ok)
90-
91-
        vbox.addWidget(tab)
92-
        vbox.addLayout(buttonbox)
93-
        self.setLayout(vbox)
94-
        cancel.clicked.connect(self.close)
95-
        ok.clicked.connect(self.ok)
96-
97-
        tab.setCurrentIndex(self.system + 1)
98-
99-
    def linkOpener(self, url):
100-
        return lambda : self.openLink(url)
101-
102-
    def openLink(self, url):
103-
        print(url)
104-
        QDesktopServices().openUrl(QUrl(url));
105-
106-
    def update(self):
107-
        for system in systems:
108-
            name = system['name']
109-
            key = system['key']
110-
            system = system['system']
111-
            spec = system.getSystemConfigSpec()
112-
113-
            if not key in self.data:
114-
                self.data[key] = {}
115-
116-
            for s in spec:
117-
                self.data[key][s.key] = self.widgets[key][s.key].content()
118-
119-
    def ok(self):
120-
        self.done = True
121-
        self.close()