From 785843198a3b4642ef939a0926e46e1896c1793f Mon Sep 17 00:00:00 2001 From: bao-qian Date: Thu, 5 May 2016 01:57:03 +0100 Subject: [PATCH] Use existing installed python 1. use installed python 2. add settings to choose python directory 3. add py3 compability 4. create hello world python example --- Plugins/HelloWorldPython/Images/app.png | Bin 0 -> 11524 bytes Plugins/HelloWorldPython/main.py | 17 ++++ Plugins/HelloWorldPython/plugin.json | 12 +++ Plugins/HelloWorldPython/wox.py | 107 ++++++++++++++++++++++ PythonHome/wox.py | 30 +++---- Wox.Core/Plugin/CSharpPluginLoader.cs | 112 +++++++++++++++++++----- Wox.Core/Plugin/IPluginLoader.cs | 10 --- Wox.Core/Plugin/JsonRPCPlugin.cs | 15 ++-- Wox.Core/Plugin/JsonRPCPluginLoader.cs | 21 ----- Wox.Core/Plugin/PluginManager.cs | 13 +-- Wox.Core/Plugin/PythonPlugin.cs | 26 ++---- Wox.Core/UserSettings/PluginSettings.cs | 45 +++++++++- Wox.Core/UserSettings/Settings.cs | 58 +----------- Wox.Core/Wox.Core.csproj | 2 - Wox/ActionKeywords.xaml.cs | 2 +- Wox/App.xaml.cs | 4 +- Wox/Languages/de.xaml | 3 +- Wox/Languages/en.xaml | 3 + Wox/Languages/fr.xaml | 3 +- Wox/Languages/ja.xaml | 2 + Wox/Languages/ru.xaml | 3 +- Wox/Languages/zh-cn.xaml | 1 + Wox/Languages/zh-tw.xaml | 1 + Wox/SettingWindow.xaml | 7 +- Wox/SettingWindow.xaml.cs | 38 +++++++- Wox/ViewModel/MainViewModel.cs | 2 +- Wox/Wox.csproj | 2 +- 27 files changed, 362 insertions(+), 177 deletions(-) create mode 100644 Plugins/HelloWorldPython/Images/app.png create mode 100644 Plugins/HelloWorldPython/main.py create mode 100644 Plugins/HelloWorldPython/plugin.json create mode 100644 Plugins/HelloWorldPython/wox.py delete mode 100644 Wox.Core/Plugin/IPluginLoader.cs delete mode 100644 Wox.Core/Plugin/JsonRPCPluginLoader.cs diff --git a/Plugins/HelloWorldPython/Images/app.png b/Plugins/HelloWorldPython/Images/app.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9ca7971a62d99905b25ef3fb3e435e1d1ceaca GIT binary patch literal 11524 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 literal 0 HcmV?d00001 diff --git a/Plugins/HelloWorldPython/main.py b/Plugins/HelloWorldPython/main.py new file mode 100644 index 0000000000..e2f54050a7 --- /dev/null +++ b/Plugins/HelloWorldPython/main.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from wox import Wox + +class HelloWorld(Wox): + + def query(self, query): + results = [] + results.append({ + "Title": "Hello World", + "SubTitle": "Query: {}".format(query), + "IcoPath":"Images/app.ico" + }) + return results + +if __name__ == "__main__": + HelloWorld() \ No newline at end of file diff --git a/Plugins/HelloWorldPython/plugin.json b/Plugins/HelloWorldPython/plugin.json new file mode 100644 index 0000000000..889f3c8848 --- /dev/null +++ b/Plugins/HelloWorldPython/plugin.json @@ -0,0 +1,12 @@ +{ + "ID":"2f4e384e-76ce-45c3-aea2-b16f5e5c328f", + "ActionKeyword":"h", + "Name":"Hello World Python", + "Description":"Hello World", + "Author":"happlebao", + "Version":"1.0", + "Language":"python", + "Website":"https://github.com/Wox-launche/Wox", + "IcoPath":"Images\\app.png", + "ExecuteFileName":"main.py" +} diff --git a/Plugins/HelloWorldPython/wox.py b/Plugins/HelloWorldPython/wox.py new file mode 100644 index 0000000000..084eac8eb2 --- /dev/null +++ b/Plugins/HelloWorldPython/wox.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function +import json +import sys +import inspect + +class Wox(object): + """ + Wox python plugin base + """ + + def __init__(self): + rpc_request = json.loads(sys.argv[1]) + self.proxy = rpc_request.get("proxy",{}) + request_method_name = rpc_request.get("method") + request_parameters = rpc_request.get("parameters") + methods = inspect.getmembers(self, predicate=inspect.ismethod) + + request_method = dict(methods)[request_method_name] + results = request_method(*request_parameters) + if request_method_name == "query": + print(json.dumps({"result": results})) + + def query(self,query): + """ + sub class need to override this method + """ + return [] + + def debug(self,msg): + """ + alert msg + """ + print("DEBUG:{}".format(msg)) + sys.exit() + +class WoxAPI(object): + + @classmethod + def change_query(cls,query,requery = False): + """ + change wox query + """ + print(json.dumps({"method": "Wox.ChangeQuery","parameters":[query,requery]})) + + @classmethod + def shell_run(cls,cmd): + """ + run shell commands + """ + print(json.dumps({"method": "Wox.ShellRun","parameters":[cmd]})) + + @classmethod + def close_app(cls): + """ + close wox + """ + print(json.dumps({"method": "Wox.CloseApp","parameters":[]})) + + @classmethod + def hide_app(cls): + """ + hide wox + """ + print(json.dumps({"method": "Wox.HideApp","parameters":[]})) + + @classmethod + def show_app(cls): + """ + show wox + """ + print(json.dumps({"method": "Wox.ShowApp","parameters":[]})) + + @classmethod + def show_msg(cls,title,sub_title,ico_path=""): + """ + show messagebox + """ + print(json.dumps({"method": "Wox.ShowMsg","parameters":[title,sub_title,ico_path]})) + + @classmethod + def open_setting_dialog(cls): + """ + open setting dialog + """ + print(json.dumps({"method": "Wox.OpenSettingDialog","parameters":[]})) + + @classmethod + def start_loadingbar(cls): + """ + start loading animation in wox + """ + print(json.dumps({"method": "Wox.StartLoadingBar","parameters":[]})) + + @classmethod + def stop_loadingbar(cls): + """ + stop loading animation in wox + """ + print(json.dumps({"method": "Wox.StopLoadingBar","parameters":[]})) + + @classmethod + def reload_plugins(cls): + """ + reload all wox plugins + """ + print(json.dumps({"method": "Wox.ReloadPlugins","parameters":[]})) diff --git a/PythonHome/wox.py b/PythonHome/wox.py index f31a5f072d..084eac8eb2 100644 --- a/PythonHome/wox.py +++ b/PythonHome/wox.py @@ -1,8 +1,8 @@ -#encoding=utf8 +# -*- coding: utf-8 -*- +from __future__ import print_function import json import sys import inspect -import chardet class Wox(object): """ @@ -10,7 +10,7 @@ class Wox(object): """ def __init__(self): - rpc_request = json.loads(sys.argv[1],encoding=chardet.detect(sys.argv[1])["encoding"]) + rpc_request = json.loads(sys.argv[1]) self.proxy = rpc_request.get("proxy",{}) request_method_name = rpc_request.get("method") request_parameters = rpc_request.get("parameters") @@ -19,7 +19,7 @@ class Wox(object): request_method = dict(methods)[request_method_name] results = request_method(*request_parameters) if request_method_name == "query": - print json.dumps({"result": results}) + print(json.dumps({"result": results})) def query(self,query): """ @@ -31,7 +31,7 @@ class Wox(object): """ alert msg """ - print "DEBUG:{}".format(msg) + print("DEBUG:{}".format(msg)) sys.exit() class WoxAPI(object): @@ -41,67 +41,67 @@ class WoxAPI(object): """ change wox query """ - print json.dumps({"method": "Wox.ChangeQuery","parameters":[query,requery]}) + print(json.dumps({"method": "Wox.ChangeQuery","parameters":[query,requery]})) @classmethod def shell_run(cls,cmd): """ run shell commands """ - print json.dumps({"method": "Wox.ShellRun","parameters":[cmd]}) + print(json.dumps({"method": "Wox.ShellRun","parameters":[cmd]})) @classmethod def close_app(cls): """ close wox """ - print json.dumps({"method": "Wox.CloseApp","parameters":[]}) + print(json.dumps({"method": "Wox.CloseApp","parameters":[]})) @classmethod def hide_app(cls): """ hide wox """ - print json.dumps({"method": "Wox.HideApp","parameters":[]}) + print(json.dumps({"method": "Wox.HideApp","parameters":[]})) @classmethod def show_app(cls): """ show wox """ - print json.dumps({"method": "Wox.ShowApp","parameters":[]}) + print(json.dumps({"method": "Wox.ShowApp","parameters":[]})) @classmethod def show_msg(cls,title,sub_title,ico_path=""): """ show messagebox """ - print json.dumps({"method": "Wox.ShowMsg","parameters":[title,sub_title,ico_path]}) + print(json.dumps({"method": "Wox.ShowMsg","parameters":[title,sub_title,ico_path]})) @classmethod def open_setting_dialog(cls): """ open setting dialog """ - print json.dumps({"method": "Wox.OpenSettingDialog","parameters":[]}) + print(json.dumps({"method": "Wox.OpenSettingDialog","parameters":[]})) @classmethod def start_loadingbar(cls): """ start loading animation in wox """ - print json.dumps({"method": "Wox.StartLoadingBar","parameters":[]}) + print(json.dumps({"method": "Wox.StartLoadingBar","parameters":[]})) @classmethod def stop_loadingbar(cls): """ stop loading animation in wox """ - print json.dumps({"method": "Wox.StopLoadingBar","parameters":[]}) + print(json.dumps({"method": "Wox.StopLoadingBar","parameters":[]})) @classmethod def reload_plugins(cls): """ reload all wox plugins """ - print json.dumps({"method": "Wox.ReloadPlugins","parameters":[]}) + print(json.dumps({"method": "Wox.ReloadPlugins","parameters":[]})) diff --git a/Wox.Core/Plugin/CSharpPluginLoader.cs b/Wox.Core/Plugin/CSharpPluginLoader.cs index 7fb02fbd1d..e46b468c0e 100644 --- a/Wox.Core/Plugin/CSharpPluginLoader.cs +++ b/Wox.Core/Plugin/CSharpPluginLoader.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using Wox.Infrastructure.Exception; @@ -8,42 +9,105 @@ using Wox.Plugin; namespace Wox.Core.Plugin { - internal class CSharpPluginLoader : IPluginLoader + public static class PluginsLoader { - public IEnumerable LoadPlugin(List pluginMetadatas) + public const string PATH = "PATH"; + public const string Python = "python"; + public const string PythonExecutable = "pythonw.exe"; + + public static IEnumerable CSharpPlugins(IEnumerable source) { var plugins = new List(); - List CSharpPluginMetadatas = pluginMetadatas.Where(o => o.Language.ToUpper() == AllowedLanguage.CSharp.ToUpper()).ToList(); + var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.CSharp); - foreach (PluginMetadata metadata in CSharpPluginMetadatas) + foreach (var metadata in metadatas) { + Assembly assembly; try { - Assembly asm = Assembly.Load(AssemblyName.GetAssemblyName(metadata.ExecuteFilePath)); - List types = asm.GetTypes().Where(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))).ToList(); - if (types.Count == 0) - { - Log.Warn($"Couldn't load plugin {metadata.Name}: didn't find the class that implement IPlugin"); - continue; - } - - foreach (Type type in types) - { - PluginPair pair = new PluginPair - { - Plugin = Activator.CreateInstance(type) as IPlugin, - Metadata = metadata - }; - - plugins.Add(pair); - } + assembly = Assembly.Load(AssemblyName.GetAssemblyName(metadata.ExecuteFilePath)); } catch (Exception e) { - Log.Error(new WoxPluginException(metadata.Name, $"Couldn't load plugin", e)); + Log.Error(new WoxPluginException(metadata.Name, "Couldn't load assembly", e)); + continue; + } + var types = assembly.GetTypes(); + Type type; + try + { + type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); + } + catch (InvalidOperationException e) + { + Log.Error(new WoxPluginException(metadata.Name, "Can't find class implement IPlugin", e)); + continue; + } + IPlugin plugin; + try + { + plugin = (IPlugin)Activator.CreateInstance(type); + } + catch (Exception e) + { + Log.Error(new WoxPluginException(metadata.Name, "Can't create instance", e)); + continue; + } + PluginPair pair = new PluginPair + { + Plugin = plugin, + Metadata = metadata + }; + plugins.Add(pair); + } + return plugins; + } + + public static IEnumerable PythonPlugins(IEnumerable source, string pythonDirecotry) + { + var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Python); + string filename; + + if (string.IsNullOrEmpty(pythonDirecotry)) + { + var paths = Environment.GetEnvironmentVariable(PATH); + if (paths != null) + { + var pythonPaths = paths.Split(';').Where(p => p.ToLower().Contains(Python)); + if (pythonPaths.Any()) + { + filename = PythonExecutable; + } + else + { + Log.Error(new WoxException("Python can't be found in PATH.")); + return new List(); + } + } + else + { + Log.Error(new WoxException("Path variable is not set.")); + return new List(); } } - + else + { + var path = Path.Combine(pythonDirecotry, PythonExecutable); + if (File.Exists(path)) + { + filename = path; + } + else + { + Log.Error(new WoxException("Can't find python executable in python directory")); + return new List(); + } + } + var plugins = metadatas.Select(metadata => new PluginPair + { + Plugin = new PythonPlugin(filename), + Metadata = metadata + }); return plugins; } } diff --git a/Wox.Core/Plugin/IPluginLoader.cs b/Wox.Core/Plugin/IPluginLoader.cs deleted file mode 100644 index a85e7e361e..0000000000 --- a/Wox.Core/Plugin/IPluginLoader.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using Wox.Plugin; - -namespace Wox.Core.Plugin -{ - internal interface IPluginLoader - { - IEnumerable LoadPlugin(List pluginMetadatas); - } -} diff --git a/Wox.Core/Plugin/JsonRPCPlugin.cs b/Wox.Core/Plugin/JsonRPCPlugin.cs index c0654336d7..16e39c1a5a 100644 --- a/Wox.Core/Plugin/JsonRPCPlugin.cs +++ b/Wox.Core/Plugin/JsonRPCPlugin.cs @@ -14,6 +14,7 @@ namespace Wox.Core.Plugin { /// /// Represent the plugin that using JsonPRC + /// every JsonRPC plugin should has its own plugin instance /// internal abstract class JsonRPCPlugin : IPlugin { @@ -22,7 +23,7 @@ namespace Wox.Core.Plugin /// /// The language this JsonRPCPlugin support /// - public abstract string SupportedLanguage { get; } + public abstract string SupportedLanguage { get; set; } protected abstract string ExecuteQuery(Query query); protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); @@ -30,7 +31,7 @@ namespace Wox.Core.Plugin public List Query(Query query) { string output = ExecuteQuery(query); - if (!string.IsNullOrEmpty(output)) + if (!String.IsNullOrEmpty(output)) { try { @@ -46,7 +47,7 @@ namespace Wox.Core.Plugin { if (result1.JsonRPCAction == null) return false; - if (!string.IsNullOrEmpty(result1.JsonRPCAction.Method)) + if (!String.IsNullOrEmpty(result1.JsonRPCAction.Method)) { if (result1.JsonRPCAction.Method.StartsWith("Wox.")) { @@ -59,7 +60,7 @@ namespace Wox.Core.Plugin string actionReponse = ExecuteCallback(result1.JsonRPCAction); JsonRPCRequestModel jsonRpcRequestModel = JsonConvert.DeserializeObject(actionReponse); if (jsonRpcRequestModel != null - && !string.IsNullOrEmpty(jsonRpcRequestModel.Method) + && !String.IsNullOrEmpty(jsonRpcRequestModel.Method) && jsonRpcRequestModel.Method.StartsWith("Wox.")) { ExecuteWoxAPI(jsonRpcRequestModel.Method.Substring(4), jsonRpcRequestModel.Parameters); @@ -125,7 +126,7 @@ namespace Wox.Core.Plugin { using (Process process = Process.Start(startInfo)) { - if (process != null) + if (process != null) { using (StreamReader reader = process.StandardOutput) { @@ -135,12 +136,12 @@ namespace Wox.Core.Plugin MessageBox.Show(new Form { TopMost = true }, result.Substring(6)); return ""; } - if (string.IsNullOrEmpty(result)) + if (String.IsNullOrEmpty(result)) { using (StreamReader errorReader = process.StandardError) { string error = errorReader.ReadToEnd(); - if (!string.IsNullOrEmpty(error)) + if (!String.IsNullOrEmpty(error)) { throw new WoxJsonRPCException(error); } diff --git a/Wox.Core/Plugin/JsonRPCPluginLoader.cs b/Wox.Core/Plugin/JsonRPCPluginLoader.cs deleted file mode 100644 index eb88356719..0000000000 --- a/Wox.Core/Plugin/JsonRPCPluginLoader.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Wox.Plugin; - -namespace Wox.Core.Plugin -{ - internal class JsonRPCPluginLoader : IPluginLoader where T : JsonRPCPlugin, new() - { - public IEnumerable LoadPlugin(List pluginMetadatas) - { - T jsonRPCPlugin = new T(); - List jsonRPCPluginMetadatas = pluginMetadatas.Where(o => o.Language.ToUpper() == jsonRPCPlugin.SupportedLanguage.ToUpper()).ToList(); - - return jsonRPCPluginMetadatas.Select(metadata => new PluginPair - { - Plugin = new T(), //every JsonRPC plugin should has its own plugin instance - Metadata = metadata - }).ToList(); - } - } -} diff --git a/Wox.Core/Plugin/PluginManager.cs b/Wox.Core/Plugin/PluginManager.cs index b0cd7cfd97..7f32819dce 100644 --- a/Wox.Core/Plugin/PluginManager.cs +++ b/Wox.Core/Plugin/PluginManager.cs @@ -25,13 +25,11 @@ namespace Wox.Core.Plugin /// public static List AllPlugins { get; private set; } - public static readonly List GlobalPlugins = new List(); - public static readonly Dictionary NonGlobalPlugins = new Dictionary(); - private static IEnumerable InstantQueryPlugins { get; set; } public static IPublicAPI API { private set; get; } + private static PluginsSettings _settings; private static readonly string[] Directories = {Infrastructure.Wox.PreinstalledDirectory, Infrastructure.Wox.UserDirectory }; @@ -57,12 +55,15 @@ namespace Wox.Core.Plugin } } - public static void InitializePlugins(IPublicAPI api) + public static void InitializePlugins(IPublicAPI api, PluginsSettings settings) { + _settings = settings; + var metadatas = PluginConfig.Parse(Directories); - var plugins1 = new CSharpPluginLoader().LoadPlugin(metadatas); - var plugins2 = new JsonRPCPluginLoader().LoadPlugin(metadatas); + var plugins1 = PluginsLoader.CSharpPlugins(metadatas); + var plugins2 = PluginsLoader.PythonPlugins(metadatas, _settings.PythonDirectory); AllPlugins = plugins1.Concat(plugins2).ToList(); + _settings.UpdatePluginSettings(AllPlugins); //load plugin i18n languages ResourceMerger.UpdatePluginLanguages(); diff --git a/Wox.Core/Plugin/PythonPlugin.cs b/Wox.Core/Plugin/PythonPlugin.cs index fc74a1d7ab..7022512aee 100644 --- a/Wox.Core/Plugin/PythonPlugin.cs +++ b/Wox.Core/Plugin/PythonPlugin.cs @@ -1,37 +1,25 @@ -using System.Diagnostics; -using System.IO; +using System; +using System.Diagnostics; using Wox.Core.UserSettings; -using Wox.Infrastructure; using Wox.Plugin; namespace Wox.Core.Plugin { internal class PythonPlugin : JsonRPCPlugin { - private static readonly string PythonHome = Path.Combine(Infrastructure.Wox.ProgramPath, "PythonHome"); private readonly ProcessStartInfo _startInfo; + public override string SupportedLanguage { get; set; } = AllowedLanguage.Python; - public override string SupportedLanguage => AllowedLanguage.Python; - - public PythonPlugin() + public PythonPlugin(string filename) { _startInfo = new ProcessStartInfo { + FileName = @"C:\Program Files\Python 3.5\pythonw.exe", UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; - string additionalPythonPath = $"{Path.Combine(PythonHome, "DLLs")};{Path.Combine(PythonHome, "Lib", "site-packages")}"; - if (!_startInfo.EnvironmentVariables.ContainsKey("PYTHONPATH")) - { - - _startInfo.EnvironmentVariables.Add("PYTHONPATH", additionalPythonPath); - } - else - { - _startInfo.EnvironmentVariables["PYTHONPATH"] = additionalPythonPath; - } } protected override string ExecuteQuery(Query query) @@ -39,11 +27,10 @@ namespace Wox.Core.Plugin JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { Method = "query", - Parameters = new object[] { query.GetAllRemainingParameter() }, + Parameters = new object[] { query.Search }, HttpProxy = HttpProxy.Instance }; //Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable - _startInfo.FileName = Path.Combine(PythonHome, "pythonw.exe"); _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{request}\""; return Execute(_startInfo); @@ -51,7 +38,6 @@ namespace Wox.Core.Plugin protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest) { - _startInfo.FileName = Path.Combine(PythonHome, "pythonw.exe"); _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{rpcRequest}\""; return Execute(_startInfo); } diff --git a/Wox.Core/UserSettings/PluginSettings.cs b/Wox.Core/UserSettings/PluginSettings.cs index 80267a2015..926cc62361 100644 --- a/Wox.Core/UserSettings/PluginSettings.cs +++ b/Wox.Core/UserSettings/PluginSettings.cs @@ -1,16 +1,53 @@ using System.Collections.Generic; +using System.Linq; +using Wox.Core.Plugin; +using Wox.Plugin; namespace Wox.Core.UserSettings { + public class PluginsSettings + { + public string PythonDirectory { get; set; } + public Dictionary Plugins { get; set; } = new Dictionary(); - public class PluginSettings + public void UpdatePluginSettings(List plugins) + { + var metadatas = plugins.Select(p => p.Metadata); + foreach (var metadata in metadatas) + { + if (Plugins.ContainsKey(metadata.ID)) + { + var settings = Plugins[metadata.ID]; + if (settings.ActionKeywords?.Count > 0) + { + metadata.ActionKeywords = settings.ActionKeywords; + metadata.ActionKeyword = settings.ActionKeywords[0]; + } + } + else + { + Plugins[metadata.ID] = new Plugin + { + ID = metadata.ID, + Name = metadata.Name, + ActionKeywords = metadata.ActionKeywords, + Disabled = false + }; + } + } + } + + public void UpdateActionKeyword(PluginMetadata metadata) + { + var settings = Plugins[metadata.ID]; + settings.ActionKeywords = metadata.ActionKeywords; + } + } + public class Plugin { public string ID { get; set; } - public string Name { get; set; } - public List ActionKeywords { get; set; } - public bool Disabled { get; set; } } } diff --git a/Wox.Core/UserSettings/Settings.cs b/Wox.Core/UserSettings/Settings.cs index 8ee4a375b0..70deb9c46b 100644 --- a/Wox.Core/UserSettings/Settings.cs +++ b/Wox.Core/UserSettings/Settings.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Drawing; using System.Linq; using Wox.Core.Plugin; -using Wox.Infrastructure.Storage; using Wox.Plugin; using Newtonsoft.Json; @@ -31,7 +29,7 @@ namespace Wox.Core.UserSettings // Order defaults to 0 or -1, so 1 will let this property appear last [JsonProperty(Order = 1)] - public Dictionary PluginSettings { get; set; } = new Dictionary(); + public PluginsSettings PluginSettings { get; set; } = new PluginsSettings(); public List CustomPluginHotkeys { get; set; } = new List(); [Obsolete] @@ -54,61 +52,9 @@ namespace Wox.Core.UserSettings public int ProxyPort { get; set; } public string ProxyUserName { get; set; } public string ProxyPassword { get; set; } - - public void UpdatePluginSettings() - { - var metadatas = PluginManager.AllPlugins.Select(p => p.Metadata); - if (PluginSettings == null) - { - var configs = new Dictionary(); - foreach (var metadata in metadatas) - { - addPluginMetadata(configs, metadata); - } - PluginSettings = configs; - } - else - { - var configs = PluginSettings; - foreach (var metadata in metadatas) - { - if (configs.ContainsKey(metadata.ID)) - { - var config = configs[metadata.ID]; - if (config.ActionKeywords?.Count > 0) - { - metadata.ActionKeywords = config.ActionKeywords; - metadata.ActionKeyword = config.ActionKeywords[0]; - } - } - else - { - addPluginMetadata(configs, metadata); - } - } - } - } - - - private void addPluginMetadata(Dictionary configs, PluginMetadata metadata) - { - configs[metadata.ID] = new PluginSettings - { - ID = metadata.ID, - Name = metadata.Name, - ActionKeywords = metadata.ActionKeywords, - Disabled = false - }; - } - - public void UpdateActionKeyword(PluginMetadata metadata) - { - var config = PluginSettings[metadata.ID]; - config.ActionKeywords = metadata.ActionKeywords; - } - } + [Obsolete] public enum OpacityMode { Normal = 0, diff --git a/Wox.Core/Wox.Core.csproj b/Wox.Core/Wox.Core.csproj index 25474b6c61..bf68ebaa7b 100644 --- a/Wox.Core/Wox.Core.csproj +++ b/Wox.Core/Wox.Core.csproj @@ -78,9 +78,7 @@ - - diff --git a/Wox/ActionKeywords.xaml.cs b/Wox/ActionKeywords.xaml.cs index ffcdb8a10e..8d3769d703 100644 --- a/Wox/ActionKeywords.xaml.cs +++ b/Wox/ActionKeywords.xaml.cs @@ -50,7 +50,7 @@ namespace Wox return; } // update persistant data - _settings.UpdateActionKeyword(_plugin.Metadata); + _settings.PluginSettings.UpdateActionKeyword(_plugin.Metadata); MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed")); Close(); diff --git a/Wox/App.xaml.cs b/Wox/App.xaml.cs index 9fc782a359..fb05f85cb6 100644 --- a/Wox/App.xaml.cs +++ b/Wox/App.xaml.cs @@ -42,10 +42,10 @@ namespace Wox ImageLoader.PreloadImages(); MainViewModel mainVM = new MainViewModel(); + var pluginsSettings = mainVM._settings.PluginSettings; API = new PublicAPIInstance(mainVM, mainVM._settings); - PluginManager.InitializePlugins(API); + PluginManager.InitializePlugins(API, pluginsSettings); - mainVM._settings.UpdatePluginSettings(); Window = new MainWindow(mainVM._settings, mainVM); NotifyIconManager notifyIconManager = new NotifyIconManager(API); diff --git a/Wox/Languages/de.xaml b/Wox/Languages/de.xaml index e12307d573..9120f6de9c 100644 --- a/Wox/Languages/de.xaml +++ b/Wox/Languages/de.xaml @@ -24,7 +24,8 @@ Sprache Maximale Anzahl Ergebnisse Ignoriere Tastenkombination wenn Fenster im Vollbildmodus ist - + Python Directory + Plugin Suche weitere Plugins diff --git a/Wox/Languages/en.xaml b/Wox/Languages/en.xaml index 2e75ca24a8..71065ba6b6 100644 --- a/Wox/Languages/en.xaml +++ b/Wox/Languages/en.xaml @@ -24,6 +24,9 @@ Language Maximum show results Ignore hotkeys if window is fullscreen + Python Directory + Select + Plugin diff --git a/Wox/Languages/fr.xaml b/Wox/Languages/fr.xaml index 839fae4f23..df84a678e3 100644 --- a/Wox/Languages/fr.xaml +++ b/Wox/Languages/fr.xaml @@ -24,7 +24,8 @@ Langue Résultats à afficher Ignore les raccourcis lorsqu'une application est en plein écran - + Python Directory + Modules Trouver plus de modules diff --git a/Wox/Languages/ja.xaml b/Wox/Languages/ja.xaml index 24e1bd90ea..2b4901c0b3 100644 --- a/Wox/Languages/ja.xaml +++ b/Wox/Languages/ja.xaml @@ -24,6 +24,8 @@ 言語 結果の最大表示件数 ウィンドウがフルスクリーン時にホットキーを無効にする + Python Directory + プラグイン diff --git a/Wox/Languages/ru.xaml b/Wox/Languages/ru.xaml index 13a7be86cc..3ba2c70057 100644 --- a/Wox/Languages/ru.xaml +++ b/Wox/Languages/ru.xaml @@ -24,7 +24,8 @@ Язык Максимальное количество результатов Игнорировать горячие клавиши, если окно в полноэкранном режиме - + Python Directory + Плагины Найти больше плагинов diff --git a/Wox/Languages/zh-cn.xaml b/Wox/Languages/zh-cn.xaml index f69b5511be..b44a0c111d 100644 --- a/Wox/Languages/zh-cn.xaml +++ b/Wox/Languages/zh-cn.xaml @@ -24,6 +24,7 @@ 语言 最大结果显示个数 全屏模式下忽略热键 + Python 路径 插件 diff --git a/Wox/Languages/zh-tw.xaml b/Wox/Languages/zh-tw.xaml index ed60457dad..a3bac1ffa7 100644 --- a/Wox/Languages/zh-tw.xaml +++ b/Wox/Languages/zh-tw.xaml @@ -24,6 +24,7 @@ 語言 最大結果顯示個數 全屏模式下忽略熱鍵 + Python Directory 插件 diff --git a/Wox/SettingWindow.xaml b/Wox/SettingWindow.xaml index b0fc8b93e1..016668f75b 100644 --- a/Wox/SettingWindow.xaml +++ b/Wox/SettingWindow.xaml @@ -45,6 +45,11 @@ + + + +