How to import python package in flutter using Starflut? - python

I'm building an app with Flutter and I need to use some Python code. For that I use the Starflut plugin (https://pub.dev/packages/starflut). I succeed to run the Python code, no issue here, but I want to import extra package.
To do so, I created a virtual environment where my app is located, then I downloaded by the pip installer numpy, cv2 and Pillow, still no issue there, the downloading went fine.
But, when I try to import numpy for example, my IDE (Visual Studio Code) tell me that there's an error :
Unable to import 'numpy'
So here's my main.dart code (the only dart file, it's a test app):
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:starflut/starflut.dart';
void main() => runApp(Main());
class Main extends StatefulWidget {
#override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
var _outputString = '';
StarSrvGroupClass srvGroup;
dynamic python;
_MainState() {
_initStarCore();
}
void showOutput(String info) async {
if (info == null || info.length == 0) return;
_outputString = info;
setState(() {});
}
void _initStarCore() async {
StarCoreFactory starcore = await Starflut.getFactory();
StarServiceClass service =
await starcore.initSimple("test", "123", 0, 0, []);
String resPath = await Starflut.getResourcePath();
await starcore.regMsgCallBackP(
(int serviceGroupID, int uMsg, Object wParam, Object lParam) async {
if (uMsg == Starflut.MSG_DISPMSG || uMsg == Starflut.MSG_DISPLUAMSG) {
showOutput(wParam);
}
print("$serviceGroupID $uMsg $wParam $lParam");
return null;
});
srvGroup = await service["_ServiceGroup"];
bool isAndroid = await Starflut.isAndroid();
if (isAndroid == true) {
String libraryDir = await Starflut.getNativeLibraryDir();
String docPath = await Starflut.getDocumentPath();
if (libraryDir.indexOf("arm64") > 0) {
Starflut.unzipFromAssets("lib-dynload-arm64.zip", docPath, true);
} else if (libraryDir.indexOf("x86_64") > 0) {
Starflut.unzipFromAssets("lib-dynload-x86_64.zip", docPath, true);
} else if (libraryDir.indexOf("arm") > 0) {
Starflut.unzipFromAssets("lib-dynload-armeabi.zip", docPath, true);
} else {
//x86
Starflut.unzipFromAssets("lib-dynload-x86.zip", docPath, true);
}
await Starflut.copyFileFromAssets("python3.6.zip",
"flutter_assets/starfiles", null); //desRelatePath must be null
await Starflut.copyFileFromAssets(
"program.py", "flutter_assets/starfiles", "flutter_assets/starfiles");
}
if (await srvGroup.initRaw("python36", service) == true) {
_outputString = "init starcore and python 3.6 successfully";
} else {
_outputString = "init starcore and python 3.6 failed";
}
await srvGroup.loadRawModule("python", "",
resPath + "/flutter_assets/starfiles/" + "program.py", false);
python = await service.importRawContext("python", "", false, "");
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
//_counter++;
});
}
void runScriptCode() async {
var result = await python.call("multiply", [5, 2]);
_outputString = result.toString();
setState(() {});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: FlatButton(
color: Colors.red,
child: Text(_outputString),
onPressed: () {
runScriptCode();
},
),
),
),
);
}
}
and my Python file:
import numpy as np # (to test)
def multiply(a, b):
return a*b
Here's my tree structure if it can help:
The tree structure of my Flutter project
Hopping you can help me.
Thanks in advance !

You can use chaquopy plugin for flutter, if you want to integrate python packages inside your android app.

If you can, connect python backend to flutter because with that there will be smoothness in your app.

Android Flutter Only: As of last month, Chaquopy is now open-source and has a Flutter plug-in. So that would definitely be easiest way to include python code

Related

Running a Python script from Node.js returns blank

I'm following the posts here but when I go to localhost:3000, the page is blank. I've tried to change my Python script path but that does not seem to work. It seems that it's not accessing the script1.py file. I'm not sure why.
NOTE: script1.py and index.js are both in the same directory.
Here is my index.js:
const express = require('express');
const {spawn} = require('child_process');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
var dataToSend;
const python = spawn('python', ['script1.py']);
python.stdout.on('data', function (data) {
dataToSend = data.toString();
});
python.on('close', (code) => {
console.log(`child process close all stdio with code ${code}`);
res.send(dataToSend)
});
});
app.listen(port);
Here is my script1.py:
print('Hello from python')
And http://localhost:3000/ is completely blank (though it is being run) but it's not displaying 'Hello from python'.
this is how it worked for me:
app.js file:
const spawn = require("child_process").spawn;
const pythonProcess = spawn('python', ["./p1.py"]);
const http = require('http');
let pythonData = null;
pythonProcess.stdout.on('data', (data) => {
pythonData = data.toString();
});
let app = http.createServer((req, res) => {
// Set a response type of plain text for the response
if(req.url === "/getPyt") {
res.end(JSON.stringify(pythonData));
}
if(req.url === "/") {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end("Hello there");
}
});
// Start the server on port 3000
app.listen(3000, 'localhost');
console.log('Node server running on port 3000');
p1.py :
import sys
print("Hello there")
sys.stdout.flush()
I think what you missed is that ./ refferencing python file.
I've commented sys.stdout.flush() inside p1.py and it's also working.

