Add python bindings for RadioService, RadioModel, MergedProxyModel, TaskManager and SettingsDialog. Make sure radio services are removed from the model when they are deleted (eg. when their python script gets unloaded)

This commit is contained in:
David Sansome 2011-01-12 21:20:20 +00:00
parent 03e6231483
commit 4e0cf13b26
19 changed files with 240 additions and 10 deletions

View File

@ -327,6 +327,7 @@ set(HEADERS
radio/savedradio.h
radio/somafmservice.h
scripting/languageengine.h
scripting/scriptdialog.h
scripting/scriptdialogtabwidget.h
scripting/scriptinterface.h
@ -733,16 +734,23 @@ if(HAVE_SCRIPTING_PYTHON)
scripting/python/pythonscript.cpp
)
list(APPEND HEADERS
scripting/python/pythonengine.h
)
add_sip_binding(SOURCES
scripting/python/clementine.sip
${CMAKE_CURRENT_BINARY_DIR}/sipclementineLibraryBackendAlbum.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementinePlaylistItemOptions.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementinePlaylistItemPtr.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementinePlaylistItemSpecialLoadResult.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementineTaskManagerTask.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Directory.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100LibraryBackendAlbum.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100PlaylistItemPtr.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Song.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Subdirectory.cpp
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100TaskManagerTask.cpp
)
endif(HAVE_SCRIPTING_PYTHON)

View File

@ -100,7 +100,8 @@ void RadioModel::RemoveService(RadioService* service) {
}
void RadioModel::ServiceDeleted() {
RadioService* service = qobject_cast<RadioService*>(sender());
// qobject_cast doesn't work here with services created by python
RadioService* service = static_cast<RadioService*>(sender());
if (service)
RemoveService(service);
}

View File

@ -17,7 +17,8 @@
#include "languageengine.h"
LanguageEngine::LanguageEngine(ScriptManager* manager)
: manager_(manager)
LanguageEngine::LanguageEngine(ScriptManager* parent)
: QObject(parent),
manager_(parent)
{
}

View File

