From dccf2a573d9a69ae190dbc2af60c32c9970c4ca0 Mon Sep 17 00:00:00 2001 From: sirjonasxx <36828922+sirjonasxx@users.noreply.github.com> Date: Mon, 19 Oct 2020 02:00:39 +0200 Subject: [PATCH] g-python shell part1 --- G-Earth/pom.xml | 8 + .../gearth/services/gpython/GPythonShell.java | 158 +++++++++++++++++- .../services/gpython/GPythonVersionUtils.java | 96 +++++++++++ .../services/gpython/JupyterConsole.java | 4 - .../services/gpython/OnQtConsoleLaunch.java | 7 + .../gearth/services/gpython/PythonUtils.java | 4 - .../ui/extensions/ExtensionsController.java | 28 +++- .../java/gearth/ui/extra/ExtraController.java | 51 ++++++ .../main/resources/gearth/ui/extra/Extra.fxml | 2 +- 9 files changed, 343 insertions(+), 15 deletions(-) create mode 100644 G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java delete mode 100644 G-Earth/src/main/java/gearth/services/gpython/JupyterConsole.java create mode 100644 G-Earth/src/main/java/gearth/services/gpython/OnQtConsoleLaunch.java delete mode 100644 G-Earth/src/main/java/gearth/services/gpython/PythonUtils.java diff --git a/G-Earth/pom.xml b/G-Earth/pom.xml index fb8e3c7..8627241 100644 --- a/G-Earth/pom.xml +++ b/G-Earth/pom.xml @@ -164,6 +164,14 @@ slf4j-jdk14 2.0.0-alpha0 + + + org.apache.maven + maven-artifact + 3.6.3 + + + diff --git a/G-Earth/src/main/java/gearth/services/gpython/GPythonShell.java b/G-Earth/src/main/java/gearth/services/gpython/GPythonShell.java index 204d848..bb6cc21 100644 --- a/G-Earth/src/main/java/gearth/services/gpython/GPythonShell.java +++ b/G-Earth/src/main/java/gearth/services/gpython/GPythonShell.java @@ -1,9 +1,165 @@ package gearth.services.gpython; +import gearth.Main; +import gearth.ui.extra.ExtraController; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Region; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.List; + public class GPythonShell { - public void launch() { + private final String extensionName; + private final String cookie; + public GPythonShell(String extensionName, String cookie) { + this.extensionName = extensionName; + this.cookie = cookie; + } + + public void launch(OnQtConsoleLaunch onLaunch) { + new Thread(() -> { + try { + // launch the jupyter console + ProcessBuilder gConsoleBuilder = new ProcessBuilder("python", "-m", "jupyter", "console", "--simple-prompt"); + Process gConsole = gConsoleBuilder.start(); + + InputStreamReader in = new InputStreamReader(gConsole.getInputStream()); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(gConsole.getOutputStream())); + + readUntilExpectInput(in); + + // obtain jupyter kernel name + List sysargs = enterCommandAndAwait(out, in, "import sys; sys.argv"); + String kernelName = extractKernelName(sysargs); + + + onLaunch.launched(false); + } + catch (Exception e) { + e.printStackTrace(); + showError(); + onLaunch.launched(true); + } + }).start(); + } + + private List readUntilExpectInput(InputStreamReader in) throws IOException { + List readings = new ArrayList<>(); + StringBuilder builder = new StringBuilder(); + + int c; + while ((c = in.read()) != -1) { + char character = (char)c; + if (character == '\n') { + String reading = builder.toString(); + if (reading.endsWith("\r")) { + reading = reading.substring(0, reading.length() - 1); + } + if (reading.matches("Out\\[[0-9+]]: .*")) { + reading = reading.replaceAll("^Out\\[[0-9+]]: ", ""); + if (reading.equals("")) { + builder = new StringBuilder(); + continue; + } + } + + readings.add(reading); + builder = new StringBuilder(); + } + else { + builder.append(character); + + if (builder.toString().matches("In \\[[0-9]+]: ") && !in.ready()) { + return readings; + } + } + } + return null; + } + private void enterCommand(BufferedWriter out, String command) throws IOException { + out.write(command); + out.newLine(); + out.flush(); + } + private List enterCommandAndAwait(BufferedWriter out, InputStreamReader in, String command) throws IOException { + enterCommand(out, command); + return readUntilExpectInput(in); + } + + private String extractKernelName(List inputArgs) { + // null if not found + + String joined = String.join("", inputArgs); + String paramsFull = joined.replaceAll("^[^\\[]*\\[", "").replaceAll("][^]]*$", ""); + List params = new ArrayList<>(); + + boolean backslashed = false; + int beginParameterIndex = -1; + for (int i = 0; i < paramsFull.length(); i++) { + char c = paramsFull.charAt(i); + if (c == '\'' && !backslashed) { + if (beginParameterIndex == -1) { + beginParameterIndex = i + 1; + } + else { + params.add(paramsFull.substring(beginParameterIndex, i)); + beginParameterIndex = -1; + } + } + + backslashed = c == '\\' && !backslashed; + } + + for (int i = 0; i < params.size() - 1; i++) { + if (params.get(i).equals("-f")) { + return params.get(i+1); + } + } + return null; + } + + private void showError() { + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.ERROR, "G-Python error", ButtonType.OK); + alert.setTitle("G-Python error"); + + FlowPane fp = new FlowPane(); + Label lbl = new Label("Something went wrong launching the G-Python shell," + + System.lineSeparator() + "are you sure you followed the installation guide correctly?" + + System.lineSeparator() + System.lineSeparator() + "More information here:"); + Hyperlink link = new Hyperlink(ExtraController.INFO_URL_GPYTHON); + fp.getChildren().addAll(lbl, link); + link.setOnAction(event -> { + Main.main.getHostServices().showDocument(link.getText()); + event.consume(); + }); + + alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + alert.getDialogPane().setContent(fp); + alert.show(); + }); + } + + public String getExtensionName() { + return extensionName; + } + + public static void main(String[] args) throws IOException { + GPythonShell shell = new GPythonShell("test", "cookie"); + shell.launch((b) -> { + System.out.println("launched"); + }); } } diff --git a/G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java b/G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java new file mode 100644 index 0000000..9d2f3bf --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java @@ -0,0 +1,96 @@ +package gearth.services.gpython; + +import org.apache.maven.artifact.versioning.ComparableVersion; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +public class GPythonVersionUtils { + + private static final String MIN_GPYTHON_VERSION = "0.1"; + private static final String MIN_PYTHON_VERSION = "3.2"; + + // returns null if python not installed + public static String pythonVersion() { + List commandOutput = executeCommand("python", "--version"); + if (commandOutput.size() == 0) { + return null; + } + String maybeVersion = commandOutput.get(0); + if (!maybeVersion.contains("Python")) { + return null; + } + + return maybeVersion.split(" ")[1]; + } + + public static boolean validInstallation() { + // validates if user has all dependencies installed + String pythonVersion = pythonVersion(); + if (pythonVersion == null) { + return false; + } + + ComparableVersion version = new ComparableVersion(pythonVersion); + if (version.compareTo(new ComparableVersion(MIN_PYTHON_VERSION)) < 0) { + return false; + } + + List allPackages = executeCommand("python", "-m", "pip", "list"); + allPackages = allPackages.subList(2, allPackages.size()); + + String qtconsole = getPackageVersion(allPackages, "qtconsole"); + String pyqt5 = getPackageVersion(allPackages, "pyqt5"); + String jupyterConsole = getPackageVersion(allPackages, "jupyter-console"); + String gPython = getPackageVersion(allPackages, "g-python"); + + if (qtconsole == null || pyqt5 == null || jupyterConsole == null || gPython == null) { + return false; + } + + ComparableVersion gVersion = new ComparableVersion(gPython); + return gVersion.compareTo(new ComparableVersion(MIN_GPYTHON_VERSION)) >= 0; + } + + // null if not found + private static String getPackageVersion(List allPackages, String pkg) { + pkg = pkg.toLowerCase(); + + for (String maybePkg : allPackages) { + String[] split = maybePkg.split(" +"); + if (split[0].toLowerCase().equals(pkg)) { + return split[1]; + } + } + return null; + } + + private static List executeCommand(String... command) { + List output = new ArrayList<>(); + + try { + ProcessBuilder processBuilder = new ProcessBuilder(command); + Process process = processBuilder.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + while ((line = reader.readLine()) != null) { + output.add(line); + } + + process.waitFor(); + + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + return output; + } + +// public static void main(String[] args) { +// System.out.println(validInstallation()); +// } + +} diff --git a/G-Earth/src/main/java/gearth/services/gpython/JupyterConsole.java b/G-Earth/src/main/java/gearth/services/gpython/JupyterConsole.java deleted file mode 100644 index cf36fa0..0000000 --- a/G-Earth/src/main/java/gearth/services/gpython/JupyterConsole.java +++ /dev/null @@ -1,4 +0,0 @@ -package gearth.services.gpython; - -public class JupyterConsole { -} diff --git a/G-Earth/src/main/java/gearth/services/gpython/OnQtConsoleLaunch.java b/G-Earth/src/main/java/gearth/services/gpython/OnQtConsoleLaunch.java new file mode 100644 index 0000000..8414672 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/gpython/OnQtConsoleLaunch.java @@ -0,0 +1,7 @@ +package gearth.services.gpython; + +public interface OnQtConsoleLaunch { + + void launched(boolean failed); + +} diff --git a/G-Earth/src/main/java/gearth/services/gpython/PythonUtils.java b/G-Earth/src/main/java/gearth/services/gpython/PythonUtils.java deleted file mode 100644 index 92c7787..0000000 --- a/G-Earth/src/main/java/gearth/services/gpython/PythonUtils.java +++ /dev/null @@ -1,4 +0,0 @@ -package gearth.services.gpython; - -public class PythonUtils { -} diff --git a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java index ba43241..098207b 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java +++ b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java @@ -1,5 +1,6 @@ package gearth.ui.extensions; +import gearth.Main; import gearth.services.extensionhandler.ExtensionConnectedListener; import gearth.services.extensionhandler.ExtensionHandler; import gearth.services.extensionhandler.extensions.ExtensionListener; @@ -9,18 +10,21 @@ import gearth.services.extensionhandler.extensions.implementations.network.execu import gearth.services.extensionhandler.extensions.implementations.network.executer.ExtensionRunner; import gearth.services.extensionhandler.extensions.implementations.network.executer.ExtensionRunnerFactory; import gearth.services.gpython.GPythonShell; +import gearth.services.gpython.OnQtConsoleLaunch; import gearth.ui.SubForm; import gearth.ui.extensions.logger.ExtensionLogger; +import gearth.ui.extra.ExtraController; import javafx.application.Platform; import javafx.event.ActionEvent; -import javafx.scene.control.Button; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextField; +import javafx.scene.control.*; +import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import java.io.File; +import java.io.IOException; /** * Created by Jonas on 06/04/18. @@ -103,10 +107,24 @@ public class ExtensionsController extends SubForm { @Override protected void onTabOpened() { - btn_gpython.setDisable(!parentController.extraController.useGPython()); + updateGPythonStatus(); } + public void updateGPythonStatus() { + if (!pythonShellLaunching) { + btn_gpython.setDisable(!parentController.extraController.useGPython()); + } + } + + + private volatile boolean pythonShellLaunching = false; public void gpythonBtnClicked(ActionEvent actionEvent) { - new GPythonShell().launch(); + pythonShellLaunching = true; + Platform.runLater(() -> btn_gpython.setDisable(true)); + GPythonShell shell = new GPythonShell("test", "cookie"); + shell.launch((b) -> { + pythonShellLaunching = false; + Platform.runLater(this::updateGPythonStatus); + }); } } diff --git a/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java b/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java index 69148a3..1fa67f0 100644 --- a/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java +++ b/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java @@ -1,15 +1,21 @@ package gearth.ui.extra; +import gearth.Main; import gearth.misc.Cacher; import gearth.protocol.HConnection; import gearth.protocol.connection.HState; import gearth.protocol.connection.proxy.ProxyProviderFactory; import gearth.protocol.connection.proxy.SocksConfiguration; +import gearth.services.gpython.GPythonVersionUtils; import gearth.ui.SubForm; import gearth.ui.info.InfoController; +import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.scene.control.*; +import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; +import javafx.scene.layout.Region; +import javafx.scene.web.WebView; import org.json.JSONObject; /** @@ -17,6 +23,8 @@ import org.json.JSONObject; */ public class ExtraController extends SubForm implements SocksConfiguration { + public static final String INFO_URL_GPYTHON = "www.placeholder.com"; + public static final String NOTEPAD_CACHE_KEY = "notepad_text"; public static final String SOCKS_CACHE_KEY = "socks_config"; public static final String GPYTHON_CACHE_KEY = "use_gpython"; @@ -143,4 +151,47 @@ public class ExtraController extends SubForm implements SocksConfiguration { public boolean useGPython() { return cbx_gpython.isSelected(); } + + public void gpythonCbxClick(ActionEvent actionEvent) { + if (cbx_gpython.isSelected()) { + new Thread(() -> { + Platform.runLater(() -> { + cbx_gpython.setSelected(false); + cbx_gpython.setDisable(true); + }); + if (!GPythonVersionUtils.validInstallation()) { + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.ERROR, "G-Python installation", ButtonType.OK); + alert.setTitle("G-Python installation"); + + FlowPane fp = new FlowPane(); + Label lbl = new Label("Before using G-Python, install the right packages using pip!" + + System.lineSeparator() + System.lineSeparator() + "More information here:"); + Hyperlink link = new Hyperlink(INFO_URL_GPYTHON); + fp.getChildren().addAll( lbl, link); + link.setOnAction(event -> { + Main.main.getHostServices().showDocument(link.getText()); + event.consume(); + }); + + alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); + alert.getDialogPane().setContent(fp); + alert.show(); + + cbx_gpython.setDisable(false); + }); + } + else { + Platform.runLater(() -> { + cbx_gpython.setSelected(true); + cbx_gpython.setDisable(false); + parentController.extensionsController.updateGPythonStatus(); + }); + } + }).start(); + + + } + + } } diff --git a/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml b/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml index ff6adad..fac9d86 100644 --- a/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml +++ b/G-Earth/src/main/resources/gearth/ui/extra/Extra.fxml @@ -112,7 +112,7 @@ - +