How to upload Files using Flutter Web (Python Server)

I am trying to upload multiple files through a Flutter frontend, possibly to a Python server. I have not found any working code on how to upload files through Flutter Web. My frontend code is according an answer here: How to Pick files and Images for upload with flutter web
import 'package:http/http.dart' as http;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
void main() {
/// your app lunch from here
runApp(new MaterialApp(
//// remove debug logo top left AppBar
debugShowCheckedModeBanner: false,
// application title
title: 'Hello World',
// whole content
home: TabsExample(),
));
}
class TabsExample extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return TabsState();
}
}
class TabsState extends State<TabsExample> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return DefaultTabController(
length: 1,
child: new Scaffold(
appBar: AppBar(
title: Text('Test Tab'),
bottom: TabBar(tabs: [
Tab(
icon: Text(
'Test',
),
),
]),
),
body: TabBarView(children: [
new FileUploadWithHttp(),
]),
));
}
}
class FileUploadWithHttp extends StatefulWidget {
#override
_FileUploadWithHttpState createState() => _FileUploadWithHttpState();
}
class _FileUploadWithHttpState extends State<FileUploadWithHttp> {
PlatformFile objFile;
PlatformFile result;
void chooseFileUsingFilePicker() async {
//-----pick file by file picker,
var result = await FilePicker.platform.pickFiles(
withReadStream:
true, // this will return PlatformFile object with read stream
allowMultiple: true);
print(result.files.length);
print(result.names);
// print(result.files.first.path); //not supported on web
if (result != null) {
setState(() {
objFile = result.files[0];
//print(objFile.readStream);
});
}
}
void uploadSelectedFile() async {
//---Create http package multipart request object
final request = http.MultipartRequest(
"POST",
Uri.parse("http://localhost:8000"), // e.g. localhost
);
//-----add other fields if needed
//request.fields["id"] = "abc";
//-----add selected file with request
request.files.add(new http.MultipartFile(
"file", objFile.readStream, objFile.size,
filename: objFile.name));
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
print('Upload successfull!');
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
//------Button to choose file using file picker plugin
ElevatedButton(
child: Text("Choose File"),
onPressed: () => chooseFileUsingFilePicker()),
//------Show file name when file is selected
if (objFile != null) Text("File name : ${objFile.name}"),
//------Show file size when file is selected
if (objFile != null) Text("File size : ${objFile.size} bytes"),
//------Show upload utton when file is selected
ElevatedButton(
child: Text("Upload"), onPressed: () => uploadSelectedFile()),
],
),
);
}
}
Running this on a python server according to this suggestion: https://gist.github.com/UniIsland/3346170
Or any other one that i've tried does not work, the server is not able to recieve the file properly. The error message is:
(False, "Can't find out file name...", 'by: ', ('::1', 62868, 0, 0))
Is there any straighforward way (possibly with code) on how to upload the file? Or do you have an idea why this error is coming?
Any help would be greatly appreciated!
follow this, it might help you.
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:path/path.dart';
import 'package:dio/dio.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
File _image;
final GlobalKey<ScaffoldState> _scaffoldstate =
new GlobalKey<ScaffoldState>();
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
_uploadFile(image);
setState(() {
_image = image;
});
}
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
// Methode for file upload
void _uploadFile(filePath) async {
// Get base file name
String fileName = basename(filePath.path);
print("File base name: $fileName");
try {
FormData formData =
new FormData.from({"file": new UploadFileInfo(filePath, fileName)});
Response response =
await Dio().post("http://192.168.0.101/saveFile.php", data: formData);
print("File upload response: $response");
// Show the incoming message in snakbar
_showSnakBarMsg(response.data['message']);
} catch (e) {
print("Exception Caught: $e");
}
}
// Method for showing snak bar message
void _showSnakBarMsg(String msg) {
_scaffoldstate.currentState
.showSnackBar(new SnackBar(content: new Text(msg)));
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
key: _scaffoldstate,
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: _image == null ? Text('No image selected.') : Image.file(_image),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}

Kivy android foreground service - unable to open the App after service restarted (case app swiped out from recent apps)