@ -20,11 +20,15 @@
#include "scriptmanager.h"
#include <QObject>
class Script;
class LanguageEngine {
class LanguageEngine : public QObject {
Q_OBJECT
public:
LanguageEngine(ScriptManager* manager);
LanguageEngine(ScriptManager* parent);
virtual ~LanguageEngine() {}
ScriptManager* manager() const { return manager_; }

View File

@ -7,13 +7,18 @@
%Include engine_fwd.sip
%Include librarybackend.sip
%Include libraryquery.sip
%Include mergedproxymodel.sip
%Include player.sip
%Include playlist.sip
%Include playlistitem.sip
%Include playlistmanager.sip
%Include playlistsequence.sip
%Include pythonengine.sip
%Include radiomodel.sip
%Include radioservice.sip
%Include queue.sip
%Include scriptinterface.sip
%Include settingsdialog.sip
%Include song.sip
%Include taskmanager.sip
%Include uiinterface.sip

View File

@ -0,0 +1,17 @@
class MergedProxyModel : QAbstractProxyModel {
%TypeHeaderCode
#include "core/mergedproxymodel.h"
%End
public:
MergedProxyModel(QObject* parent = 0);
void AddSubModel(const QModelIndex& source_parent, QAbstractItemModel* submodel);
void RemoveSubModel(const QModelIndex& source_parent);
QModelIndex FindSourceParent(const QModelIndex& proxy_index) const;
signals:
void SubModelReset(const QModelIndex& root, QAbstractItemModel* model);
};

View File

@ -26,6 +26,22 @@ public:
};
typedef QFlags<PlaylistItem::Option> Options;
struct SpecialLoadResult {
enum Type {
NoMoreTracks,
WillLoadAsynchronously,
TrackAvailable,
};
SpecialLoadResult(Type type = NoMoreTracks,
const QUrl& original_url = QUrl(),
const QUrl& media_url = QUrl());
Type type_;
QUrl original_url_;
QUrl media_url_;
};
QString type() const;
Options options() const;

View File

@ -109,6 +109,9 @@ Script* PythonEngine::CreateScript(const QString& path,
AddObject(manager()->data().library_->backend(), sipType_LibraryBackend, "library");
AddObject(manager()->data().player_, sipType_Player, "player");
AddObject(manager()->data().playlists_, sipType_PlaylistManager, "playlists");
AddObject(manager()->data().task_manager_, sipType_TaskManager, "task_manager");
AddObject(manager()->data().settings_dialog_, sipType_SettingsDialog, "settings_dialog");
AddObject(manager()->data().radio_model_, sipType_RadioModel, "radio_model");
AddObject(manager()->ui(), sipType_UIInterface, "ui");
AddObject(this, sipType_PythonEngine, "pythonengine");
@ -201,4 +204,16 @@ void PythonEngine::RegisterNativeObject(QObject* object) {
// Finally got the script - tell it about this object so it will get destroyed
// when the script is unloaded.
script->AddNativeObject(object);
// Save the script as a property on the object so we can remove it later
object->setProperty("owning_python_script", QVariant::fromValue(script));
connect(object, SIGNAL(destroyed(QObject*)), SLOT(NativeObjectDestroyed(QObject*)));
}
void PythonEngine::NativeObjectDestroyed(QObject* object) {
Script* script = object->property("owning_python_script").value<Script*>();
if (!script || !loaded_scripts_.values().contains(script))
return;
script->RemoveNativeObject(object);
}

View File

@ -25,6 +25,8 @@ struct _sipAPIDef;
struct _sipTypeDef;
class PythonEngine : public LanguageEngine {
Q_OBJECT
public:
PythonEngine(ScriptManager* manager);
~PythonEngine();
@ -56,6 +58,9 @@ private:
// would not.
Script* FindScriptMatchingId(const QString& id) const;
private slots:
void NativeObjectDestroyed(QObject* object);
private:
static PythonEngine* sInstance;

View File

@ -115,6 +115,7 @@ bool PythonScript::Unload() {
// Delete any native objects this script created
qDeleteAll(native_objects_);
native_objects_.clear();
return true;
}

View File

@ -0,0 +1,44 @@
class RadioModel : QStandardItemModel {
%TypeHeaderCode
#include "radio/radiomodel.h"
#include "scripting/python/pythonengine.h"
%End
public:
enum Role {
Role_Type,
Role_PlayBehaviour,
Role_Url,
Role_Title,
Role_Artist,
Role_CanLazyLoad,
Role_Service,
};
enum Type {
Type_Service,
TypeCount
};
enum PlayBehaviour {
PlayBehaviour_None,
PlayBehaviour_UseSongLoader,
PlayBehaviour_SingleItem,
};
void AddService(RadioService* service /Transfer/);
%MethodCode
sipCpp->AddService(a0);
PythonEngine::instance()->RegisterNativeObject(a0);
%End
void RemoveService(RadioService* service /TransferBack/);
bool IsPlayable(const QModelIndex& index) const;
MergedProxyModel* merged_model() const;
TaskManager* task_manager() const;
private:
RadioModel();
};

View File

@ -0,0 +1,40 @@
class RadioService : QObject {
%TypeHeaderCode
#include "radio/radioservice.h"
%End
public:
RadioService(const QString& name, RadioModel* model);
QString name() const;
RadioModel* model() const;
virtual QStandardItem* CreateRootItem() = 0 /TransferBack/;
virtual void LazyPopulate(QStandardItem* parent) = 0;
virtual void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
virtual PlaylistItem::SpecialLoadResult StartLoading(const QUrl& url);
virtual PlaylistItem::SpecialLoadResult LoadNext(const QUrl& url);
virtual PlaylistItem::Options playlistitem_options() const;
virtual QWidget* HeaderWidget() const /Transfer/;
virtual void ReloadSettings();
virtual QString Icon();
signals:
void AsyncLoadFinished(const PlaylistItem::SpecialLoadResult& result);
void StreamError(const QString& message);
void StreamMetadataFound(const QUrl& original_url, const Song& song);
void OpenSettingsAtPage(SettingsDialog::Page page);
void AddToPlaylistSignal(QMimeData* data);
protected:
void AddItemToPlaylist(const QModelIndex& index, bool clear = false, bool enqueue = false);
void AddItemsToPlaylist(const QModelIndexList& indexes, bool clear = false, bool enqueue = false);
};

View File

@ -0,0 +1,24 @@
class SettingsDialog : QDialog {
%TypeHeaderCode
#include "radio/radioservice.h"
%End
public:
enum Page {
Page_Playback,
Page_Behaviour,
Page_SongInformation,
Page_GlobalShortcuts,
Page_Notifications,
Page_Library,
Page_Magnatune,
Page_BackgroundStreams,
Page_Proxy,
};
void OpenAtPage(Page page);
private:
SettingsDialog();
};

View File

@ -0,0 +1,31 @@
class TaskManager : QObject {
%TypeHeaderCode
#include "core/taskmanager.h"
%End
public:
struct Task {
int id;
QString name;
int progress;
int progress_max;
bool blocks_library_scans;
};
QList<TaskManager::Task> GetTasks();
int StartTask(const QString& name);
void SetTaskBlocksLibraryScans(int id);
void SetTaskProgress(int id, int progress, int max = 0);
void SetTaskFinished(int id);
signals:
void TasksChanged();
void PauseLibraryWatchers();
void ResumeLibraryWatchers();
private:
TaskManager();
};

View File

@ -34,3 +34,7 @@ Script::~Script() {
void Script::AddNativeObject(QObject* object) {
native_objects_ << object;
}
void Script::RemoveNativeObject(QObject* object) {
native_objects_.removeAll(object);
}

View File

@ -19,6 +19,7 @@
#define SCRIPT_H
#include <QList>
#include <QMetaType>
#include <QString>
#include <boost/scoped_ptr.hpp>
@ -43,6 +44,7 @@ public:
// The script can "own" QObjects like QActions that must be deleted (and
// removed from the UI, etc.) when the script is unloaded.
void AddNativeObject(QObject* object);
void RemoveNativeObject(QObject* object);
virtual bool Init() = 0;
virtual bool Unload() = 0;
@ -59,5 +61,6 @@ private:
QString script_file_;
QString id_;
};
Q_DECLARE_METATYPE(Script*);
#endif // SCRIPT_H

View File

@ -53,8 +53,6 @@ ScriptManager::~ScriptManager() {
info.loaded_->language()->DestroyScript(info.loaded_);
}
}
qDeleteAll(engines_);
}
void ScriptManager::Init(const GlobalData& data) {

View File

@ -27,7 +27,10 @@ class LanguageEngine;
class Library;
class Player;
class PlaylistManager;
class RadioModel;
class Script;
class SettingsDialog;
class TaskManager;
class UIInterface;
class ScriptManager : public QAbstractListModel {
@ -55,15 +58,23 @@ public:
struct GlobalData {
GlobalData() {}
GlobalData(Library* library, Player* player, PlaylistManager* playlists)
GlobalData(Library* library, Player* player, PlaylistManager* playlists,
TaskManager* task_manager, SettingsDialog* settings_dialog,
RadioModel* radio_model)
: library_(library),
player_(player),
playlists_(playlists)
playlists_(playlists),
task_manager_(task_manager),
settings_dialog_(settings_dialog),
radio_model_(radio_model)
{}
Library* library_;
Player* player_;
PlaylistManager* playlists_;
TaskManager* task_manager_;
SettingsDialog* settings_dialog_;
RadioModel* radio_model_;
};
static const char* kSettingsGroup;

View File

@ -632,7 +632,9 @@ MainWindow::MainWindow(
wiimotedev_shortcuts_.reset(new WiimotedevShortcuts(osd_, this, player_));
#endif
scripts_->Init(ScriptManager::GlobalData(library_, player_, playlists_));
scripts_->Init(ScriptManager::GlobalData(
library_, player_, playlists_, task_manager_, settings_dialog_.get(),
radio_model_));
connect(ui_->action_script_manager, SIGNAL(triggered()), SLOT(ShowScriptDialog()));
}