From c5e61808729e556fd2d8cf739a2101aadff74789 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Fri, 13 Jan 2017 15:46:57 +0100 Subject: [PATCH] PythonQt documentation. --- src/modules/dummypythonqt/main.py | 70 +++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/src/modules/dummypythonqt/main.py b/src/modules/dummypythonqt/main.py index 44a4bcf20..79a1c3b42 100644 --- a/src/modules/dummypythonqt/main.py +++ b/src/modules/dummypythonqt/main.py @@ -3,7 +3,7 @@ # # === This file is part of Calamares - === # -# Copyright 2016, Teo Mrnjavac +# Copyright 2016-2017, Teo Mrnjavac # # Calamares is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,6 +23,10 @@ import platform from PythonQt.QtGui import * import PythonQt.calamares as calamares +# WARNING: the Calamares PythonQt API is considered EXPERIMENTAL as of +# Calamares 2.5. It comes with no promise or commitment to API stability. + + # Set up translations. # You may skip this if your Calamares module has no user visible strings. # DO NOT install _ into the builtin namespace because each module loads @@ -39,6 +43,7 @@ _ = gettext.gettext # Example Python ViewModule. # A Python ViewModule is a Python program which defines a ViewStep class. +# One UI module ==> one ViewStep. # This class must be marked with the @calamares_module decorator. A # ViewModule may define other classes, but only one may be decorated with # @calamares_module. Such a class must conform to the Calamares ViewStep @@ -47,9 +52,16 @@ _ = gettext.gettext # back/next, and reports its status through isNextEnabled/isBackEnabled/ # isAtBeginning/isAtEnd. The whole UI, including all the pages, must be # exposed as a single QWidget, returned by the widget function. +# +# For convenience, both C++ and PythonQt ViewSteps are considered to be +# implementations of ViewStep.h. Additionally, the Calamares PythonQt API allows +# Python developers to keep their identifiers more Pythonic on the Python side. +# Thus, all of the following are considered valid method identifiers in a +# ViewStep implementation: isNextEnabled, isnextenabled, is_next_enabled. @calamares_module class DummyPythonQtViewStep: def __init__(self): + # Importing PythonQt.QtGui provides access to most Qt widget classes. self.main_widget = QFrame() self.main_widget.setLayout(QVBoxLayout()) @@ -62,8 +74,16 @@ class DummyPythonQtViewStep: label.text = accumulator btn = QPushButton() + + # Python strings can be used wherever a method wants a QString. Python + # gettext translations can be used seamlessly as well. btn.setText(_("Click me!")) self.main_widget.layout().addWidget(btn) + + # The syntax for signal-slot connections is very simple, though slightly + # different from the C++ equivalent. There are no SIGNAL and SLOT + # macros, and a signal can be connected to any Python method (without a + # special "slot" designation). btn.connect("clicked(bool)", self.on_btn_clicked) def on_btn_clicked(self): @@ -73,27 +93,64 @@ class DummyPythonQtViewStep: return "Dummy PythonQt ViewStep" def isNextEnabled(self): - return True + return True # The "Next" button should be clickable def isBackEnabled(self): - return True + return True # The "Back" button should be clickable def isAtBeginning(self): + # True means the currently shown UI page is the first page of this + # module, thus a "Back" button click will not be handled by this module, + # and will cause a skip to the previous ViewStep instead (if any). + # Fals means that the present ViewStep provides other UI pages placed + # logically "before" the current one, thus a "Back" button click will + # be handled by this module instead of skipping to another ViewStep. + # A module (ViewStep) with only one page will always return True here. return True def isAtEnd(self): + # True means the currently shown UI page is the last page of this + # module, thus a "Next" button click will not be handled by this module, + # and will cause a skip to the next ViewStep instead (if any). + # Fals means that the present ViewStep provides other UI pages placed + # logically "after" the current one, thus a "Next" button click will + # be handled by this module instead of skipping to another ViewStep. + # A module (ViewStep) with only one page will always return True here. return True def jobs(self): + # Returns a list of objects that implement Calamares::Job. return [DummyPQJob("Dummy PythonQt job reporting for duty")] def widget(self): + # Returns the base QWidget of this module's UI. return self.main_widget def retranslate(self, locale_name): + # This is where it gets slightly weird. In most desktop applications we + # shouldn't need this kind of mechanism, because we could assume that + # the operating environment is configured to use a certain language. + # Usually the user would change the system-wide language in a settings + # UI, restart the application, done. + # Alas, Calamares runs on an unconfigured live system, and one of the + # core features of Calamares is to allow the user to pick a language. + # Unfortunately, strings in the UI do not automatically react to a + # runtime language change. To get UI strings in a new language, all + # user-visible strings must be retranslated (by calling tr() in C++ or + # _() in Python) and reapplied on the relevant widgets. + # When the user picks a new UI translation language, Qt raises a QEvent + # of type LanguageChange, which propagates through the QObject hierarchy. + # By catching and reacting to this event, we can show user-visible + # strings in the new language at the right time. + # The C++ side of the Calamares PythonQt API catches the LanguageChange + # event and calls the present method. It is then up to the module + # developer to add here all the needed code to load the module's + # translation catalog for the new language (which is separate from the + # main Calamares strings catalog) and reapply any user-visible strings. calamares.utils.debug("PythonQt retranslation event " "for locale name: {}".format(locale_name)) + # First we load the catalog file for the new language... try: global _ _t = gettext.translation('dummypythonqt', @@ -104,7 +161,13 @@ class DummyPythonQtViewStep: calamares.utils.debug(e) pass + # ... and then we can call setText(_("foo")) and similar methods on + # the relevant widgets here to reapply the strings. +# An example Job class. Implements Calamares::Job. For method identifiers, the +# same rules apply as for ViewStep. No decorators are necessary here, because +# only the ViewStep implementation is the unique entry point, and a module can +# have any number of jobs. class DummyPQJob: def __init__(self, my_msg): self.my_msg = my_msg @@ -120,6 +183,7 @@ class DummyPQJob: return _("A status message for Dummy PythonQt Job.") def exec(self): + # As an example, we touch a file in the target root filesystem. rmp = calamares.global_storage['rootMountPoint'] os.system("touch {}/calamares_dpqt_was_here".format(rmp)) calamares.utils.debug("the dummy job says {}".format(self.my_msg))