Add enrolling wizard for first-time users. This replaces the initial setup screen that used the same window as the settings manager, which is a bit confusing. The rest of the app is unchanged.
CHANGELOG.md
5 | 5 | ||
6 | 6 | ### Improvements and features ### | |
7 | 7 | ||
8 | + | * First-time users now get a specific window to help them with initial configuration. | |
8 | 9 | * Fixed TS format not setting the obsolete flag when saving. | |
9 | 10 | * Whitespace characters are shown with a small symbol in the editor. Spaces get | |
10 | 11 | a small middle dot, no-break spaces get a line underneath. Tabs are also represented. |
offlate/ui/config/settings.py
47 | 47 | self.welcomeWidget = SettingsWidget(self, self.manager) | |
48 | 48 | self.setCentralWidget(self.welcomeWidget) | |
49 | 49 | ||
50 | - | class SystemSettingsWindow(QDialog): | |
51 | - | def __init__(self, manager, system = -1, parent = None): | |
50 | + | class SystemSettingsWidget(QWidget): | |
51 | + | def __init__(self, manager, system = -1, parent=None): | |
52 | 52 | super().__init__(parent) | |
53 | 53 | self.system = systems[system] | |
54 | 54 | self.manager = manager | |
… | |||
92 | 92 | ||
93 | 93 | formBox.setLayout(formLayout) | |
94 | 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 | 95 | self.setLayout(vbox) | |
103 | 96 | ||
104 | 97 | def update(self): | |
… | |||
109 | 102 | widget = self.widgets[key][s.key] | |
110 | 103 | self.data[key][s.key] = widget.content() | |
111 | 104 | ||
105 | + | class SystemSettingsWindow(QDialog): | |
106 | + | def __init__(self, manager, system = -1, parent = None): | |
107 | + | super().__init__(parent) | |
108 | + | self.widget = SystemSettingsWidget(manager, system, self) | |
109 | + | ||
110 | + | buttons = QHBoxLayout() | |
111 | + | buttons.addStretch(1) | |
112 | + | okbutton = QPushButton(self.tr("OK")) | |
113 | + | buttons.addWidget(okbutton) | |
114 | + | okbutton.clicked.connect(self.close) | |
115 | + | ||
116 | + | vbox = QVBoxLayout() | |
117 | + | vbox.addWidget(self.widget) | |
118 | + | vbox.addLayout(buttons) | |
119 | + | self.setLayout(vbox) | |
120 | + | ||
112 | 121 | class SettingsWidget(QWidget): | |
113 | - | def __init__(self, parent=None, manager=None, welcome=False): | |
122 | + | def __init__(self, parent=None, manager=None): | |
114 | 123 | super().__init__(parent) | |
115 | 124 | self.parent = parent | |
116 | 125 | self.manager = manager | |
117 | - | self.welcome = welcome | |
118 | 126 | self.initUI() | |
119 | 127 | ||
120 | 128 | def initUI(self): | |
121 | 129 | 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 | 130 | ||
152 | 131 | formExplain = QLabel(self) | |
153 | 132 | formExplain.setText(self.tr("First, we need to know your name and email \ | |
… | |||
168 | 147 | form.addRow(QLabel(self.tr("Your email address:")), self.emailWidget) | |
169 | 148 | vbox.addLayout(form) | |
170 | 149 | ||
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 | 150 | systemsbox = QGridLayout() | |
183 | 151 | x = 0 | |
184 | 152 | y = 0 | |
… | |||
218 | 186 | vbox.addStretch(1) | |
219 | 187 | buttonbox = QHBoxLayout() | |
220 | 188 | buttonbox.addStretch(1) | |
221 | - | if self.welcome: | |
222 | - | closeButton = QPushButton(self.tr("Cancel")) | |
223 | - | closeButton.clicked.connect(self.quit) | |
224 | - | buttonbox.addWidget(closeButton) | |
225 | 189 | doneButton = QPushButton(self.tr("Done!")) | |
226 | 190 | doneButton.clicked.connect(self.done) | |
227 | 191 | buttonbox.addWidget(doneButton) | |
… | |||
241 | 205 | self.quit() | |
242 | 206 | ||
243 | 207 | def quit(self): | |
244 | - | if self.welcome: | |
245 | - | qApp.quit() | |
246 | - | else: | |
247 | - | self.parent.hide() | |
208 | + | self.parent.hide() | |
248 | 209 | ||
249 | 210 | def openSettings(self, system): | |
250 | 211 | w = SystemSettingsWindow(self.manager, system, self) |
offlate/ui/data/illustration.png unknown status 1
Binary data |
offlate/ui/data/illustration.xcf unknown status 1
Binary data |
offlate/ui/main.py
19 | 19 | from PyQt5.QtCore import * | |
20 | 20 | ||
21 | 21 | from .manager import ProjectManagerWindow | |
22 | - | from .config.welcome import WelcomeWindow | |
22 | + | from .welcome import WelcomeWindow | |
23 | 23 | ||
24 | 24 | from ..data.common import REPO, EMAIL | |
25 | 25 | ||
… | |||
44 | 44 | return | |
45 | 45 | ||
46 | 46 | w.projectManagerWidget.manager.setNotNew() | |
47 | + | w.projectManagerWidget.filter() | |
47 | 48 | try: | |
48 | 49 | w.show() | |
49 | 50 | exit = app.exec_() |
offlate/ui/manager.py
220 | 220 | self.tr('Fetching project {}...').format(name, progress)) | |
221 | 221 | ||
222 | 222 | def finishReport(self, name): | |
223 | + | self.filter() | |
223 | 224 | self.actionProgress.setValue(0) | |
224 | 225 | self.actionProgress.setEnabled(False) | |
225 | 226 | self.actionLabel.setText("") | |
… | |||
301 | 302 | else: | |
302 | 303 | self.signals.restart_required.emit(self.name, self.lang, self.system, | |
303 | 304 | self.info, self.error) | |
304 | - | self.parent.filter() | |
305 | 305 | ||
306 | 306 | class EditRunnable(RunnableCallback): | |
307 | 307 | def __init__(self, parent, name, lang, system, info): |
offlate/ui/new.py
27 | 27 | from ..systems.list import * | |
28 | 28 | from .multiplelineedit import MultipleLineEdit | |
29 | 29 | ||
30 | - | class NewWindow(QDialog): | |
31 | - | def __init__(self, manager, parent = None, name = "", | |
32 | - | lang = "", system = 0, info = None): | |
30 | + | class PredefinedProjectWidget(QWidget): | |
31 | + | currentItemChanged = pyqtSignal() | |
32 | + | ||
33 | + | def __init__(self, parent = None): | |
33 | 34 | super().__init__(parent) | |
34 | - | self.name = name | |
35 | - | self.lang = lang | |
36 | - | self.system = system | |
37 | - | self.info = info | |
38 | - | self.manager = manager | |
39 | - | self.askNew = False | |
35 | + | self.setProperty("data", {}) | |
40 | 36 | self.initUI() | |
41 | 37 | ||
42 | 38 | def initUI(self): | |
43 | - | hbox = QHBoxLayout() | |
44 | 39 | predefinedbox = QVBoxLayout() | |
45 | 40 | self.searchfield = QLineEdit() | |
41 | + | self.searchfield.addAction(QIcon.fromTheme("search"), QLineEdit.LeadingPosition) | |
46 | 42 | predefinedbox.addWidget(self.searchfield) | |
47 | 43 | self.predefinedprojects = QListWidget() | |
48 | 44 | with open(os.path.dirname(__file__) + '/../data/data.json') as f: | |
… | |||
53 | 49 | self.predefinedprojects.addItem(item) | |
54 | 50 | predefinedbox.addWidget(self.predefinedprojects) | |
55 | 51 | ||
52 | + | self.predefinedprojects.currentItemChanged.connect(self.itemChanged) | |
53 | + | ||
54 | + | self.searchfield.textChanged.connect(self.filter) | |
55 | + | self.setLayout(predefinedbox) | |
56 | + | ||
57 | + | def itemChanged(self): | |
58 | + | item = self.currentItem() | |
59 | + | if item is None: | |
60 | + | self.setProperty("data", {}) | |
61 | + | self.setProperty("data", item.data(Qt.UserRole)) | |
62 | + | self.currentItemChanged.emit() | |
63 | + | ||
64 | + | def currentItem(self): | |
65 | + | return self.predefinedprojects.currentItem() | |
66 | + | ||
67 | + | def filter(self): | |
68 | + | search = self.searchfield.text() | |
69 | + | self.predefinedprojects.clear() | |
70 | + | regexp = re.compile(".*"+search) | |
71 | + | for d in self.projectdata: | |
72 | + | if regexp.match(d['name']): | |
73 | + | item = QListWidgetItem(d['name']) | |
74 | + | item.setData(Qt.UserRole, d) | |
75 | + | self.predefinedprojects.addItem(item) | |
76 | + | ||
77 | + | def currentData(self): | |
78 | + | return self.property("data") | |
79 | + | ||
80 | + | class AdvancedProjectWidget(QWidget): | |
81 | + | modified = pyqtSignal() | |
82 | + | ||
83 | + | def __init__(self, parent = None, name = "", lang = "", system = 0, info = None): | |
84 | + | super().__init__(parent) | |
85 | + | self.name = name | |
86 | + | self.lang = lang | |
87 | + | self.system = system | |
88 | + | self.info = info | |
89 | + | self._registerAdditionalFields() | |
90 | + | self.initUI() | |
91 | + | ||
92 | + | def initUI(self): | |
56 | 93 | contentbox = QVBoxLayout() | |
57 | 94 | formbox = QGroupBox(self.tr("Project information")) | |
58 | 95 | self.formLayout = QFormLayout() | |
… | |||
73 | 110 | self.nameWidget.textChanged.connect(self.modify) | |
74 | 111 | self.langWidget.textChanged.connect(self.modify) | |
75 | 112 | ||
76 | - | hhbox = QHBoxLayout() | |
77 | - | self.cancelbutton = QPushButton(self.tr("Cancel")) | |
78 | - | self.okbutton = QPushButton(self.tr("OK")) | |
79 | - | self.okbutton.setEnabled(False) | |
80 | - | hhbox.addWidget(self.cancelbutton) | |
81 | - | hhbox.addWidget(self.okbutton) | |
82 | 113 | contentbox.addWidget(formbox) | |
83 | - | contentbox.addLayout(hhbox) | |
84 | - | hbox.addLayout(predefinedbox) | |
85 | - | hbox.addLayout(contentbox) | |
86 | - | ||
87 | - | self.additionalFields = [] | |
88 | - | ||
89 | - | for system in systems: | |
90 | - | fields = [] | |
91 | - | for spec in system['system'].getProjectConfigSpec(): | |
92 | - | if isinstance(spec, StringConfigSpec): | |
93 | - | widget = QLineEdit() | |
94 | - | widget.textChanged.connect(self.modify) | |
95 | - | if spec.placeholder is not None and spec.placeholder != '': | |
96 | - | widget.setPlaceholderText(spec.placeholder) | |
97 | - | label = QLabel(spec.name) | |
98 | - | fields.append({'label': label, 'widget': widget}) | |
99 | - | else: | |
100 | - | raise Exception("Unknown spec type: " + spec) | |
101 | - | self.additionalFields.append(fields) | |
102 | - | ||
103 | - | self.setLayout(hbox) | |
104 | - | ||
105 | - | self.predefinedprojects.currentItemChanged.connect(self.fill) | |
106 | - | self.cancelbutton.clicked.connect(self.close) | |
107 | - | self.okbutton.clicked.connect(self.ok) | |
108 | - | self.searchfield.textChanged.connect(self.filter) | |
109 | 114 | self.combo.currentIndexChanged.connect(self.othersystem) | |
110 | - | self.modify() | |
111 | - | self.othersystem() | |
112 | - | ||
113 | - | def ok(self): | |
114 | - | self.askNew = True | |
115 | - | self.close() | |
116 | 115 | ||
117 | - | def fill(self): | |
118 | - | item = self.predefinedprojects.currentItem() | |
116 | + | self.setLayout(contentbox) | |
119 | 117 | ||
120 | - | if item is None: | |
121 | - | return | |
118 | + | self.othersystem() | |
122 | 119 | ||
123 | - | data = item.data(Qt.UserRole) | |
120 | + | def fill(self, data): | |
124 | 121 | self.nameWidget.setText(data['name']) | |
125 | 122 | self.combo.setCurrentIndex(int(data['system'])) | |
126 | 123 | ||
… | |||
132 | 129 | if spec.key in data: | |
133 | 130 | widget.setText(data[spec.key]) | |
134 | 131 | i = i + 1 | |
132 | + | self.modified.emit() | |
135 | 133 | ||
136 | - | def filter(self): | |
137 | - | search = self.searchfield.text() | |
138 | - | self.predefinedprojects.clear() | |
139 | - | regexp = re.compile(".*"+search) | |
140 | - | for d in self.projectdata: | |
141 | - | if regexp.match(d['name']): | |
142 | - | item = QListWidgetItem(d['name']) | |
143 | - | item.setData(Qt.UserRole, d) | |
144 | - | self.predefinedprojects.addItem(item) | |
145 | - | ||
146 | - | def modify(self): | |
147 | - | enable = False | |
134 | + | def isComplete(self): | |
135 | + | complete = False | |
148 | 136 | if self.nameWidget.text() != '' and self.langWidget.text() != '': | |
149 | - | enable = True | |
137 | + | complete = True | |
150 | 138 | system = systems[self.combo.currentIndex()] | |
151 | 139 | fields = self.additionalFields[self.combo.currentIndex()] | |
152 | 140 | i = 0 | |
… | |||
154 | 142 | if not spec.optional: | |
155 | 143 | widget = fields[i]['widget'] | |
156 | 144 | if isinstance(widget, QLineEdit) and widget.text() == '': | |
157 | - | enable = False | |
145 | + | complete = False | |
158 | 146 | break | |
159 | 147 | i = i + 1 | |
160 | - | self.okbutton.setEnabled(enable) | |
148 | + | return complete | |
161 | 149 | ||
162 | - | def wantNew(self): | |
163 | - | return self.askNew | |
150 | + | def _registerAdditionalFields(self): | |
151 | + | self.additionalFields = [] | |
152 | + | ||
153 | + | for system in systems: | |
154 | + | fields = [] | |
155 | + | for spec in system['system'].getProjectConfigSpec(): | |
156 | + | if isinstance(spec, StringConfigSpec): | |
157 | + | widget = QLineEdit() | |
158 | + | widget.textChanged.connect(self.modify) | |
159 | + | if spec.placeholder is not None and spec.placeholder != '': | |
160 | + | widget.setPlaceholderText(spec.placeholder) | |
161 | + | label = QLabel(spec.name) | |
162 | + | fields.append({'label': label, 'widget': widget}) | |
163 | + | else: | |
164 | + | raise Exception("Unknown spec type: " + spec) | |
165 | + | self.additionalFields.append(fields) | |
164 | 166 | ||
165 | 167 | def getProjectName(self): | |
166 | 168 | return self.nameWidget.text() | |
… | |||
195 | 197 | self.formLayout.addRow(widget['label'], widget['widget']) | |
196 | 198 | widget['widget'].show() | |
197 | 199 | widget['label'].show() | |
198 | - | self.setTabOrder(oldwidget, self.cancelbutton) | |
199 | - | self.setTabOrder(self.cancelbutton, self.okbutton) | |
200 | 200 | self.modify() | |
201 | + | ||
202 | + | def modify(self): | |
203 | + | data = {'name': self.getProjectName(), 'system': self.getProjectSystem(), | |
204 | + | 'system': self.getProjectSystem(), 'info': self.getProjectInfo()} | |
205 | + | self.setProperty("data", data) | |
206 | + | self.modified.emit() | |
207 | + | ||
208 | + | class NewWindow(QDialog): | |
209 | + | def __init__(self, manager, parent = None, name = "", | |
210 | + | lang = "", system = 0, info = None): | |
211 | + | super().__init__(parent) | |
212 | + | self.name = name | |
213 | + | self.lang = lang | |
214 | + | self.system = system | |
215 | + | self.info = info | |
216 | + | self.manager = manager | |
217 | + | self.askNew = False | |
218 | + | self.initUI() | |
219 | + | ||
220 | + | def initUI(self): | |
221 | + | vbox = QVBoxLayout() | |
222 | + | hbox = QHBoxLayout() | |
223 | + | self.predefinedprojects = PredefinedProjectWidget(self) | |
224 | + | self.advancedproject = AdvancedProjectWidget(self, self.name, self.lang, | |
225 | + | self.system, self.info) | |
226 | + | ||
227 | + | hbox.addWidget(self.predefinedprojects) | |
228 | + | hbox.addWidget(self.advancedproject) | |
229 | + | ||
230 | + | self.cancelbutton = QPushButton(self.tr("Cancel")) | |
231 | + | self.okbutton = QPushButton(self.tr("OK")) | |
232 | + | self.okbutton.setEnabled(False) | |
233 | + | self.cancelbutton.clicked.connect(self.close) | |
234 | + | self.okbutton.clicked.connect(self.ok) | |
235 | + | self.predefinedprojects.currentItemChanged.connect(self.fill) | |
236 | + | self.advancedproject.modified.connect(self.modify) | |
237 | + | ||
238 | + | hhbox = QHBoxLayout() | |
239 | + | hhbox.addWidget(self.cancelbutton) | |
240 | + | hhbox.addWidget(self.okbutton) | |
241 | + | vbox.addLayout(hbox) | |
242 | + | vbox.addLayout(hhbox) | |
243 | + | ||
244 | + | self.setLayout(vbox) | |
245 | + | ||
246 | + | self.modify() | |
247 | + | ||
248 | + | def ok(self): | |
249 | + | self.askNew = True | |
250 | + | self.close() | |
251 | + | ||
252 | + | def fill(self): | |
253 | + | item = self.predefinedprojects.currentItem() | |
254 | + | ||
255 | + | if item is None: | |
256 | + | return | |
257 | + | ||
258 | + | data = item.data(Qt.UserRole) | |
259 | + | self.advancedproject.fill(data) | |
260 | + | ||
261 | + | def modify(self): | |
262 | + | self.okbutton.setEnabled(self.advancedproject.isComplete()) | |
263 | + | ||
264 | + | def wantNew(self): | |
265 | + | return self.askNew | |
266 | + | ||
267 | + | def getProjectName(self): | |
268 | + | return self.advancedproject.getProjectName() | |
269 | + | ||
270 | + | def getProjectLang(self): | |
271 | + | return self.advancedproject.getProjectLang() | |
272 | + | ||
273 | + | def getProjectSystem(self): | |
274 | + | return self.advancedproject.getProjectSystem() | |
275 | + | ||
276 | + | def getProjectInfo(self): | |
277 | + | return self.advancedproject.getProjectInfo() |
offlate/ui/welcome.py unknown status 1
1 | + | # Copyright (c) 2021 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 | + | import datetime | |
22 | + | import sys | |
23 | + | import os | |
24 | + | ||
25 | + | from translate.lang.data import languages, tr_lang | |
26 | + | from .new import PredefinedProjectWidget, AdvancedProjectWidget | |
27 | + | from .manager import NewRunnable | |
28 | + | from .config.settings import SystemSettingsWidget | |
29 | + | ||
30 | + | WelcomePage = 1 | |
31 | + | CopyrightPage = 2 | |
32 | + | LangPage = 3 | |
33 | + | ProjectPage = 4 | |
34 | + | DetailPage = 5 | |
35 | + | SettingsPage = 6 | |
36 | + | DownloadPage = 7 | |
37 | + | SuccessPage = 8 | |
38 | + | HintsPage = 9 | |
39 | + | ||
40 | + | class WelcomeWindow(QWizard): | |
41 | + | """ | |
42 | + | This it the class that contains the first-time welcome screen, implemented | |
43 | + | as a wizard, to benefit from the page logic of the class. It is mostly | |
44 | + | linera, but we add one optional page to add the details of a project, if | |
45 | + | the first project is not available in the list. | |
46 | + | """ | |
47 | + | def __init__(self, manager): | |
48 | + | super().__init__() | |
49 | + | self.manager = manager | |
50 | + | self.initUI() | |
51 | + | ||
52 | + | def initUI(self): | |
53 | + | center = QDesktopWidget().availableGeometry().center() | |
54 | + | self.addPage(PageWelcome(self)) | |
55 | + | self.addPage(PageCopyright(self)) | |
56 | + | self.addPage(PageLang(self)) | |
57 | + | self.addPage(PageProjectSelection(self)) | |
58 | + | self.addPage(PageSystemSettings(self)) | |
59 | + | self.addPage(PageDownload(self)) | |
60 | + | self.addPage(PageSuccess(self)) | |
61 | + | self.addPage(PageHints(self)) | |
62 | + | self.setGeometry(center.x()-300, center.y()-225, 600, 450) | |
63 | + | self.setWindowTitle(self.tr('Welcome to Offlate')) | |
64 | + | self.setWizardStyle(QWizard.ModernStyle); | |
65 | + | self.setPixmap(QWizard.LogoPixmap, QPixmap(os.path.dirname(__file__) + '/data/icon.png')); | |
66 | + | ||
67 | + | ||
68 | + | class PageWelcome(QWizardPage): | |
69 | + | def __init__(self, parent=None): | |
70 | + | super().__init__(parent) | |
71 | + | self.initUI() | |
72 | + | ||
73 | + | def initUI(self): | |
74 | + | layout = QVBoxLayout() | |
75 | + | ||
76 | + | self.setTitle("Offlate") | |
77 | + | self.setPixmap(QWizard.WatermarkPixmap, QPixmap(os.path.dirname(__file__) + '/data/illustration.png')) | |
78 | + | ||
79 | + | #self.setSubTitle(self.tr("Welcome to Offlate!")) | |
80 | + | ||
81 | + | explainText = QLabel(self) | |
82 | + | explainText.setText(self.tr("Offlate is a tool to help you localise \ | |
83 | + | free and open source software. Before you start contributing translations to a \ | |
84 | + | project though, there are a few things we need to set up and talk about. Let's \ | |
85 | + | get started!")) | |
86 | + | explainText.setWordWrap(True) | |
87 | + | layout.addWidget(explainText) | |
88 | + | ||
89 | + | self.setLayout(layout) | |
90 | + | ||
91 | + | class PageCopyright(QWizardPage): | |
92 | + | def __init__(self, parent): | |
93 | + | super().__init__(parent) | |
94 | + | self.initUI() | |
95 | + | ||
96 | + | def initUI(self): | |
97 | + | layout = QVBoxLayout() | |
98 | + | ||
99 | + | self.setTitle(self.tr("Copyright Settings")) | |
100 | + | self.setSubTitle(self.tr("Configuring how your copyright is added to files")) | |
101 | + | ||
102 | + | explainText = QLabel(self) | |
103 | + | explainText.setText(self.tr("In some cases, we need to add a copyright \ | |
104 | + | line for you in the translation files we send upstream. Here, you can configure \ | |
105 | + | how you want the copyright line to look like. This information will most likely \ | |
106 | + | become public after your first contribution.")) | |
107 | + | explainText.setWordWrap(True) | |
108 | + | layout.addWidget(explainText) | |
109 | + | ||
110 | + | form = QFormLayout() | |
111 | + | self.nameEdit = QLineEdit() | |
112 | + | self.emailEdit = QLineEdit() | |
113 | + | form.addRow(QLabel("Name:"), self.nameEdit) | |
114 | + | form.addRow(QLabel("Email:"), self.emailEdit) | |
115 | + | layout.addLayout(form) | |
116 | + | ||
117 | + | layout.addSpacing(15) | |
118 | + | ||
119 | + | previewLabel = QLabel() | |
120 | + | previewLabel.setText(self.tr("Here is how your copyright line will look like:")) | |
121 | + | layout.addWidget(previewLabel) | |
122 | + | ||
123 | + | layout.addSpacing(9) | |
124 | + | ||
125 | + | self.previewLine = QLabel() | |
126 | + | self.previewLine.setTextFormat(Qt.RichText) | |
127 | + | self.previewLine.setWordWrap(True) | |
128 | + | self.previewLine.setAlignment(Qt.AlignCenter) | |
129 | + | pal = QPalette() | |
130 | + | pal.setColor(QPalette.Window, Qt.white) | |
131 | + | self.previewLine.setAutoFillBackground(True) | |
132 | + | self.previewLine.setMargin(9) | |
133 | + | self.previewLine.setPalette(pal) | |
134 | + | layout.addWidget(self.previewLine) | |
135 | + | self.updatePreview() | |
136 | + | self.nameEdit.textChanged.connect(self.updatePreview) | |
137 | + | self.emailEdit.textChanged.connect(self.updatePreview) | |
138 | + | ||
139 | + | self.setLayout(layout) | |
140 | + | ||
141 | + | self.registerField("name*", self.nameEdit) | |
142 | + | self.registerField("email*", self.emailEdit) | |
143 | + | ||
144 | + | def updatePreview(self, _ignore=None): | |
145 | + | currentDateTime = datetime.datetime.now() | |
146 | + | date = currentDateTime.date() | |
147 | + | year = date.strftime("%Y") | |
148 | + | name = self.nameEdit.text() | |
149 | + | nameHolder = "<i>{}</i>".format(self.tr("Your Name")) | |
150 | + | email = self.emailEdit.text() | |
151 | + | emailHolder = "<i>{}</i>".format(self.tr("your@email")) | |
152 | + | self.previewLine.setText(self.tr("Copyright © {} {} <{}>").format(year, | |
153 | + | name if name != "" else nameHolder, email if email != "" else emailHolder)) | |
154 | + | ||
155 | + | class PageLang(QWizardPage): | |
156 | + | def __init__(self, parent): | |
157 | + | super().__init__(parent) | |
158 | + | self.initUI() | |
159 | + | ||
160 | + | def initUI(self): | |
161 | + | layout = QVBoxLayout() | |
162 | + | ||
163 | + | self.setTitle(self.tr("Language Settings")) | |
164 | + | self.setSubTitle(self.tr("Configuring the default language for new projects")) | |
165 | + | ||
166 | + | explainText = QLabel(self) | |
167 | + | explainText.setText(self.tr("Which language will you most frequently \ | |
168 | + | translate projects into? This setting can be changed later. It can also be \ | |
169 | + | overriden by individual projects.")) | |
170 | + | explainText.setWordWrap(True) | |
171 | + | layout.addWidget(explainText) | |
172 | + | ||
173 | + | self.languageCombo = QComboBox() | |
174 | + | self.languageCombo.addItem(self.tr("None"), userData="") | |
175 | + | for lang in languages: | |
176 | + | langtag = lang | |
177 | + | lang = languages[lang] | |
178 | + | langname = lang[0] | |
179 | + | self.languageCombo.addItem(tr_lang()(langname), userData=langtag) | |
180 | + | self.languageCombo.currentIndexChanged.connect(self.comboChanged) | |
181 | + | layout.addWidget(self.languageCombo) | |
182 | + | ||
183 | + | layout.addSpacing(15) | |
184 | + | ||
185 | + | warnText = QLabel(self) | |
186 | + | warnText.setText(self.tr("Note that you should only translate into \ | |
187 | + | native language or to a language you are extremely familiar with, to avoid \ | |
188 | + | weird or nonsensical translations.")) | |
189 | + | warnText.setWordWrap(True) | |
190 | + | layout.addWidget(warnText) | |
191 | + | ||
192 | + | # TODO: Add information about the selected language, to redirect users | |
193 | + | # to translation teams, resources, etc | |
194 | + | ||
195 | + | # TODO: Allow users to select an unlisted language, by providing | |
196 | + | # language codes. | |
197 | + | ||
198 | + | self.setLayout(layout) | |
199 | + | ||
200 | + | self.langEdit = QLineEdit() | |
201 | + | self.registerField("lang*", self.langEdit) | |
202 | + | ||
203 | + | def comboChanged(self, index): | |
204 | + | self.langEdit.setText(self.languageCombo.itemData(index)) | |
205 | + | ||
206 | + | class PageProjectSelection(QWizardPage): | |
207 | + | def __init__(self, parent): | |
208 | + | super().__init__(parent) | |
209 | + | self.initUI() | |
210 | + | ||
211 | + | def initUI(self): | |
212 | + | layout = QVBoxLayout() | |
213 | + | ||
214 | + | self.setTitle(self.tr("First Project")) | |
215 | + | self.setSubTitle(self.tr("Choosing a first project")) | |
216 | + | ||
217 | + | self.predefinedprojects = PredefinedProjectWidget() | |
218 | + | self.advancedproject = AdvancedProjectWidget() | |
219 | + | self.predefinedprojects.currentItemChanged.connect(self.selected) | |
220 | + | layout.addWidget(self.predefinedprojects) | |
221 | + | ||
222 | + | self.setLayout(layout) | |
223 | + | ||
224 | + | self.registerField("project*", self.advancedproject, "data", | |
225 | + | self.advancedproject.modified) | |
226 | + | ||
227 | + | def selected(self): | |
228 | + | self.advancedproject.fill(self.predefinedprojects.currentData()) | |
229 | + | ||
230 | + | class PageSystemSettings(QWizardPage): | |
231 | + | def __init__(self, parent): | |
232 | + | super().__init__(parent) | |
233 | + | self.parent = parent | |
234 | + | self.initUI() | |
235 | + | ||
236 | + | def initUI(self): | |
237 | + | self.setTitle(self.tr("System Configuration")) | |
238 | + | self.setSubTitle(self.tr("Getting an account on the project's platform")) | |
239 | + | ||
240 | + | def initializePage(self): | |
241 | + | layout = QVBoxLayout() | |
242 | + | system = self.field("project")["system"] | |
243 | + | lang = self.field("lang") | |
244 | + | email = self.field("email") | |
245 | + | name = self.field("name") | |
246 | + | self.parent.manager.updateSettings({'Generic': {'name': name, 'email': email, 'lang': lang}}) | |
247 | + | widget = SystemSettingsWidget(self.parent.manager, system, self) | |
248 | + | layout.addWidget(widget) | |
249 | + | self.setLayout(layout) | |
250 | + | ||
251 | + | ||
252 | + | class PageDownload(QWizardPage): | |
253 | + | def __init__(self, parent): | |
254 | + | super().__init__(parent) | |
255 | + | self.parent = parent | |
256 | + | self.threadpool = QThreadPool() | |
257 | + | self.initUI() | |
258 | + | ||
259 | + | def initUI(self): | |
260 | + | layout = QVBoxLayout() | |
261 | + | self.setTitle(self.tr("Project Fetch")) | |
262 | + | self.setSubTitle(self.tr("Downloading translation files")) | |
263 | + | ||
264 | + | explainText = QLabel(self) | |
265 | + | explainText.setText(self.tr("We are now attempting to fetch the \ | |
266 | + | translation files of your project. This step should be pretty fast.")) | |
267 | + | explainText.setWordWrap(True) | |
268 | + | layout.addWidget(explainText) | |
269 | + | ||
270 | + | self.bar = QProgressBar() | |
271 | + | self.bar.setEnabled(True) | |
272 | + | self.bar.setRange(0, 100) | |
273 | + | layout.addWidget(self.bar) | |
274 | + | ||
275 | + | layout.addSpacing(15) | |
276 | + | ||
277 | + | pal = QPalette() | |
278 | + | pal.setColor(QPalette.WindowText, Qt.red) | |
279 | + | ||
280 | + | self.notConfigured = QLabel(self) | |
281 | + | self.notConfigured.setText("The platform is not properly configured, \ | |
282 | + | please go back.") | |
283 | + | self.notConfigured.setWordWrap(True) | |
284 | + | self.notConfigured.hide() | |
285 | + | self.notConfigured.setPalette(pal) | |
286 | + | layout.addWidget(self.notConfigured) | |
287 | + | ||
288 | + | self.errorLabel = QLabel(self) | |
289 | + | self.errorLabel.setWordWrap(True) | |
290 | + | self.errorLabel.hide() | |
291 | + | self.errorLabel.setPalette(pal) | |
292 | + | layout.addWidget(self.errorLabel) | |
293 | + | ||
294 | + | self.finishedLabel = QLabel(self) | |
295 | + | self.finishedLabel.setText(self.tr("Finished!")) | |
296 | + | self.finishedLabel.setWordWrap(True) | |
297 | + | self.finishedLabel.hide() | |
298 | + | layout.addWidget(self.finishedLabel) | |
299 | + | ||
300 | + | self.setLayout(layout) | |
301 | + | ||
302 | + | self.check = QCheckBox() | |
303 | + | self.check.setCheckState(Qt.Unchecked) | |
304 | + | self.registerField("downloaded*", self.check) | |
305 | + | ||
306 | + | def initializePage(self): | |
307 | + | self.check.setCheckState(Qt.Unchecked) | |
308 | + | project = self.field("project") | |
309 | + | lang = self.field("lang") | |
310 | + | email = self.field("email") | |
311 | + | name = self.field("name") | |
312 | + | self.bar.setValue(0) | |
313 | + | self.notConfigured.hide() | |
314 | + | self.errorLabel.hide() | |
315 | + | self.finishedLabel.hide() | |
316 | + | if not self.parent.manager.isConfigured(project['system'], project['info']): | |
317 | + | self.notConfigured.show() | |
318 | + | return | |
319 | + | ||
320 | + | self.manager = self.parent.manager | |
321 | + | worker = NewRunnable(self, project['name'], lang, project['system'], | |
322 | + | project['info']) | |
323 | + | worker.signals.finished.connect(self.reportFinish) | |
324 | + | worker.signals.progress.connect(self.reportProgress) | |
325 | + | worker.signals.restart_required.connect(self.reportError) | |
326 | + | self.threadpool.start(worker) | |
327 | + | ||
328 | + | def reportProgress(self, name, progress): | |
329 | + | self.bar.setValue(progress) | |
330 | + | ||
331 | + | def reportError(self, name, lang, system, info, error): | |
332 | + | self.errorLabel.setText(error) | |
333 | + | self.errorLabel.show() | |
334 | + | ||
335 | + | def reportFinish(self, name): | |
336 | + | self.bar.setValue(100) | |
337 | + | self.finishedLabel.show() | |
338 | + | self.check.setCheckState(Qt.Checked) | |
339 | + | ||
340 | + | class PageSuccess(QWizardPage): | |
341 | + | def __init__(self, parent): | |
342 | + | super().__init__(parent) | |
343 | + | self.parent = parent | |
344 | + | self.initUI() | |
345 | + | ||
346 | + | def initUI(self): | |
347 | + | layout = QVBoxLayout() | |
348 | + | self.setTitle("Success") | |
349 | + | self.setPixmap(QWizard.WatermarkPixmap, QPixmap(os.path.dirname(__file__) + '/data/illustration.png')) | |
350 | + | ||
351 | + | explainText = QLabel(self) | |
352 | + | explainText.setText(self.tr("Congratulations! We've just set up your \ | |
353 | + | first project! Initial setup is now complete, and you can always change the \ | |
354 | + | settings you've set here from the Offlate welcome screen. After a few \ | |
355 | + | explanations you will be able to work on your project right away!")) | |
356 | + | explainText.setWordWrap(True) | |
357 | + | layout.addWidget(explainText) | |
358 | + | ||
359 | + | self.setLayout(layout) | |
360 | + | ||
361 | + | def initializePage(self): | |
362 | + | self.parent.manager.updateSettings({'Generic': {'new': 'False'}}) | |
363 | + | ||
364 | + | class PageHints(QWizardPage): | |
365 | + | def __init__(self, parent): | |
366 | + | super().__init__(parent) | |
367 | + | self.initUI() | |
368 | + | ||
369 | + | def initUI(self): | |
370 | + | layout = QVBoxLayout() | |
371 | + | self.setTitle("Offlate Hints") | |
372 | + | self.setPixmap(QWizard.WatermarkPixmap, QPixmap(os.path.dirname(__file__) + '/data/illustration.png')) | |
373 | + | ||
374 | + | explainManager = QLabel(self) | |
375 | + | explainManager.setText(self.tr("You can always go back to the initial \ | |
376 | + | screen (called the project manager) from the editor, by using \ | |
377 | + | <i>file > project manager</i>")) | |
378 | + | explainManager.setWordWrap(True) | |
379 | + | layout.addWidget(explainManager) | |
380 | + | ||
381 | + | layout.addSpacing(15) | |
382 | + | ||
383 | + | explainManager = QLabel(self) | |
384 | + | explainManager.setText(self.tr("Quickly switch to the next string with \ | |
385 | + | <i>Ctrl+Enter</i>. Use <i>Ctrl+Shift+Enter</i> to go back to the previous string \ | |
386 | + | instead.")) | |
387 | + | explainManager.setWordWrap(True) | |
388 | + | layout.addWidget(explainManager) | |
389 | + | ||
390 | + | layout.addSpacing(15) | |
391 | + | ||
392 | + | explainManager = QLabel(self) | |
393 | + | explainManager.setText(self.tr("With these hints, you are now ready \ | |
394 | + | to start working on your project! We will now open the project manager, from \ | |
395 | + | which you can add more projects, change your settings, etc. Have fun!")) | |
396 | + | explainManager.setWordWrap(True) | |
397 | + | layout.addWidget(explainManager) | |
398 | + | ||
399 | + | self.setLayout(layout) |