Merge pythonqt branch

This commit is contained in:
Philip 2016-10-31 07:45:35 +01:00
parent 095e5317cb
commit 4752f70809
30 changed files with 1550 additions and 36 deletions

View File

@ -55,8 +55,9 @@ find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Core Gui Widgets LinguistTools S
find_package( YAMLCPP 0.5.1 REQUIRED ) find_package( YAMLCPP 0.5.1 REQUIRED )
find_package( PolkitQt5-1 REQUIRED ) find_package( PolkitQt5-1 REQUIRED )
option( WITH_PYTHON "Enable Python modules support." ON ) option( WITH_PYTHON "Enable Python modules API (requires Boost.Python)." ON )
option( WITH_CRASHREPORTER "Build with CrashReporter" ON ) option( WITH_CRASHREPORTER "Build with CrashReporter" ON )
option( WITH_PYTHONQT "Enable next generation Python modules API (experimental, requires PythonQt)." OFF )
if( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/libcrashreporter-qt/CMakeLists.txt" ) if( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/libcrashreporter-qt/CMakeLists.txt" )
message( STATUS "Build of crashreporter disabled." ) message( STATUS "Build of crashreporter disabled." )
@ -84,11 +85,23 @@ if ( PYTHONLIBS_FOUND )
FALSE "1.54.0" FALSE "1.54.0"
"Boost.Python is used for interfacing with Calamares job modules written in Python 3." "Boost.Python is used for interfacing with Calamares job modules written in Python 3."
) )
macro_optional_find_package( PythonQt )
macro_log_feature( PYTHONQT_FOUND
"PythonQt"
"A Python embedding solution for Qt applications."
"http://pythonqt.sourceforge.net"
FALSE "3.1"
"PythonQt is used for the Python modules API."
)
endif() endif()
if ( PYTHONLIBS_NOTFOUND OR NOT CALAMARES_BOOST_PYTHON3_FOUND ) if ( PYTHONLIBS_NOTFOUND OR NOT CALAMARES_BOOST_PYTHON3_FOUND )
set( WITH_PYTHON OFF ) set( WITH_PYTHON OFF )
endif() endif()
if ( PYTHONLIBS_NOTFOUND OR NOT PYTHONQT_FOUND )
set( WITH_PYTHONQT OFF )
endif()
### ###
### Calamares application info ### Calamares application info
@ -167,8 +180,12 @@ add_subdirectory( src )
macro_display_feature_log() macro_display_feature_log()
if ( NOT WITH_PYTHON ) if ( NOT WITH_PYTHON )
message( "-- WARNING: Building Calamares without Python support. Python modules will not work.\n" ) message( "-- WARNING: Building Calamares without Python support. Legacy Python job modules will not work.\n" )
endif() endif()
if ( NOT WITH_PYTHONQT )
message( "-- WARNING: Building Calamares without PythonQt support. Python modules will not work.\n" )
endif()
# Add all targets to the build-tree export set # Add all targets to the build-tree export set
set( CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/Calamares" CACHE PATH "Installation directory for CMake files" ) set( CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/Calamares" CACHE PATH "Installation directory for CMake files" )

View File

@ -0,0 +1,69 @@
# Find PythonQt
#
# Sets PYTHONQT_FOUND, PYTHONQT_INCLUDE_DIR, PYTHONQT_LIBRARY, PYTHONQT_LIBRARIES
#
# Python is required
find_package(PythonLibs)
if(NOT PYTHONLIBS_FOUND)
message(FATAL_ERROR "error: Python is required to build PythonQt")
endif()
if(NOT EXISTS "${PYTHONQT_INSTALL_DIR}")
find_path(PYTHONQT_INSTALL_DIR include/PythonQt/PythonQt.h DOC "Directory where PythonQt was installed.")
endif()
# XXX Since PythonQt 3.0 is not yet cmakeified, depending
# on how PythonQt is built, headers will not always be
# installed in "include/PythonQt". That is why "src"
# is added as an option. See [1] for more details.
# [1] https://github.com/commontk/CTK/pull/538#issuecomment-86106367
find_path(PYTHONQT_INCLUDE_DIR PythonQt.h
PATHS "${PYTHONQT_INSTALL_DIR}/include/PythonQt"
"${PYTHONQT_INSTALL_DIR}/src"
DOC "Path to the PythonQt include directory")
find_library(PYTHONQT_LIBRARY_RELEASE PythonQt PATHS "${PYTHONQT_INSTALL_DIR}/lib" DOC "The PythonQt library.")
find_library(PYTHONQT_LIBRARY_DEBUG NAMES PythonQt${CTK_CMAKE_DEBUG_POSTFIX} PythonQt${CMAKE_DEBUG_POSTFIX} PythonQt PATHS "${PYTHONQT_INSTALL_DIR}/lib" DOC "The PythonQt library.")
find_library(PYTHONQT_QTALL_LIBRARY_RELEASE PythonQt_QtAll PATHS "${PYTHONQT_INSTALL_DIR}/lib" DOC "Full Qt bindings for the PythonQt library.")
find_library(PYTHONQT_QTALL_LIBRARY_DEBUG NAMES PythonQt_QtAll${CTK_CMAKE_DEBUG_POSTFIX} PythonQt_QtAll${CMAKE_DEBUG_POSTFIX} PythonQt_QtAll PATHS "${PYTHONQT_INSTALL_DIR}/lib" DOC "Full Qt bindings for the PythonQt library.")
set(PYTHONQT_LIBRARY)
if(PYTHONQT_LIBRARY_RELEASE)
list(APPEND PYTHONQT_LIBRARY optimized ${PYTHONQT_LIBRARY_RELEASE})
endif()
if(PYTHONQT_LIBRARY_DEBUG)
list(APPEND PYTHONQT_LIBRARY debug ${PYTHONQT_LIBRARY_DEBUG})
endif()
set(PYTHONQT_QTALL_LIBRARY)
if(PYTHONQT_QTALL_LIBRARY_RELEASE)
list(APPEND PYTHONQT_QTALL_LIBRARY optimized ${PYTHONQT_QTALL_LIBRARY_RELEASE})
endif()
if(PYTHONQT_QTALL_LIBRARY_DEBUG)
list(APPEND PYTHONQT_QTALL_LIBRARY debug ${PYTHONQT_QTALL_LIBRARY_DEBUG})
endif()
mark_as_advanced(PYTHONQT_INSTALL_DIR)
mark_as_advanced(PYTHONQT_INCLUDE_DIR)
mark_as_advanced(PYTHONQT_LIBRARY_RELEASE)
mark_as_advanced(PYTHONQT_LIBRARY_DEBUG)
mark_as_advanced(PYTHONQT_QTALL_LIBRARY_RELEASE)
mark_as_advanced(PYTHONQT_QTALL_LIBRARY_DEBUG)
# On linux, also find libutil
if(UNIX AND NOT APPLE)
find_library(PYTHONQT_LIBUTIL util)
mark_as_advanced(PYTHONQT_LIBUTIL)
endif()
# All upper case _FOUND variable is maintained for backwards compatibility.
set(PYTHONQT_FOUND 0)
set(PythonQt_FOUND 0)
if(PYTHONQT_INCLUDE_DIR AND PYTHONQT_LIBRARY AND PYTHONQT_QTALL_LIBRARY)
# Currently CMake'ified PythonQt only supports building against a python Release build.
# This applies independently of CTK build type (Release, Debug, ...)
add_definitions(-DPYTHONQT_USE_RELEASE_PYTHON_FALLBACK)
set(PYTHONQT_FOUND 1)
set(PythonQt_FOUND ${PYTHONQT_FOUND})
set(PYTHONQT_LIBRARIES ${PYTHONQT_LIBRARY} ${PYTHONQT_LIBUTIL} ${PYTHONQT_QTALL_LIBRARY})
endif()

View File

@ -58,6 +58,20 @@ if( WITH_PYTHON )
) )
endif() endif()
if( WITH_PYTHONQT )
include_directories(${PYTHON_INCLUDE_DIRS})
link_directories(${PYTHON_LIBRARIES})
include_directories(${PYTHONQT_INCLUDE_DIR})
link_directories(${PYTHONQT_LIBRARY})
set( OPTIONAL_PRIVATE_LIBRARIES
${OPTIONAL_PRIVATE_LIBRARIES}
${PYTHON_LIBRARIES}
${PYTHONQT_LIBRARY}
)
endif()
add_library( calamares SHARED ${libSources} ) add_library( calamares SHARED ${libSources} )
set_target_properties( calamares set_target_properties( calamares

View File

@ -11,5 +11,6 @@
//cmakedefines for CMake variables (e.g. for optdepends) go here //cmakedefines for CMake variables (e.g. for optdepends) go here
#cmakedefine WITH_PYTHON #cmakedefine WITH_PYTHON
#cmakedefine WITH_CRASHREPORTER #cmakedefine WITH_CRASHREPORTER
#cmakedefine WITH_PYTHONQT
#endif // CALAMARESCONFIG_H #endif // CALAMARESCONFIG_H

View File

@ -29,37 +29,33 @@ namespace Calamares {
class DLLEXPORT JobResult class DLLEXPORT JobResult
{ {
public: public:
operator bool() const; virtual ~JobResult() {}
QString message() const; virtual operator bool() const;
void setMessage( const QString& message );
QString details() const; virtual QString message() const;
void setDetails( const QString& details ); virtual void setMessage( const QString& message );
virtual QString details() const;
virtual void setDetails( const QString& details );
static JobResult ok(); static JobResult ok();
static JobResult error( const QString& message, const QString& details = QString() ); static JobResult error( const QString& message, const QString& details = QString() );
protected:
explicit JobResult( bool ok, const QString& message, const QString& details );
private: private:
bool m_ok; bool m_ok;
QString m_message; QString m_message;
QString m_details; QString m_details;
JobResult( bool ok, const QString& message, const QString& details );
}; };
class DLLEXPORT Job : public QObject class DLLEXPORT Job : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
enum State
{
Pending = 0,
Running,
Finished
};
explicit Job( QObject* parent = nullptr ); explicit Job( QObject* parent = nullptr );
virtual ~Job(); virtual ~Job();

View File

@ -189,7 +189,8 @@ Helper::Helper( QObject* parent )
// Let's make extra sure we only call Py_Initialize once // Let's make extra sure we only call Py_Initialize once
if ( !s_instance ) if ( !s_instance )
{ {
Py_Initialize(); if ( !Py_IsInitialized() )
Py_Initialize();
m_mainModule = bp::import( "__main__" ); m_mainModule = bp::import( "__main__" );
m_mainNamespace = m_mainModule.attr( "__dict__" ); m_mainNamespace = m_mainModule.attr( "__dict__" );

View File

@ -39,6 +39,25 @@ if( WITH_PYTHON )
) )
endif() endif()
if( WITH_PYTHONQT )
include_directories(${PYTHON_INCLUDE_DIRS})
include_directories(${PYTHONQT_INCLUDE_DIR})
list( APPEND ${CALAMARESUI_LIBRARY_TARGET}_SOURCES
modulesystem/PythonQtViewModule.cpp
utils/PythonQtUtils.cpp
viewpages/PythonQtJob.cpp
viewpages/PythonQtViewStep.cpp
viewpages/PythonQtGlobalStorageWrapper.cpp
viewpages/PythonQtUtilsWrapper.cpp
)
set( OPTIONAL_PRIVATE_LIBRARIES
${OPTIONAL_PRIVATE_LIBRARIES}
${PYTHON_LIBRARIES}
${PYTHONQT_LIBRARIES}
)
endif()
calamares_add_library( ${CALAMARESUI_LIBRARY_TARGET} calamares_add_library( ${CALAMARESUI_LIBRARY_TARGET}
SOURCES ${${CALAMARESUI_LIBRARY_TARGET}_SOURCES} SOURCES ${${CALAMARESUI_LIBRARY_TARGET}_SOURCES}
UI ${${CALAMARESUI_LIBRARY_TARGET}_UI} UI ${${CALAMARESUI_LIBRARY_TARGET}_UI}
@ -47,6 +66,7 @@ calamares_add_library( ${CALAMARESUI_LIBRARY_TARGET}
yaml-cpp yaml-cpp
Qt5::Svg Qt5::Svg
Qt5::QuickWidgets Qt5::QuickWidgets
${OPTIONAL_PRIVATE_LIBRARIES}
RESOURCES libcalamaresui.qrc RESOURCES libcalamaresui.qrc
EXPORT CalamaresLibraryDepends EXPORT CalamaresLibraryDepends
VERSION ${CALAMARES_VERSION_SHORT} VERSION ${CALAMARES_VERSION_SHORT}

View File

@ -39,7 +39,7 @@ CppJobModule::type() const
Module::Interface Module::Interface
CppJobModule::interface() const CppJobModule::interface() const
{ {
return QtPlugin; return QtPluginInterface;
} }

View File

@ -31,6 +31,10 @@
#include "PythonJobModule.h" #include "PythonJobModule.h"
#endif #endif
#ifdef WITH_PYTHONQT
#include "PythonQtViewModule.h"
#endif
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
#include <QDir> #include <QDir>
@ -71,9 +75,18 @@ Module::fromDescriptor( const QVariantMap& moduleDescriptor,
<< instanceId; << instanceId;
return nullptr; return nullptr;
} }
if ( typeString == "view" && intfString == "qtplugin" ) if ( typeString == "view" )
{ {
m = new ViewModule(); if ( intfString == "qtplugin" )
{
m = new ViewModule();
}
#ifdef WITH_PYTHONQT
else if ( intfString == "pythonqt" )
{
m = new PythonQtViewModule();
}
#endif
} }
else if ( typeString == "job" ) else if ( typeString == "job" )
{ {
@ -215,6 +228,38 @@ Module::location() const
} }
QString
Module::typeString() const
{
switch ( type() )
{
case Job:
return "Job Module";
case View:
return "View Module";
}
return QString();
}
QString
Module::interfaceString() const
{
switch ( interface() )
{
case ProcessInterface:
return "External process";
case PythonInterface:
return "Python (Boost.Python)";
case PythonQtInterface:
return "Python (experimental)";
case QtPluginInterface:
return "Qt Plugin";
}
return QString();
}
bool bool
Module::isLoaded() const Module::isLoaded() const
{ {

View File

@ -48,9 +48,10 @@ public:
enum Interface enum Interface
{ {
QtPlugin, QtPluginInterface,
Python, PythonInterface,
Process ProcessInterface,
PythonQtInterface
}; };
virtual ~Module(); virtual ~Module();
@ -65,7 +66,9 @@ public:
virtual QStringList requiredModules() const; virtual QStringList requiredModules() const;
virtual QString location() const final; virtual QString location() const final;
virtual Type type() const = 0; virtual Type type() const = 0;
virtual QString typeString() const;
virtual Interface interface() const = 0; virtual Interface interface() const = 0;
virtual QString interfaceString() const;
virtual bool isLoaded() const; virtual bool isLoaded() const;

View File

@ -35,7 +35,7 @@ ProcessJobModule::type() const
Module::Interface Module::Interface
ProcessJobModule::interface() const ProcessJobModule::interface() const
{ {
return Process; return ProcessInterface;
} }

View File

@ -36,7 +36,7 @@ PythonJobModule::type() const
Module::Interface Module::Interface
PythonJobModule::interface() const PythonJobModule::interface() const
{ {
return Python; return PythonInterface;
} }

View File

@ -0,0 +1,203 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PythonQtViewModule.h"
#include "utils/Logger.h"
#include "viewpages/ViewStep.h"
#include "viewpages/PythonQtViewStep.h"
#include "ViewManager.h"
#include "CalamaresConfig.h"
#include "viewpages/PythonQtGlobalStorageWrapper.h"
#include "viewpages/PythonQtUtilsWrapper.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include <PythonQt.h>
#include <extensions/PythonQt_QtAll/PythonQt_QtAll.h>
#include <QDir>
#include <QPointer>
static QPointer< GlobalStorage > s_gs = nullptr;
static QPointer< Utils > s_utils = nullptr;
namespace Calamares {
Module::Type
PythonQtViewModule::type() const
{
return View;
}
Module::Interface
PythonQtViewModule::interface() const
{
return PythonQtInterface;
}
void
PythonQtViewModule::loadSelf()
{
if ( !m_scriptFileName.isEmpty() )
{
if ( PythonQt::self() == nullptr )
{
if ( Py_IsInitialized() )
PythonQt::init( PythonQt::IgnoreSiteModule |
PythonQt::RedirectStdOut |
PythonQt::PythonAlreadyInitialized );
else
PythonQt::init();
PythonQt_QtAll::init();
cDebug() << "Initializing PythonQt bindings."
<< "This should only happen once.";
//TODO: register classes here into the PythonQt environment, like this:
//PythonQt::self()->registerClass( &PythonQtViewStep::staticMetaObject,
// "calamares" );
// We only do the following to force PythonQt to create a submodule
// "calamares" for us to put our static objects in
PythonQt::self()->registerClass( &::GlobalStorage::staticMetaObject,
"calamares" );
// Get a PythonQtObjectPtr to the PythonQt.calamares submodule
PythonQtObjectPtr pqtm = PythonQt::priv()->pythonQtModule();
PythonQtObjectPtr cala = PythonQt::self()->lookupObject( pqtm, "calamares" );
// Prepare GlobalStorage object, in module PythonQt.calamares
if ( !s_gs )
s_gs = new ::GlobalStorage( Calamares::JobQueue::instance()->globalStorage() );
cala.addObject( "global_storage", s_gs );
// Prepare Utils object, in module PythonQt.calamares
if ( !s_utils )
s_utils = new ::Utils( Calamares::JobQueue::instance()->globalStorage() );
cala.addObject( "utils", s_utils );
// Basic stdout/stderr handling
QObject::connect( PythonQt::self(), &PythonQt::pythonStdOut,
[]( const QString& message )
{
cDebug() << "PythonQt OUT>" << message;
} );
QObject::connect( PythonQt::self(), &PythonQt::pythonStdErr,
[]( const QString& message )
{
cDebug() << "PythonQt ERR>" << message;
} );
}
QDir workingDir( m_workingPath );
if ( !workingDir.exists() )
{
cDebug() << "Invalid working directory"
<< m_workingPath
<< "for module"
<< name();
return;
}
QString fullPath = workingDir.absoluteFilePath( m_scriptFileName );
QFileInfo scriptFileInfo( fullPath );
if ( !scriptFileInfo.isReadable() )
{
cDebug() << "Invalid main script file path"
<< fullPath
<< "for module"
<< name();
return;
}
// Construct empty Python module with the given name
PythonQtObjectPtr cxt =
PythonQt::self()->
createModuleFromScript( name() );
if ( cxt.isNull() )
{
cDebug() << "Cannot load PythonQt context from file"
<< fullPath
<< "for module"
<< name();
return;
}
QString calamares_module_annotation =
"_calamares_module_typename = 'foo'\n"
"def calamares_module(viewmodule_type):\n"
" global _calamares_module_typename\n"
" _calamares_module_typename = viewmodule_type.__name__\n"
" return viewmodule_type\n";
// Load in the decorator
PythonQt::self()->evalScript( cxt, calamares_module_annotation );
// Load the module
PythonQt::self()->evalFile( cxt, fullPath );
m_viewStep = new PythonQtViewStep( cxt );
cDebug() << "PythonQtViewModule loading self for instance" << instanceKey()
<< "\nPythonQtViewModule at address" << this
<< "\nViewStep at address" << m_viewStep;
m_viewStep->setModuleInstanceKey( instanceKey() );
m_viewStep->setConfigurationMap( m_configurationMap );
ViewManager::instance()->addViewStep( m_viewStep );
m_loaded = true;
cDebug() << "PythonQtViewModule" << instanceKey() << "loading complete.";
}
}
QList< job_ptr >
PythonQtViewModule::jobs() const
{
return m_viewStep->jobs();
}
void
PythonQtViewModule::initFrom( const QVariantMap& moduleDescriptor )
{
Module::initFrom( moduleDescriptor );
QDir directory( location() );
m_workingPath = directory.absolutePath();
if ( !moduleDescriptor.value( "script" ).toString().isEmpty() )
{
m_scriptFileName = moduleDescriptor.value( "script" ).toString();
}
}
PythonQtViewModule::PythonQtViewModule()
: Module()
{
}
PythonQtViewModule::~PythonQtViewModule()
{
}
} // namespace Calamares

View File

@ -0,0 +1,54 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALAMARES_PYTHONQTVIEWMODULE_H
#define CALAMARES_PYTHONQTVIEWMODULE_H
#include "UiDllMacro.h"
#include "Module.h"
namespace Calamares {
class ViewStep;
class UIDLLEXPORT PythonQtViewModule : public Module
{
public:
Type type() const override;
Interface interface() const override;
void loadSelf() override;
QList< job_ptr > jobs() const override;
protected:
void initFrom( const QVariantMap& moduleDescriptor ) override;
private:
friend class Module; //so only the superclass can instantiate
explicit PythonQtViewModule();
virtual ~PythonQtViewModule();
ViewStep* m_viewStep = nullptr;
QString m_scriptFileName;
QString m_workingPath;
};
} // namespace Calamares
#endif // CALAMARES_PYTHONQTVIEWMODULE_H

View File

@ -39,7 +39,7 @@ ViewModule::type() const
Module::Interface Module::Interface
ViewModule::interface() const ViewModule::interface() const
{ {
return QtPlugin; return QtPluginInterface;
} }

View File

@ -26,6 +26,12 @@
#include "modulesystem/ModuleManager.h" #include "modulesystem/ModuleManager.h"
#include "modulesystem/Module.h" #include "modulesystem/Module.h"
#ifdef WITH_PYTHONQT
#include <gui/PythonQtScriptingConsole.h>
#include "ViewManager.h"
#include "viewpages/PythonQtViewStep.h"
#endif
#include <QJsonDocument> #include <QJsonDocument>
#include <QSplitter> #include <QSplitter>
#include <QStringListModel> #include <QStringListModel>
@ -68,11 +74,6 @@ DebugWindow::DebugWindow()
} ); } );
// Modules page // Modules page
QSplitter* splitter = new QSplitter( modulesTab );
modulesTab->layout()->addWidget( splitter );
splitter->addWidget( modulesListView );
splitter->addWidget( moduleConfigView );
QStringListModel* modulesModel = new QStringListModel( ModuleManager::instance()->loadedInstanceKeys() ); QStringListModel* modulesModel = new QStringListModel( ModuleManager::instance()->loadedInstanceKeys() );
modulesListView->setModel( modulesModel ); modulesListView->setModel( modulesModel );
modulesListView->setSelectionMode( QAbstractItemView::SingleSelection ); modulesListView->setSelectionMode( QAbstractItemView::SingleSelection );
@ -80,8 +81,76 @@ DebugWindow::DebugWindow()
QJsonModel* moduleConfigModel = new QJsonModel( this ); QJsonModel* moduleConfigModel = new QJsonModel( this );
moduleConfigView->setModel( moduleConfigModel ); moduleConfigView->setModel( moduleConfigModel );
#ifdef WITH_PYTHONQT
QPushButton* pythonConsoleButton = new QPushButton;
pythonConsoleButton->setText( "Attach Python console" );
modulesVerticalLayout->insertWidget( 1, pythonConsoleButton );
pythonConsoleButton->hide();
QObject::connect( pythonConsoleButton, &QPushButton::clicked,
this, [ this, moduleConfigModel ]
{
QString moduleName = modulesListView->currentIndex().data().toString();
Module* module = ModuleManager::instance()->moduleInstance( moduleName );
if ( module->interface() != Module::PythonQtInterface ||
module->type() != Module::View )
return;
for ( ViewStep* step : ViewManager::instance()->viewSteps() )
{
if ( step->moduleInstanceKey() == module->instanceKey() )
{
PythonQtViewStep* pqvs =
qobject_cast< PythonQtViewStep* >( step );
if ( pqvs )
{
QWidget* consoleWindow = new QWidget;
QWidget* console = pqvs->createScriptingConsole();
console->setParent( consoleWindow );
QVBoxLayout* layout = new QVBoxLayout;
consoleWindow->setLayout( layout );
layout->addWidget( console );
QHBoxLayout* bottomLayout = new QHBoxLayout;
layout->addLayout( bottomLayout );
QLabel* bottomLabel = new QLabel( consoleWindow );
bottomLayout->addWidget( bottomLabel );
QString line =
QString( "Module: <font color=\"#008000\"><code>%1</code></font><br/>"
"Python class: <font color=\"#008000\"><code>%2</code></font>" )
.arg( module->instanceKey() )
.arg( console->property( "classname" ).toString() );
bottomLabel->setText( line );
QPushButton* closeButton = new QPushButton( consoleWindow );
closeButton->setText( "&Close" );
QObject::connect( closeButton, &QPushButton::clicked,
[ consoleWindow ]
{
consoleWindow->close();
} );
bottomLayout->addWidget( closeButton );
bottomLabel->setSizePolicy( QSizePolicy::Expanding,
QSizePolicy::Preferred );
consoleWindow->setParent( this );
consoleWindow->setWindowFlags( Qt::Window );
consoleWindow->setWindowTitle( "Calamares Python console" );
consoleWindow->setAttribute( Qt::WA_DeleteOnClose, true );
consoleWindow->showNormal();
break;
}
}
}
} );
#endif
connect( modulesListView->selectionModel(), &QItemSelectionModel::selectionChanged, connect( modulesListView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, [ this, moduleConfigModel ] this, [ this, moduleConfigModel, pythonConsoleButton ]
{ {
QString moduleName = modulesListView->currentIndex().data().toString(); QString moduleName = modulesListView->currentIndex().data().toString();
Module* module = ModuleManager::instance()->moduleInstance( moduleName ); Module* module = ModuleManager::instance()->moduleInstance( moduleName );
@ -89,6 +158,13 @@ DebugWindow::DebugWindow()
{ {
moduleConfigModel->loadJson( QJsonDocument::fromVariant( module->configurationMap() ).toJson() ); moduleConfigModel->loadJson( QJsonDocument::fromVariant( module->configurationMap() ).toJson() );
moduleConfigView->expandAll(); moduleConfigView->expandAll();
moduleTypeLabel->setText( module->typeString() );
moduleInterfaceLabel->setText( module->interfaceString() );
#ifdef WITH_PYTHONQT
pythonConsoleButton->setVisible(
module->interface() == Module::PythonQtInterface &&
module->type() == Module::View );
#endif
} }
} ); } );

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>632</width> <width>962</width>
<height>497</height> <height>651</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -17,7 +17,7 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="globalStorageTab"> <widget class="QWidget" name="globalStorageTab">
<attribute name="title"> <attribute name="title">
@ -48,7 +48,43 @@
<widget class="QListView" name="modulesListView"/> <widget class="QListView" name="modulesListView"/>
</item> </item>
<item> <item>
<widget class="QTreeView" name="moduleConfigView"/> <layout class="QVBoxLayout" name="modulesVerticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="moduleTypeLabel">
<property name="text">
<string>none</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Interface:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="moduleInterfaceLabel">
<property name="text">
<string>none</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="moduleConfigView"/>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@ -0,0 +1,44 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PythonQtUtils.h"
namespace CalamaresUtils
{
QVariant
lookupAndCall( PyObject* object,
const QStringList& candidateNames,
const QVariantList& args,
const QVariantMap& kwargs )
{
Q_ASSERT( object );
Q_ASSERT( !candidateNames.isEmpty() );
for ( const QString& name : candidateNames )
{
PythonQtObjectPtr callable = PythonQt::self()->lookupCallable( object, name );
if ( callable )
return callable.call( args, kwargs );
}
// If we haven't found a callable with the given names, we force an error:
return PythonQt::self()->call( object, candidateNames.first(), args, kwargs );
}
}

View File

@ -0,0 +1,38 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PYTHONQTUTILS_H
#define PYTHONQTUTILS_H
#include <PythonQt.h>
#include <QVariant>
namespace CalamaresUtils
{
//NOTE: when running this, it is assumed that Python is initialized and
// PythonQt::self() is valid.
QVariant lookupAndCall( PyObject* object,
const QStringList& candidateNames,
const QVariantList& args = QVariantList(),
const QVariantMap& kwargs = QVariantMap() );
} //ns
#endif // PYTHONQTUTILS_H

View File

@ -0,0 +1,69 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PythonQtGlobalStorageWrapper.h"
#include "GlobalStorage.h"
GlobalStorage::GlobalStorage( Calamares::GlobalStorage* gs )
: QObject( gs )
, m_gs( gs )
{}
bool
GlobalStorage::contains( const QString& key ) const
{
return m_gs->contains( key );
}
int
GlobalStorage::count() const
{
return m_gs->count();
}
void
GlobalStorage::insert( const QString& key, const QVariant& value )
{
m_gs->insert( key, value );
}
QStringList
GlobalStorage::keys() const
{
return m_gs->keys();
}
int
GlobalStorage::remove( const QString& key )
{
return m_gs->remove( key );
}
QVariant
GlobalStorage::value( const QString& key ) const
{
return m_gs->value( key );
}

View File

@ -0,0 +1,55 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PYTHONQTGLOBALSTORAGEWRAPPER_H
#define PYTHONQTGLOBALSTORAGEWRAPPER_H
#include <QObject>
namespace Calamares
{
class GlobalStorage;
}
/**
* @brief This GlobalStorage class is a namespace-free wrapper for
* Calamares::GlobalStorage. This is unfortunately a necessity
* because PythonQt doesn't like namespaces.
*/
class GlobalStorage : public QObject
{
Q_OBJECT
public:
explicit GlobalStorage( Calamares::GlobalStorage* gs );
virtual ~GlobalStorage() {}
public slots:
bool contains( const QString& key ) const;
int count() const;
void insert( const QString& key, const QVariant& value );
QStringList keys() const;
int remove( const QString& key );
QVariant value( const QString& key ) const;
private:
Calamares::GlobalStorage* m_gs;
};
#endif // PYTHONQTGLOBALSTORAGEWRAPPER_H

View File

@ -0,0 +1,77 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PythonQtJob.h"
#include "utils/PythonQtUtils.h"
PythonQtJob::PythonQtJob( PythonQtObjectPtr cxt,
PythonQtObjectPtr pyJob,
QObject* parent )
: Calamares::Job( parent )
, m_cxt( cxt )
, m_pyJob( pyJob )
{
}
QString
PythonQtJob::prettyName() const
{
return CalamaresUtils::lookupAndCall( m_pyJob,
{ "prettyName",
"prettyname",
"pretty_name" } ).toString();
}
QString
PythonQtJob::prettyDescription() const
{
return CalamaresUtils::lookupAndCall( m_pyJob,
{ "prettyDescription",
"prettydescription",
"pretty_description" } ).toString();
}
QString
PythonQtJob::prettyStatusMessage() const
{
return CalamaresUtils::lookupAndCall( m_pyJob,
{ "prettyStatusMessage",
"prettystatusmessage",
"pretty_status_message" } ).toString();
}
Calamares::JobResult
PythonQtJob::exec()
{
QVariant response = m_pyJob.call( "exec" );
if ( response.isNull() )
return Calamares::JobResult::ok();
QVariantMap map = response.toMap();
if ( map.isEmpty() || map.value( "ok" ).toBool() )
return Calamares::JobResult::ok();
return Calamares::JobResult::error( map.value( "message" ).toString(),
map.value( "details" ).toString() );
}

View File

@ -0,0 +1,65 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PYTHONQTJOB_H
#define PYTHONQTJOB_H
#include "Job.h"
#include <PythonQt.h>
namespace Calamares
{
class PythonQtViewStep;
}
class PythonQtJobResult : public QObject, public Calamares::JobResult
{
Q_OBJECT
public:
explicit PythonQtJobResult( bool ok,
const QString& message,
const QString& details )
: QObject( nullptr )
, Calamares::JobResult( ok, message, details )
{}
};
class PythonQtJob : public Calamares::Job
{
Q_OBJECT
public:
virtual ~PythonQtJob() {}
QString prettyName() const override;
QString prettyDescription() const override;
QString prettyStatusMessage() const override;
Calamares::JobResult exec() override;
private:
explicit PythonQtJob( PythonQtObjectPtr cxt,
PythonQtObjectPtr pyJob,
QObject* parent = nullptr );
friend class Calamares::PythonQtViewStep; // only this one can call the ctor
PythonQtObjectPtr m_cxt;
PythonQtObjectPtr m_pyJob;
};
#endif // PYTHONQTJOB_H

View File

@ -0,0 +1,146 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PythonQtUtilsWrapper.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/CalamaresUtils.h"
#include "utils/Logger.h"
#include <PythonQt.h>
Utils::Utils(QObject* parent)
: QObject( parent )
, m_exceptionCxt( PythonQt::self()->createUniqueModule() )
{
PythonQt::self()->evalScript( m_exceptionCxt, "import subprocess" );
}
void
Utils::debug(const QString& s) const
{
cDebug() << "PythonQt DBG>" << s;
}
int
Utils::mount( const QString& device_path,
const QString& mount_point,
const QString& filesystem_name,
const QString& options ) const
{
return CalamaresUtils::System::instance()->
mount( device_path, mount_point, filesystem_name, options );
}
int
Utils::target_env_call( const QString& command,
const QString& stdin,
int timeout ) const
{
return CalamaresUtils::System::instance()->
targetEnvCall( command, QString(), stdin, timeout );
}
int
Utils::target_env_call( const QStringList& args,
const QString& stdin,
int timeout ) const
{
return CalamaresUtils::System::instance()->
targetEnvCall( args, QString(), stdin, timeout );
}
int
Utils::check_target_env_call( const QString& command,
const QString& stdin,
int timeout ) const
{
int ec = target_env_call( command, stdin, timeout );
return _handle_check_target_env_call_error( ec, command );
}
int
Utils::check_target_env_call( const QStringList& args,
const QString& stdin,
int timeout) const
{
int ec = target_env_call( args, stdin, timeout );
return _handle_check_target_env_call_error( ec, args.join( ' ' ) );
}
QString
Utils::check_target_env_output( const QString& command,
const QString& stdin,
int timeout ) const
{
QString output;
int ec = CalamaresUtils::System::instance()->
targetEnvOutput( command,
output,
QString(),
stdin,
timeout );
_handle_check_target_env_call_error( ec, command );
return output;
}
QString
Utils::check_target_env_output( const QStringList& args,
const QString& stdin,
int timeout ) const
{
QString output;
int ec = CalamaresUtils::System::instance()->
targetEnvOutput( args,
output,
QString(),
stdin,
timeout );
_handle_check_target_env_call_error( ec, args.join( ' ' ) );
return output;
}
QString
Utils::obscure( const QString& string ) const
{
return CalamaresUtils::obscure( string );
}
int
Utils::_handle_check_target_env_call_error( int ec, const QString& cmd) const
{
if ( ec )
{
QString raise = QString( "raise subprocess.CalledProcessError(%1,\"%2\")" )
.arg( ec )
.arg( cmd );
PythonQt::self()->evalScript( m_exceptionCxt, raise );
}
return ec;
}

View File

@ -0,0 +1,74 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PYTHONQTUTILSWRAPPER_H
#define PYTHONQTUTILSWRAPPER_H
#include <PythonQtObjectPtr.h>
#include <QObject>
class Utils : public QObject
{
Q_OBJECT
public:
explicit Utils( QObject* parent = nullptr );
virtual ~Utils() {}
public slots:
void debug( const QString& s ) const;
int mount( const QString& device_path,
const QString& mount_point,
const QString& filesystem_name,
const QString& options ) const;
int target_env_call( const QString& command,
const QString& stdin = QString(),
int timeout = 0 ) const;
int target_env_call( const QStringList& args,
const QString& stdin = QString(),
int timeout = 0 ) const;
int check_target_env_call( const QString& command,
const QString& stdin = QString(),
int timeout = 0 ) const;
int check_target_env_call( const QStringList& args,
const QString& stdin = QString(),
int timeout = 0 ) const;
QString check_target_env_output( const QString& command,
const QString& stdin = QString(),
int timeout = 0 ) const;
QString check_target_env_output( const QStringList& args,
const QString& stdin = QString(),
int timeout = 0 ) const;
QString obscure( const QString& string ) const;
private:
inline int _handle_check_target_env_call_error( int ec, const QString& cmd ) const;
PythonQtObjectPtr m_exceptionCxt;
};
#endif // PYTHONQTUTILSWRAPPER_H

View File

@ -0,0 +1,206 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PythonQtViewStep.h"
#include "utils/Logger.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/PythonQtUtils.h"
#include "utils/Retranslator.h"
#include "viewpages/PythonQtJob.h"
#include <gui/PythonQtScriptingConsole.h>
#include <QBoxLayout>
#include <QWidget>
namespace Calamares
{
PythonQtViewStep::PythonQtViewStep( PythonQtObjectPtr cxt,
QObject* parent )
: ViewStep( parent )
, m_widget( new QWidget() )
, m_cxt( cxt )
{
PythonQt* pq = PythonQt::self();
Q_ASSERT( pq );
// The @calamares_module decorator should have filled _calamares_module_typename
// for us.
QString className = m_cxt.getVariable( "_calamares_module_typename" ).toString();
// Instantiate an object of the class marked with @calamares_module and
// store it as _calamares_module.
pq->evalScript( m_cxt, QString( "_calamares_module = %1()" )
.arg( className ) );
m_obj = pq->lookupObject( m_cxt, "_calamares_module" );
Q_ASSERT( !m_obj.isNull() ); // no entry point, no party
// Prepare the base widget for the module's pages
m_widget->setLayout( new QVBoxLayout );
CalamaresUtils::unmarginLayout( m_widget->layout() );
m_cxt.addObject( "_calamares_module_basewidget", m_widget );
CALAMARES_RETRANSLATE(
CalamaresUtils::lookupAndCall( m_obj, { "retranslate" } );
)
}
QString
PythonQtViewStep::prettyName() const
{
return CalamaresUtils::lookupAndCall( m_obj,
{ "prettyName",
"prettyname",
"pretty_name" } ).toString();
}
QWidget*
PythonQtViewStep::widget()
{
if ( m_widget->layout()->count() > 1 )
cDebug() << "WARNING: PythonQtViewStep wrapper widget has more than 1 child. "
"This should never happen.";
bool nothingChanged = m_cxt.evalScript(
"_calamares_module.widget() in _calamares_module_basewidget.children()" ).toBool();
if ( nothingChanged )
return m_widget;
// Else, we either don't have a child widget, or we have a child widget that
// was previously set and doesn't apply any more since the Python module
// set a new one.
// First we clear the layout, which should only ever have 1 item.
// We only remove from the layout and not delete because Python is in charge
// of memory management for these widgets.
while ( m_widget->layout()->itemAt( 0 ) )
m_widget->layout()->takeAt( 0 );
m_cxt.evalScript(
"_calamares_module_basewidget.layout().addWidget(_calamares_module.widget())" );
return m_widget;
}
void
PythonQtViewStep::next()
{
CalamaresUtils::lookupAndCall( m_obj, { "next" } );
}
void
PythonQtViewStep::back()
{
CalamaresUtils::lookupAndCall( m_obj, { "back" } );
}
bool
PythonQtViewStep::isNextEnabled() const
{
return CalamaresUtils::lookupAndCall( m_obj,
{ "isNextEnabled",
"isnextenabled",
"is_next_enabled" } ).toBool();
}
bool
PythonQtViewStep::isBackEnabled() const
{
return CalamaresUtils::lookupAndCall( m_obj,
{ "isBackEnabled",
"isbackenabled",
"is_back_enabled" } ).toBool();
}
bool
PythonQtViewStep::isAtBeginning() const
{
return CalamaresUtils::lookupAndCall( m_obj,
{ "isAtBeginning",
"isatbeginning",
"is_at_beginning" } ).toBool();
}
bool
PythonQtViewStep::isAtEnd() const
{
return CalamaresUtils::lookupAndCall( m_obj,
{ "isAtEnd",
"isatend",
"is_at_end" } ).toBool();
}
QList< Calamares::job_ptr >
PythonQtViewStep::jobs() const
{
QList< Calamares::job_ptr > jobs;
PythonQtObjectPtr jobsCallable = PythonQt::self()->lookupCallable( m_obj, "jobs" );
if ( jobsCallable.isNull() )
return jobs;
PythonQtObjectPtr response = PythonQt::self()->callAndReturnPyObject( jobsCallable );
if ( response.isNull() )
return jobs;
PythonQtObjectPtr listPopCallable = PythonQt::self()->lookupCallable( response, "pop" );
if ( listPopCallable.isNull() )
return jobs;
forever
{
PythonQtObjectPtr aJob = PythonQt::self()->callAndReturnPyObject( listPopCallable, { 0 } );
if ( aJob.isNull() )
break;
jobs.append( Calamares::job_ptr( new PythonQtJob( m_cxt, aJob ) ) );
}
return jobs;
}
void
PythonQtViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
m_obj.addVariable( "configuration", configurationMap );
}
QWidget*
PythonQtViewStep::createScriptingConsole()
{
PythonQtScriptingConsole* console = new PythonQtScriptingConsole( nullptr, m_cxt );
console->setProperty( "classname",
m_cxt.getVariable( "_calamares_module_typename" ).toString() );
return console;
}
}

View File

@ -0,0 +1,65 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2016, Teo Mrnjavac <teo@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PYTHONQTVIEWSTEP_H
#define PYTHONQTVIEWSTEP_H
#include "ViewStep.h"
#include <PythonQt.h>
namespace Calamares
{
class PythonQtViewStep : public Calamares::ViewStep
{
Q_OBJECT
public:
PythonQtViewStep( PythonQtObjectPtr cxt,
QObject* parent = nullptr );
QString prettyName() const override;
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
QList< Calamares::job_ptr > jobs() const override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
QWidget* createScriptingConsole();
protected:
QWidget* m_widget;
private:
PythonQtObjectPtr m_cxt;
PythonQtObjectPtr m_obj;
};
}
#endif // PYTHONQTVIEWSTEP_H

View File

@ -0,0 +1,18 @@
---
syntax: "YAML map of anything"
example:
whats_this: "module-specific configuration"
from_where: "dummypythonqt.conf"
a_list:
- "item1"
- "item2"
- "item3"
- "item4"
a_list_of_maps:
- name: "an Item"
contents:
- "an element"
- "another element"
- name: "another item"
contents:
- "not much"

View File

@ -0,0 +1,115 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# === This file is part of Calamares - <http://github.com/calamares> ===
#
# Copyright 2016, Teo Mrnjavac <teo@kde.org>
#
# Calamares is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Calamares is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Calamares. If not, see <http://www.gnu.org/licenses/>.
import platform
from PythonQt.QtGui import *
import PythonQt.calamares as calamares
# 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
# its own catalog.
# DO use the gettext class-based API and manually alias _ as described in:
# https://docs.python.org/3.5/library/gettext.html#localizing-your-module
import gettext
import inspect
import os
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
# t = gettext.translation('dummypythonqt',
# os.path.join(path, 'lang'),
# languages=['en'])
#_ = t.lgettext
_ = gettext.gettext
# Example Python ViewModule.
# A Python ViewModule is a Python program which defines a ViewStep class.
# 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
# interface and functions as the entry point of the module.
# A ViewStep manages one or more "wizard pages" through methods like
# 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.
@calamares_module
class DummyPythonQtViewStep():
def __init__(self):
self.main_widget = QFrame()
self.main_widget.setLayout(QVBoxLayout())
label = QLabel()
self.main_widget.layout().addWidget(label)
accumulator = "\nCalamares+PythonQt running embedded Python " +\
platform.python_version()
label.text = accumulator
btn = QPushButton()
btn.setText(_("Click me!"))
self.main_widget.layout().addWidget(btn)
btn.connect("clicked(bool)", self.on_btn_clicked)
def on_btn_clicked(self):
self.main_widget.layout().addWidget(QLabel("A new QLabel."))
def prettyName(self):
return "Dummy PythonQt ViewStep"
def isNextEnabled(self):
return True
def isBackEnabled(self):
return True
def isAtBeginning(self):
return True
def isAtEnd(self):
return True
def jobs(self):
return [DummyPQJob("hi there lol")]
def widget(self):
return self.main_widget
class DummyPQJob():
def __init__(self, my_msg):
self.my_msg = my_msg
def pretty_name(self):
return _("The Dummy PythonQt Job")
def pretty_description(self):
return _("This description says that the Dummy PythonQt Job is a dummy. "
"The dummy job says: {}".format(self.my_msg))
def pretty_status_message(self):
return _("A status message for DPQ Job.")
def exec(self):
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))
return {'ok': True}

View File

@ -0,0 +1,7 @@
# Module metadata file for dummy pythonqt jobmodule
# Syntax is YAML 1.2
---
type: "view"
name: "dummypythonqt"
interface: "pythonqt"
script: "main.py" #assumed relative to the current directory