1 /* === This file is part of Calamares - <https://calamares.io> ===
2 *
3 * SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
4 * SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
5 * SPDX-FileCopyrightText: 2018 Raul Rodrigo Segura <raurodse@gmail.com>
6 * SPDX-License-Identifier: GPL-3.0-or-later
7 *
8 * Calamares is Free Software: see the License-Identifier above.
9 *
10 */
11
12 #include "PythonQtViewModule.h"
13
14 #include "CalamaresConfig.h"
15 #include "GlobalStorage.h"
16 #include "JobQueue.h"
17 #include "ViewManager.h"
18 #include "utils/Logger.h"
19 #include "viewpages/PythonQtGlobalStorageWrapper.h"
20 #include "viewpages/PythonQtUtilsWrapper.h"
21 #include "viewpages/PythonQtViewStep.h"
22 #include "viewpages/ViewStep.h"
23
24 #include <PythonQt.h>
25 #include <PythonQt_QtAll.h>
26
27 #include <QDir>
28 #include <QPointer>
29
30
31 static QPointer< GlobalStorage > s_gs = nullptr;
32 static QPointer< Utils > s_utils = nullptr;
33
34 namespace Calamares
35 {
36
37 Module::Type
type() const38 PythonQtViewModule::type() const
39 {
40 return Module::Type::View;
41 }
42
43
44 Module::Interface
interface() const45 PythonQtViewModule::interface() const
46 {
47 return Module::Interface::PythonQt;
48 }
49
50
51 void
loadSelf()52 PythonQtViewModule::loadSelf()
53 {
54 if ( !m_scriptFileName.isEmpty() )
55 {
56 if ( PythonQt::self() == nullptr )
57 {
58 if ( Py_IsInitialized() )
59 PythonQt::init( PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut
60 | PythonQt::PythonAlreadyInitialized );
61 else
62 {
63 PythonQt::init();
64 }
65
66 PythonQt_QtAll::init();
67 cDebug() << "Initializing PythonQt bindings."
68 << "This should only happen once.";
69
70 //TODO: register classes here into the PythonQt environment, like this:
71 //PythonQt::self()->registerClass( &PythonQtViewStep::staticMetaObject,
72 // "calamares" );
73
74 // We only do the following to force PythonQt to create a submodule
75 // "calamares" for us to put our static objects in
76 PythonQt::self()->registerClass( &::GlobalStorage::staticMetaObject, "calamares" );
77
78 // Get a PythonQtObjectPtr to the PythonQt.calamares submodule
79 PythonQtObjectPtr pqtm = PythonQt::priv()->pythonQtModule();
80 PythonQtObjectPtr cala = PythonQt::self()->lookupObject( pqtm, "calamares" );
81
82 // Prepare GlobalStorage object, in module PythonQt.calamares
83 if ( !s_gs )
84 {
85 s_gs = new ::GlobalStorage( Calamares::JobQueue::instance()->globalStorage() );
86 }
87 cala.addObject( "global_storage", s_gs );
88
89 // Prepare Utils object, in module PythonQt.calamares
90 if ( !s_utils )
91 {
92 s_utils = new ::Utils( Calamares::JobQueue::instance()->globalStorage() );
93 }
94 cala.addObject( "utils", s_utils );
95
96 // Append configuration object, in module PythonQt.calamares
97 cala.addVariable( "configuration", m_configurationMap );
98
99 // Basic stdout/stderr handling
100 QObject::connect( PythonQt::self(), &PythonQt::pythonStdOut, []( const QString& message ) {
101 cDebug() << "PythonQt OUT>" << message;
102 } );
103 QObject::connect( PythonQt::self(), &PythonQt::pythonStdErr, []( const QString& message ) {
104 cDebug() << "PythonQt ERR>" << message;
105 } );
106 }
107
108 QDir workingDir( m_workingPath );
109 if ( !workingDir.exists() )
110 {
111 cDebug() << "Invalid working directory" << m_workingPath << "for module" << name();
112 return;
113 }
114
115 QString fullPath = workingDir.absoluteFilePath( m_scriptFileName );
116 QFileInfo scriptFileInfo( fullPath );
117 if ( !scriptFileInfo.isReadable() )
118 {
119 cDebug() << "Invalid main script file path" << fullPath << "for module" << name();
120 return;
121 }
122
123 // Construct empty Python module with the given name
124 PythonQtObjectPtr cxt = PythonQt::self()->createModuleFromScript( name() );
125 if ( cxt.isNull() )
126 {
127 cDebug() << "Cannot load PythonQt context from file" << fullPath << "for module" << name();
128 return;
129 }
130
131 static const QLatin1String calamares_module_annotation(
132 "_calamares_module_typename = ''\n"
133 "def calamares_module(viewmodule_type):\n"
134 " global _calamares_module_typename\n"
135 " _calamares_module_typename = viewmodule_type.__name__\n"
136 " return viewmodule_type\n" );
137
138 // Load in the decorator
139 PythonQt::self()->evalScript( cxt, calamares_module_annotation );
140
141 // Load the module
142 PythonQt::self()->evalFile( cxt, fullPath );
143
144 m_viewStep = new PythonQtViewStep( cxt );
145
146 cDebug() << "PythonQtViewModule loading self for instance" << instanceKey() << "\nPythonQtViewModule at address"
147 << this << "\nViewStep at address" << m_viewStep;
148
149 m_viewStep->setModuleInstanceKey( instanceKey() );
150 m_viewStep->setConfigurationMap( m_configurationMap );
151 ViewManager::instance()->addViewStep( m_viewStep );
152 m_loaded = true;
153 cDebug() << "PythonQtViewModule" << instanceKey() << "loading complete.";
154 }
155 }
156
157
158 JobList
jobs() const159 PythonQtViewModule::jobs() const
160 {
161 return m_viewStep->jobs();
162 }
163
164
165 void
initFrom(const QVariantMap & moduleDescriptor)166 PythonQtViewModule::initFrom( const QVariantMap& moduleDescriptor )
167 {
168 QDir directory( location() );
169 m_workingPath = directory.absolutePath();
170
171 if ( !moduleDescriptor.value( "script" ).toString().isEmpty() )
172 {
173 m_scriptFileName = moduleDescriptor.value( "script" ).toString();
174 }
175 }
176
PythonQtViewModule()177 PythonQtViewModule::PythonQtViewModule()
178 : Module()
179 {
180 }
181
~PythonQtViewModule()182 PythonQtViewModule::~PythonQtViewModule() {}
183
184 } // namespace Calamares
185