When the main app is running but not active the service notification click brings it to front, that's Ok.
Another case when I close the app by swiping it out from the recent apps list, the service is restarted and when I click the notification just black screen is shown and that's all.
The one interesting thing I noticed, after some time (an hour or two) it gets working - the main app is started on service notification click!
How can I achieve that once the service restarted?
The app is created with Python+Kivy and built with Buildozer and I run it on Android 10.
I tried setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) for the Intent but it didn't help.
Here is some code.
PythonService.java:
package org.kivy.android;
import android.os.Build;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import android.app.Service;
import android.os.IBinder;
import android.os.Bundle;
import android.content.Intent;
import android.content.Context;
import android.util.Log;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Process;
import java.io.File;
//imports for channel definition
import android.app.NotificationManager;
import android.app.NotificationChannel;
import android.graphics.Color;
public class PythonService extends Service implements Runnable {
// Thread for Python code
private Thread pythonThread = null;
// Python environment variables
private String androidPrivate;
private String androidArgument;
private String pythonName;
private String pythonHome;
private String pythonPath;
private String serviceEntrypoint;
// Argument to pass to Python code,
private String pythonServiceArgument;
public static PythonService mService = null;
private Intent startIntent = null;
private boolean autoRestartService = false;
public void setAutoRestartService(boolean restart) {
autoRestartService = restart;
}
public int startType() {
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (pythonThread != null) {
Log.v("python service", "service exists, do not start again");
return START_NOT_STICKY;
}
startIntent = intent;
Bundle extras = intent.getExtras();
androidPrivate = extras.getString("androidPrivate");
androidArgument = extras.getString("androidArgument");
serviceEntrypoint = extras.getString("serviceEntrypoint");
pythonName = extras.getString("pythonName");
pythonHome = extras.getString("pythonHome");
pythonPath = extras.getString("pythonPath");
boolean serviceStartAsForeground = (
extras.getString("serviceStartAsForeground").equals("true")
);
pythonServiceArgument = extras.getString("pythonServiceArgument");
pythonThread = new Thread(this);
pythonThread.start();
if (serviceStartAsForeground) {
doStartForeground(extras);
}
return startType();
}
protected int getServiceId() {
return 1;
}
protected void doStartForeground(Bundle extras) {
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = extras.getString("serviceDescription");
Notification notification;
Context context = getApplicationContext();
Intent contextIntent = new Intent(context, PythonActivity.class);
contextIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // my attempt but it didn't helped
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
notification = new Notification(
context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis());
try {
// prevent using NotificationCompat, this saves 100kb on apk
Method func = notification.getClass().getMethod(
"setLatestEventInfo", Context.class, CharSequence.class,
CharSequence.class, PendingIntent.class);
func.invoke(notification, context, serviceTitle, serviceDescription, pIntent);
} catch (NoSuchMethodException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
}
} else {
// for android 8+ we need to create our own channel
// https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable
String channelName = "Background Service"; //TODO: make this configurable
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(chan);
Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
builder.setContentTitle(serviceTitle);
builder.setContentText(serviceDescription);
builder.setContentIntent(pIntent);
builder.setSmallIcon(context.getApplicationInfo().icon);
notification = builder.build();
}
startForeground(getServiceId(), notification);
}
#Override
public void onDestroy() {
super.onDestroy();
pythonThread = null;
if (autoRestartService && startIntent != null) {
Log.v("python service", "service restart requested");
startService(startIntent);
}
Process.killProcess(Process.myPid());
}
/**
* Stops the task gracefully when killed.
* Calling stopSelf() will trigger a onDestroy() call from the system.
*/
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopSelf();
}
#Override
public void run(){
String app_root = getFilesDir().getAbsolutePath() + "/app";
File app_root_file = new File(app_root);
PythonUtil.loadLibraries(app_root_file,
new File(getApplicationInfo().nativeLibraryDir));
this.mService = this;
nativeStart(
androidPrivate, androidArgument,
serviceEntrypoint, pythonName,
pythonHome, pythonPath,
pythonServiceArgument);
stopSelf();
}
// Native part
public static native void nativeStart(
String androidPrivate, String androidArgument,
String serviceEntrypoint, String pythonName,
String pythonHome, String pythonPath,
String pythonServiceArgument);
}
and the class extending PythonService:
package org.test.schedules;
import android.content.Intent;
import android.content.Context;
import org.kivy.android.PythonService;
public class ServiceService extends PythonService {
#Override
protected int getServiceId() {
return 1;
}
static public void start(Context ctx, String pythonServiceArgument) {
Intent intent = new Intent(ctx, ServiceService.class);
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
intent.putExtra("androidArgument", argument);
intent.putExtra("serviceTitle", "Schedules1106fgs_sdk28");
intent.putExtra("serviceDescription", "Service");
intent.putExtra("serviceEntrypoint", "service/main.py");
intent.putExtra("pythonName", "service");
intent.putExtra("serviceStartAsForeground", "true");
intent.putExtra("pythonHome", argument);
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
ctx.startService(intent);
}
static public void stop(Context ctx) {
Intent intent = new Intent(ctx, ServiceService.class);
ctx.stopService(intent);
}
}
Also here is a piece of AndroidManifest.xml
<activity android:name="org.kivy.android.PythonActivity"
android:label="#string/app_name"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale|uiMode|uiMode|screenSize|smallestScreenSize|layoutDirection"
android:screenOrientation="portrait"
android:launchMode="singleTask"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="org.kivy.android.PythonService"
android:process=":pythonservice" />
<service android:name="org.test.schedules.ServiceService"
android:process=":service_service" />
Will be much appreciated for your help
Update
I haven't found the solution yet but noticed but seems the issue is related to background cached process. So if I go to Developer options -> Running services -> Show cached processes and stop the process there manually the app restarts fine.
I guess the solution could be somehow to prevent creating the cached process in the background or to kill that process at some specific moment, on app exit or service restart, I'm not an expert in Android, can anyone help with this?

