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:
parent
03e6231483
commit
4e0cf13b26
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
#include "languageengine.h"
|
||||
|
||||
LanguageEngine::LanguageEngine(ScriptManager* manager)
|
||||
: manager_(manager)
|
||||
LanguageEngine::LanguageEngine(ScriptManager* parent)
|
||||
: QObject(parent),
|
||||
manager_(parent)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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_; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ bool PythonScript::Unload() {
|
|||
|
||||
// Delete any native objects this script created
|
||||
qDeleteAll(native_objects_);
|
||||
native_objects_.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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();
|
||||
};
|
|
@ -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();
|
||||
};
|
|
@ -34,3 +34,7 @@ Script::~Script() {
|
|||
void Script::AddNativeObject(QObject* object) {
|
||||
native_objects_ << object;
|
||||
}
|
||||
|
||||
void Script::RemoveNativeObject(QObject* object) {
|
||||
native_objects_.removeAll(object);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -53,8 +53,6 @@ ScriptManager::~ScriptManager() {
|
|||
info.loaded_->language()->DestroyScript(info.loaded_);
|
||||
}
|
||||
}
|
||||
|
||||
qDeleteAll(engines_);
|
||||
}
|
||||
|
||||
void ScriptManager::Init(const GlobalData& data) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue