tree-wide: remove pylint cruft
[git-cola.git] / cola / widgets / about.py
blobdd66fcb1bda4a128697ba7a1411cefbb002f7865
1 import platform
2 import webbrowser
3 import sys
5 import qtpy
6 from qtpy.QtCore import Qt
7 from qtpy import QtGui
8 from qtpy import QtWidgets
10 from ..i18n import N_
11 from .. import core
12 from .. import resources
13 from .. import hotkeys
14 from .. import icons
15 from .. import qtutils
16 from .. import version
17 from . import defs
20 def about_dialog(context):
21 """Launches the Help -> About dialog"""
22 view = AboutView(context, qtutils.active_window())
23 view.show()
24 return view
27 class ExpandingTabBar(QtWidgets.QTabBar):
28 """A TabBar with tabs that expand to fill the empty space
30 The setExpanding(True) method does not work in practice because
31 it respects the OS style. We override the style by implementing
32 tabSizeHint() so that we can specify the size explicitly.
33 """
35 def tabSizeHint(self, tab_index):
36 width = self.parent().width() // max(2, self.count()) - 1
37 size = super().tabSizeHint(tab_index)
38 size.setWidth(width)
39 return size
42 class ExpandingTabWidget(QtWidgets.QTabWidget):
43 def __init__(self, parent=None):
44 super().__init__(parent)
45 self.setTabBar(ExpandingTabBar(self))
47 def resizeEvent(self, event):
48 """Forward resize events to the ExpandingTabBar"""
49 # Qt does not resize the tab bar when the dialog is resized
50 # so manually forward resize events to the tab bar.
51 width = event.size().width()
52 height = self.tabBar().height()
53 self.tabBar().resize(width, height)
54 return super().resizeEvent(event)
57 class AboutView(QtWidgets.QDialog):
58 """Provides the git-cola 'About' dialog"""
60 def __init__(self, context, parent=None):
61 QtWidgets.QDialog.__init__(self, parent)
63 self.context = context
64 self.setWindowTitle(N_('About git-cola'))
65 self.setWindowModality(Qt.WindowModal)
67 # Top-most large icon
68 logo_pixmap = icons.cola().pixmap(defs.huge_icon, defs.large_icon)
70 self.logo_label = QtWidgets.QLabel()
71 self.logo_label.setPixmap(logo_pixmap)
72 self.logo_label.setAlignment(Qt.AlignCenter)
74 self.logo_text_label = QtWidgets.QLabel()
75 self.logo_text_label.setText('Git Cola')
76 self.logo_text_label.setAlignment(Qt.AlignLeft | Qt.AlignCenter)
78 font = self.logo_text_label.font()
79 font.setPointSize(defs.logo_text)
80 self.logo_text_label.setFont(font)
82 self.text = qtutils.textbrowser(text=copyright_text())
83 self.version = qtutils.textbrowser(text=version_text(context))
84 self.authors = qtutils.textbrowser(text=authors_text())
85 self.translators = qtutils.textbrowser(text=translators_text())
87 self.tabs = ExpandingTabWidget()
88 self.tabs.addTab(self.text, N_('About'))
89 self.tabs.addTab(self.version, N_('Version'))
90 self.tabs.addTab(self.authors, N_('Authors'))
91 self.tabs.addTab(self.translators, N_('Translators'))
93 self.close_button = qtutils.close_button()
94 self.close_button.setDefault(True)
96 self.logo_layout = qtutils.hbox(
97 defs.no_margin,
98 defs.button_spacing,
99 self.logo_label,
100 self.logo_text_label,
101 qtutils.STRETCH,
104 self.button_layout = qtutils.hbox(
105 defs.spacing, defs.margin, qtutils.STRETCH, self.close_button
108 self.main_layout = qtutils.vbox(
109 defs.no_margin,
110 defs.spacing,
111 self.logo_layout,
112 self.tabs,
113 self.button_layout,
115 self.setLayout(self.main_layout)
117 qtutils.connect_button(self.close_button, self.accept)
119 self.resize(defs.scale(600), defs.scale(720))
122 def copyright_text():
123 return """
124 Git Cola: The highly caffeinated Git GUI
126 Copyright (C) 2007-2024 David Aguilar and contributors
128 This program is free software: you can redistribute it and/or
129 modify it under the terms of the GNU General Public License
130 version 2 as published by the Free Software Foundation.
132 This program is distributed in the hope that it will
133 be useful, but WITHOUT ANY WARRANTY; without even the
134 implied warranty of MERCHANTABILITY or
135 FITNESS FOR A PARTICULAR PURPOSE.
137 See the GNU General Public License for more details.
139 You should have received a copy of the
140 GNU General Public License along with this program.
141 If not, see http://www.gnu.org/licenses/.
146 def version_text(context):
147 git_version = version.git_version(context)
148 cola_version = version.version()
149 python_path = sys.executable
150 python_version = sys.version
151 qt_version = qtpy.QT_VERSION
152 qtpy_version = qtpy.__version__
153 pyqt_api_name = qtpy.API_NAME
154 if (
155 getattr(qtpy, 'PYQT6', False)
156 or getattr(qtpy, 'PYQT5', False)
157 or getattr(qtpy, 'PYQT4', False)
159 pyqt_api_version = qtpy.PYQT_VERSION
160 elif qtpy.PYSIDE:
161 pyqt_api_version = qtpy.PYSIDE_VERSION
162 else:
163 pyqt_api_version = 'unknown'
165 platform_version = platform.platform()
167 scope = dict(
168 cola_version=cola_version,
169 git_version=git_version,
170 platform_version=platform_version,
171 pyqt_api_name=pyqt_api_name,
172 pyqt_api_version=pyqt_api_version,
173 python_path=python_path,
174 python_version=python_version,
175 qt_version=qt_version,
176 qtpy_version=qtpy_version,
179 return (
182 <br>
183 Git Cola version %(cola_version)s
184 <ul>
185 <li> %(platform_version)s
186 <li> Python (%(python_path)s) %(python_version)s
187 <li> Git %(git_version)s
188 <li> Qt %(qt_version)s
189 <li> QtPy %(qtpy_version)s
190 <li> %(pyqt_api_name)s %(pyqt_api_version)s
191 </ul>
194 % scope
198 def mailto(email, text, palette):
199 return qtutils.link('mailto:%s' % email, text, palette) + '<br>'
202 def render_authors(authors):
203 """Render a list of author details into rich text html"""
204 for x in authors:
205 x.setdefault('email', '')
207 entries = [
211 <strong>%(name)s</strong><br>
212 <em>%(title)s</em><br>
213 %(email)s
214 </p>
216 % author
218 for author in authors
221 return ''.join(entries)
224 def contributors_text(authors, prelude='', epilogue=''):
225 author_text = render_authors(authors)
226 scope = dict(author_text=author_text, epilogue=epilogue, prelude=prelude)
228 return (
230 %(prelude)s
231 %(author_text)s
232 %(epilogue)s
234 % scope
238 def authors_text():
239 palette = QtGui.QPalette()
240 contact = N_('Email contributor')
241 authors = (
242 # The names listed here are listed in the same order as
243 # `git shortlog --summary --numbered --no-merges`
244 # Please submit a pull request if you would like to include your
245 # email address in the about screen.
246 # See the `generate-about` script in the "todo" branch.
247 # vim :read! ./todo/generate-about
248 dict(
249 name='David Aguilar',
250 title=N_('Maintainer (since 2007) and developer'),
251 email=mailto('davvid@gmail.com', contact, palette),
253 dict(name='Daniel Harding', title=N_('Developer')),
254 dict(
255 name='V字龍(Vdragon)',
256 title=N_('Developer'),
257 email=mailto('Vdragon.Taiwan@gmail.com', contact, palette),
259 dict(name='Efimov Vasily', title=N_('Developer')),
260 dict(name='Guillaume de Bure', title=N_('Developer')),
261 dict(name='Uri Okrent', title=N_('Developer')),
262 dict(name='Javier Rodriguez Cuevas', title=N_('Developer')),
263 dict(name='Alex Chernetz', title=N_('Developer')),
264 dict(name='xhl', title=N_('Developer')),
265 dict(name='Andreas Sommer', title=N_('Developer')),
266 dict(name='Thomas Kluyver', title=N_('Developer')),
267 dict(name='Minarto Margoliono', title=N_('Developer')),
268 dict(name='Ville Skyttä', title=N_('Developer')),
269 dict(name='Szymon Judasz', title=N_('Developer')),
270 dict(name='jm4R', title=N_('Developer')),
271 dict(name='Stanislaw Halik', title=N_('Developer')),
272 dict(name='Igor Galarraga', title=N_('Developer')),
273 dict(name='Virgil Dupras', title=N_('Developer')),
274 dict(name='wsdfhjxc', title=N_('Developer')),
275 dict(name='Barry Roberts', title=N_('Developer')),
276 dict(name='林博仁(Buo-ren Lin)', title=N_('Developer')),
277 dict(name='Guo Yunhe', title=N_('Developer')),
278 dict(name='cclauss', title=N_('Developer')),
279 dict(name='Stefan Naewe', title=N_('Developer')),
280 dict(name='Victor Nepveu', title=N_('Developer')),
281 dict(name='Pavel Rehak', title=N_('Developer')),
282 dict(name='Benedict Lee', title=N_('Developer')),
283 dict(name='Tim Brown', title=N_('Developer')),
284 dict(name='Steffen Prohaska', title=N_('Developer')),
285 dict(name='Filip Danilović', title=N_('Developer')),
286 dict(name='NotSqrt', title=N_('Developer')),
287 dict(name='Michael Geddes', title=N_('Developer')),
288 dict(name='Rustam Safin', title=N_('Developer')),
289 dict(name='Justin Lecher', title=N_('Developer')),
290 dict(name='Alex Gulyás', title=N_('Developer')),
291 dict(name='David Martínez Martí', title=N_('Developer')),
292 dict(name='Hualiang Xie', title=N_('Developer')),
293 dict(name='Kai Krakow', title=N_('Developer')),
294 dict(name='Karl Bielefeldt', title=N_('Developer')),
295 dict(name='Marco Costalba', title=N_('Developer')),
296 dict(name='Michael Homer', title=N_('Developer')),
297 dict(name='Sebastian Schuberth', title=N_('Developer')),
298 dict(name='Sven Claussner', title=N_('Developer')),
299 dict(name='Victor Gambier', title=N_('Developer')),
300 dict(name='bsomers', title=N_('Developer')),
301 dict(name='real', title=N_('Developer')),
302 dict(name='v.paritskiy', title=N_('Developer')),
303 dict(name='vanderkoort', title=N_('Developer')),
304 dict(name='wm4', title=N_('Developer')),
305 dict(name='Audrius Karabanovas', title=N_('Developer')),
306 dict(name='Matthew E. Levine', title=N_('Developer')),
307 dict(name='Matthias Mailänder', title=N_('Developer')),
308 dict(name='Md. Mahbub Alam', title=N_('Developer')),
309 dict(name='Jakub Szymański', title=N_('Developer')),
310 dict(name='ochristi', title=N_('Developer')),
311 dict(name='Miguel Boekhold', title=N_('Developer')),
312 dict(name='MiguelBoekhold', title=N_('Developer')),
313 dict(name='Mikhail Terekhov', title=N_('Developer')),
314 dict(name='Jake Biesinger', title=N_('Developer')),
315 dict(name='Iulian Udrea', title=N_('Developer')),
316 dict(name='Paul Hildebrandt', title=N_('Developer')),
317 dict(name='Paul Weingardt', title=N_('Developer')),
318 dict(name='Paulo Fidalgo', title=N_('Developer')),
319 dict(name='Ilya Tumaykin', title=N_('Developer')),
320 dict(name='Petr Gladkikh', title=N_('Developer')),
321 dict(name='Philip Stark', title=N_('Developer')),
322 dict(name='Radek Postołowicz', title=N_('Developer')),
323 dict(name='Rainer Müller', title=N_('Developer')),
324 dict(name='Ricardo J. Barberis', title=N_('Developer')),
325 dict(name='Rolando Espinoza', title=N_('Developer')),
326 dict(name='George Vasilakos', title=N_('Developer')),
327 dict(name="Samsul Ma'arif", title=N_('Developer')),
328 dict(name='Sebastian Brass', title=N_('Developer')),
329 dict(name='Arthur Coelho', title=N_('Developer')),
330 dict(name='Simon Peeters', title=N_('Developer')),
331 dict(name='Felipe Morales', title=N_('Developer')),
332 dict(name='David Zumbrunnen', title=N_('Developer')),
333 dict(name='David Schwörer', title=N_('Developer')),
334 dict(name='Stephen', title=N_('Developer')),
335 dict(name='Andrej', title=N_('Developer')),
336 dict(name='Daniel Pavel', title=N_('Developer')),
337 dict(name='Daniel King', title=N_('Developer')),
338 dict(name='Daniel Haskin', title=N_('Developer')),
339 dict(name='Clément Pit--Claudel', title=N_('Developer')),
340 dict(name='Vaibhav Sagar', title=N_('Developer')),
341 dict(name='Ved Vyas', title=N_('Developer')),
342 dict(name='Adrien be', title=N_('Developer')),
343 dict(name='Charles', title=N_('Developer')),
344 dict(name='Boris W', title=N_('Developer')),
345 dict(name='Ben Boeckel', title=N_('Developer')),
346 dict(name='Voicu Hodrea', title=N_('Developer')),
347 dict(name='Wesley Wong', title=N_('Developer')),
348 dict(name='Wolfgang Ocker', title=N_('Developer')),
349 dict(name='Zhang Han', title=N_('Developer')),
350 dict(name='beauxq', title=N_('Developer')),
351 dict(name='Jamie Pate', title=N_('Developer')),
352 dict(name='Jean-Francois Dagenais', title=N_('Developer')),
353 dict(name='Joachim Lusiardi', title=N_('Developer')),
354 dict(name='0xflotus', title=N_('Developer')),
355 dict(name='AJ Bagwell', title=N_('Developer')),
356 dict(name='Barrett Lowe', title=N_('Developer')),
357 dict(name='Karthik Manamcheri', title=N_('Developer')),
358 dict(name='Kelvie Wong', title=N_('Developer')),
359 dict(name='Kyle', title=N_('Developer')),
360 dict(name='Maciej Filipiak', title=N_('Developer')),
361 dict(name='Maicon D. Filippsen', title=N_('Developer')),
362 dict(name='Markus Heidelberg', title=N_('Developer')),
364 bug_url = 'https://github.com/git-cola/git-cola/issues'
365 bug_link = qtutils.link(bug_url, bug_url)
366 scope = dict(bug_link=bug_link)
367 prelude = (
370 <br>
371 Please use %(bug_link)s to report issues.
372 <br>
375 % scope
378 return contributors_text(authors, prelude=prelude)
381 def translators_text():
382 palette = QtGui.QPalette()
383 contact = N_('Email contributor')
385 translators = (
386 # See the `generate-about` script in the "todo" branch.
387 # vim :read! ./Meta/generate-about --translators
388 dict(
389 name='V字龍(Vdragon)',
390 title=N_('Traditional Chinese (Taiwan) translation'),
391 email=mailto('Vdragon.Taiwan@gmail.com', contact, palette),
393 dict(name='Pavel Rehak', title=N_('Czech translation')),
394 dict(name='Zhang Han', title=N_('Simplified Chinese translation')),
395 dict(name='Victorhck', title=N_('Spanish translation')),
396 dict(name='Vitor Lobo', title=N_('Brazilian translation')),
397 dict(name='Igor Kopach', title=N_('Ukranian translation')),
398 dict(name='Łukasz Wojniłowicz', title=N_('Polish translation')),
399 dict(name='Rafael Nascimento', title=N_('Brazilian translation')),
400 dict(name='Barış ÇELİK', title=N_('Turkish translation')),
401 dict(name='Minarto Margoliono', title=N_('Indonesian translation')),
402 dict(name='Sven Claussner', title=N_('German translation')),
403 dict(name='Shun Sakai', title=N_('Japanese translation')),
404 dict(name='Vaiz', title=N_('Russian translation')),
405 dict(name='adlgrbz', title=N_('Turkish translation')),
406 dict(name='fu7mu4', title=N_('Japanese translation')),
407 dict(name='Guo Yunhe', title=N_('Simplified Chinese translation')),
408 dict(name="Samsul Ma'arif", title=N_('Indonesian translation')),
409 dict(name='Gyuris Gellért', title=N_('Hungarian translation')),
410 dict(name='Joachim Lusiardi', title=N_('German translation')),
411 dict(name='Kai Krakow', title=N_('German translation')),
412 dict(name='Louis Rousseau', title=N_('French translation')),
413 dict(name='Mickael Albertus', title=N_('French translation')),
414 dict(
415 name='Peter Dave Hello',
416 title=N_('Traditional Chinese (Taiwan) translation'),
418 dict(name='Pilar Molina Lopez', title=N_('Spanish translation')),
419 dict(name='Rafael Reuber', title=N_('Brazilian translation')),
420 dict(name='Sabri Ünal', title=N_('Turkish translation')),
421 dict(name='Balázs Meskó', title=N_('Translation')),
422 dict(name='Zeioth', title=N_('Spanish translation')),
423 dict(name='balping', title=N_('Hungarian translation')),
424 dict(name='p-bo', title=N_('Czech translation')),
425 dict(
426 name='林博仁(Buo-ren Lin)',
427 title=N_('Traditional Chinese (Taiwan) translation'),
431 bug_url = 'https://github.com/git-cola/git-cola/issues'
432 bug_link = qtutils.link(bug_url, bug_url)
433 scope = dict(bug_link=bug_link)
435 prelude = (
438 <br>
439 Git Cola has been translated into different languages thanks
440 to the help of the individuals listed below.
442 <br>
444 Translation is approximate. If you find a mistake,
445 please let us know by opening an issue on Github:
446 </p>
449 %(bug_link)s
450 </p>
452 <br>
454 We invite you to participate in translation by adding or updating
455 a translation and opening a pull request.
456 </p>
458 <br>
462 % scope
464 return contributors_text(translators, prelude=prelude)
467 def show_shortcuts():
468 hotkeys_html = resources.doc(N_('hotkeys.html'))
469 try:
470 from qtpy import QtWebEngineWidgets
471 except (ImportError, qtpy.PythonQtError):
472 # Redhat disabled QtWebKit in their Qt build but don't punish the users
473 webbrowser.open_new_tab('file://' + hotkeys_html)
474 return
476 html = core.read(hotkeys_html)
478 parent = qtutils.active_window()
479 widget = QtWidgets.QDialog(parent)
480 widget.setWindowModality(Qt.WindowModal)
481 widget.setWindowTitle(N_('Shortcuts'))
483 web = QtWebEngineWidgets.QWebEngineView()
484 web.setHtml(html)
486 layout = qtutils.hbox(defs.no_margin, defs.spacing, web)
487 widget.setLayout(layout)
488 widget.resize(800, min(parent.height(), 600))
489 qtutils.add_action(
490 widget, N_('Close'), widget.accept, hotkeys.QUESTION, *hotkeys.ACCEPT
492 widget.show()
493 widget.exec_()