Call a Python script from React with next routing and a node.js server

I am working on an ethereum application that uses react, next-routing and a basic node.js server. I want to run a python script on a specific route, claimAnalysis to be specific, in order to perform some predictions. I want the script to be executed every time I visit the claimAnalysis route.
server.js
const express = require("express")();
const next = require("next");
const app = next({
dev: process.env.NODE_ENV !== "production"
});
const routes = require("./routes");
const handler = routes.getRequestHandler(app);
app.prepare().then(() => {
const server = express.use(handler);
server.listen(3000, (err) => {
if(err) throw err;
console.log("Server ready on localhost:3000");
});
});
routes.js
const routes = require("next-routes")();
routes
.add("/insurance/new", "/insurance/new")
.add("/insurance/:address", "/insurance/show")
.add("/insurance/:address/claims", "/insurance/claims/index")
.add("/insurance/:address/claims/new", "/insurance/claims/new")
.add("/insurance/:address/claims/:id/claimAnalysis", "/insurance/claims/claimAnalysis");
module.exports = routes;
Please guide me if I can call a function from the claimAnalysis.js file that runs the python script.
If you're using Node 10+, you can use util.promisify to execute your python scriptwhich returns a Promise<{ stdout, stderr }> object. See an example below:
const util = require('util');
const exec = util.promisify(require('child_process').exec);
// function to execute python script
async function executeScript(path) {
try {
const { stdout, stderr } = await exec(`python3 ${path}`);
console.log('stdout:', stdout);
console.log('stderr:', stderr);
} catch (e) {
console.error(e);
}
}
Now let's use the function in your route:
app.get('/insurance/claims/claimAnalysis', async function (req, res) {
await executeScript();
res.send('claimAnalysis request completed');
...
})
You can use "child_process" to finish function.
You can see this example:
const path = require('path')
const {spawn} = require('child_process')
/**
* Run python myscript, pass in `-u` to not buffer console output
* #return {ChildProcess}
*/
function runScript(){
return spawn('python', [
"-u",
path.join(__dirname, 'myscript.py'),
"--foo", "some value for foo",
]);
}
const subprocess = runScript()
// print output of script
subprocess.stdout.on('data', (data) => {
console.log(`data:${data}`);
});
subprocess.stderr.on('data', (data) => {
console.log(`error:${data}`);
});
subprocess.stderr.on('close', () => {
console.log("Closed");
});

NodeJs: Get output of python-shell to send back to client

I am trying to create a website where a user can submit python code, it gets sent to my server to be executed and I send back the results to the client. Currently I am using a NodeJs server and need to run the python code from there. To do this, I am using Python-shell like so:
const runPy = async (code) => {
const options = {
mode: 'text',
pythonOptions: ['-u'],
scriptPath: path.join(__dirname, '../'),
args: [code],
};
const result = await PythonShell.run('script.py', options, (err, results) => {
if (err) throw err;
return results; <----- HOW DO I RETURN THIS
});
console.log(result.stdout);
return result;
};
I understand I can console.log() the results in the PythonShell.run() but is there a way to return the results from my runPy function to then be manipulated and sent back to the client?
It looks from the python-shell documentation that the PythonShell.run method doesn't have an async mode. So, one option is to wrap it in a promise:
const runPy = async (code) => {
const options = {
mode: 'text',
pythonOptions: ['-u'],
scriptPath: path.join(__dirname, '../'),
args: [code],
};
// wrap it in a promise, and `await` the result
const result = await new Promise((resolve, reject) => {
PythonShell.run('script.py', options, (err, results) => {
if (err) return reject(err);
return resolve(results);
});
});
console.log(result.stdout);
return result;
};

Categories

Resources