I have this stream which should handle a heavy workload subdivided in frames:
Stream<int> _test() async* {
for(int i = 0; i < 50; i++){
///Heavy workload simulation
for(int x = 0; x < 100000; x++){
double _r = Random().nextDouble();
}
print(i);
yield i;
}
}
And then I'm showing a simple indicator using a StreamBuilder:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
StreamBuilder<int>(
stream: _test(),
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text(
'${snapshot.data}',
);
},
),
RaisedButton(onPressed: () {_test();})
],
),
),
);
}
But when I execute the code, all the values of i (from 0 to 49) get printed with a little bit of delay, so the _test stream is not instantaneous. But the UI only gets updated at 49, so when the stream ends.
Am I doing something wrong?
StreamBuilder will build its components using the snapshot accompanied by ConnectionState.done, which in your case is the last value (49) when your _test() function finishes execution, because assigning them consecutively won't allow earlier operations to finish.
One solution will be adding Future.delayed(). Which will allow each iteration to be streamed completely before moving to the next value. So _test() should be modified to :
Stream<int> _test() async* {
for(int i = 0; i < 50; i++){
///Heavy workload simulation
for(int x = 0; x < 100000; x++){
double _r = Random().nextDouble();
}
print(i);
// Delay duration is arbitrarily chosen
await Future.delayed(Duration(milliseconds:10), () {
});
yield i;
}
}
Related
I am testing the Platform-Channels with the windows visual studio 2022.
I can pass the single value from the flutter UI while I get the battery value, but I want to send multi-data from flutter to the windows code.
Here is mine test code which I follow the flutter tutorial:
class _MyHomePageState extends State<MyHomePage> {
static const MethodChannel platform =
MethodChannel('samples.flutter.dev/battery');
String _batteryLevel = 'Unknown battery level.';
int value = 100;
Future<void> _getBatteryLevel(int fluval) async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel', fluval);
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
#override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
_getBatteryLevel(100);
},
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
],
),
),
);
}
}
and the windows side:
void initMethodChannel(flutter::FlutterEngine* flutter_instance) {
const static std::string channel_name("samples.flutter.dev/battery");
auto channel =
std::make_unique<flutter::MethodChannel<>>(
flutter_instance->messenger(), channel_name,
&flutter::StandardMethodCodec::GetInstance());
channel->SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
// cheack method name called from dart
if (call.method_name().compare("getBatteryLevel") == 0) {
int battery_level = GetBatteryLevel();
const auto* flu_value = std::get_if<int>(call.arguments());
std::cout << typeid(flu_value).name() << std::endl;
if (!flu_value) {
result->Error("Missing required type parameter","Expected int");
}
std::cout << "From Flutter: " << *flu_value << std::endl;
if (battery_level != -1) {
result->Success(battery_level);
}
else {
result->Error("UNAVAILABLE", "Battery level not available.");
}
}
else {
result->NotImplemented();
}
});
}
if I want to send multi-data like this:
Future<void> _getBatteryLevel(int fluval, int data2,...,...) ...
How should I do or parse these two values fluval and data2 when call once invokeMethod?
Any examples that I can reference? or tips for the Windows UI design?
I try to search but the most answer is for the Android or IOS app, and the android answer is using call.argument("value") which you can choose specific data value to use but the windows only has call.arguments(); function call, I don't know how to get the specific data I want.
============================================================
[Update]
From the #Richard tips and example,
I made a test and it works, thanks #Richard!
Here is the Flutter UI example code: (inside the Stateful Widget State class), fluval= 100, fluval2 = 87
String _getBackV1 = 'Unknown Value 1.';
String _getBackV2 = 'Unknown Value 2.';
int value = 100;
Future<void> _testMapPassValue(int fluval, int fluval2) async {
String backValue1, backValue2;
try {
final Map<String, dynamic>? getReply = await platform
.invokeMapMethod<String, dynamic>('testPass',
<String, dynamic>{"fluval": fluval, "fluval2": fluval2});
backValue1 = "Get Back Value 1 from windows: ${getReply!["fluval"]}";
backValue2 = "Get Back Value 2 from windows: ${getReply!["fluval2"]}";
} on PlatformException catch (e) {
backValue1 = "Failed to get Value 1: '${e.message}'.";
backValue2 = "Failed to get Value 2: '${e.message}'.";
}
setState(() {
_getBackV1 = backValue1;
_getBackV2 = backValue2;
});
}
At the Windows side example code:
using flutter::EncodableList;
using flutter::EncodableMap;
using flutter::EncodableValue;
constexpr char testMapPassValue[] = "testPass";
constexpr char passKey1[] = "fluval";
constexpr char passKey2[] = "fluval2";
The method call handler:
if (call.method_name().compare(testMapPassValue) == 0) {
const auto* getArguments_flu = std::get_if<flutter::EncodableMap>(call.arguments());
assert(getArguments_flu);
return Flu_2_Win_MethodHandler(*getArguments_flu, std::move(result));
}
void Flu_2_Win_MethodHandler(
const EncodableMap& args, std::unique_ptr<flutter::MethodResult<>> result) {
int wins2fluValue1;
int wins2fluValue2;
EncodableMap win2fluMap;
const auto* flu2winsValue1 =
std::get_if<int>(ValueOrNull(args, passKey1));
if (!flu2winsValue1) {
return result->Error("argument_error",
std::string(passKey1) + " argument missing");
}
wins2fluValue1 = *flu2winsValue1;
std::cout << "From Flutter value1: " << *flu2winsValue1 << std::endl;
wins2fluValue1 = wins2fluValue1 + 10;
std::cout << "After calculate value1: " << wins2fluValue1 << std::endl;
const auto* flu2winsValue2 =
std::get_if<int>(ValueOrNull(args, passKey2));
if (!flu2winsValue2) {
return result->Error("argument_error",
std::string(passKey2) + " argument missing");
}
wins2fluValue2 = *flu2winsValue2;
std::cout << "From Flutter value1: " << *flu2winsValue2 << std::endl;
wins2fluValue2 = wins2fluValue2 + 20;
std::cout << "After calculate value2: " << wins2fluValue2 << std::endl;
win2fluMap.insert(std::pair<std::string, int>("fluval", wins2fluValue1));
win2fluMap.insert(std::pair<std::string, int>("fluval2", wins2fluValue2));
return result->Success(std::move(EncodableValue(win2fluMap)));
}
and the final result:
Thank you!
You can only pass a single entity as argument or result, but that entity can be any of the supported types (whether primitive - int, double, etc - or complex - list or map).
The mapping between Dart and C++ (native for Windows) types is:
// std::monostate -> null
// bool -> bool
// int32_t -> int
// int64_t -> int
// double -> double
// std::string -> String
// std::vector<uint8_t> -> Uint8List
// std::vector<int32_t> -> Int32List
// std::vector<int64_t> -> Int64List
// std::vector<float> -> Float32List
// std::vector<double> -> Float64List
// EncodableList -> List
// EncodableMap -> Map
(see encodable_value.h)
So, to send your two integers (fluval and data2) you could choose to put them in a Dart list (i.e. <int>[123, 456]) or a Dart map (i.e. {'fluval':123, 'data2':456}). At the native end, arguments will be either an EncodableList or EncodableMap.
Here's an example of how a complex Dart structure would appear at the native end:
// {
// 'flag': true,
// 'name': 'Thing',
// 'values': [1, 2.0, 4],
// }
// would correspond to:
// EncodableValue(EncodableMap{
// {EncodableValue("flag"), EncodableValue(true)},
// {EncodableValue("name"), EncodableValue("Thing")},
// {EncodableValue("values"), EncodableValue(EncodableList{
// EncodableValue(1),
// EncodableValue(2.0),
// EncodableValue(4),
// })},
// })
The camera plugin has some example usage (to get the arguments as a map):
const auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
In the same file, check out the two utility functions:
// Looks for |key| in |map|, returning the associated value if it is present, or
// a nullptr if not.
const EncodableValue* ValueOrNull(const EncodableMap& map, const char* key) {
auto it = map.find(EncodableValue(key));
if (it == map.end()) {
return nullptr;
}
return &(it->second);
}
// Looks for |key| in |map|, returning the associated int64 value if it is
// present, or std::nullopt if not.
std::optional<int64_t> GetInt64ValueOrNull(const EncodableMap& map,
const char* key) {
auto value = ValueOrNull(map, key);
if (!value) {
return std::nullopt;
}
if (std::holds_alternative<int32_t>(*value)) {
return static_cast<int64_t>(std::get<int32_t>(*value));
}
auto val64 = std::get_if<int64_t>(value);
if (!val64) {
return std::nullopt;
}
return *val64;
}
and their typical usage:
const auto* camera_name =
std::get_if<std::string>(ValueOrNull(args, kCameraNameKey));
auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey);
I'm trying to pick a random color for strokeline from an array list. From the program, it indicates that random() expect parameter like random(float). But I feel a bit stuck of how to correct it. Below is my code:
int n = 200;
drop[] d;
color[] colors = {
#013a63,
#01497c,
#014f86,
#2a6f97,
#2c7da0,
#468faf,
#61a5c2,
#89c2d9,
#a9d6e5,
};
void setup() {
size(1080, 720);
stroke(random(colors));
d = new drop[n];
for(int i = 0; i < d.length; i ++) {
d[i] = new drop(random(width), random(-10, 20), random(5));
}
}
void draw() {
background(1, 42, 74,10);
for(int i = 0; i < d.length; i ++) {
d[i].show();
d[i].update();
}
}
Generate a random # between 0 and 8, then use that index:
stroke(colors[(int)random(8)]);
or dynamically using the length or colors (in case it changes):
stroke(colors[(int)random(colors.length)]);
I'm trying to make an animation of sorting algorithms in flutter. So far I've coded the algorithm and managed to get some sort of animation by iterating once at a time instead of the whole sorting process but you have to keep tapping the button to sort, one item at a time. I've been trying to look for a way to animate this process. Here's my code:
import 'package:flutter/material.dart';
import 'dart:math';
List<double> rectHeights = new List<double>();
int n = 2;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sorting',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
int _selectedIndex = 0;
final _widgetOptions = [
Text('Index 0: Sort'),
Text('Index 1: Shuffle'),
];
#override
void initState() {
super.initState();
Random random = new Random();
for (int i = 0; i < 35; i++) {
double ranNum = random.nextDouble() * 600;
rectHeights.add(ranNum);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: Row(
children: rectangles(),
),
),
),
bottomNavigationBar: BottomNavigationBar(
iconSize: 50.0,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.sort), title: Text('Sort', style: TextStyle(fontSize: 20.0),)),
BottomNavigationBarItem(icon: Icon(Icons.shuffle), title: Text('Shuffle', style: TextStyle(fontSize: 20.0),)),
],
currentIndex: _selectedIndex,
fixedColor: Colors.blue,
onTap: _onItemTapped,
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
switch(_selectedIndex) {
case 0:
setState(() {
insertSortOnce(rectHeights, 1);
});
break;
case 1:
setState(() {
shuffle(rectHeights);
n = 2;
});
}
}
}
List<Widget> rectangles() {
List<Widget> rects = new List<Widget>();
for (double height in rectHeights) {
var rect = Padding(
padding: EdgeInsets.symmetric(horizontal: 1.0),
child: Container(
width: 8.0,
height: height,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.blue
),
),
);
rects.add(rect);
}
return rects;
}
void insertSort(values, choice) {
int i, j;
double key, temp;
for (i = 1; i < values.length; i++) {
key = values[i];
j = i - 1;
switch (choice) {
case 1:
while (j >= 0 && key < values[j]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
j--;
}
break;
case 2:
while (j >= 0 && key > values[j]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
j--;
}
break;
}
}
}
void insertSortOnce(values, choice) {
int i, j;
double key, temp;
for (i = 1; i < n; i++) {
key = values[i];
j = i - 1;
switch (choice) {
case 1:
while (j >= 0 && key < values[j]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
j--;
}
break;
case 2:
while (j >= 0 && key > values[j]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
j--;
}
break;
}
}
n++;
}
List shuffle(List items) {
var random = new Random();
// Go through all elements.
for (var i = items.length - 1; i > 0; i--) {
// Pick a pseudorandom number according to the list length
var n = random.nextInt(i + 1);
var temp = items[i];
items[i] = items[n];
items[n] = temp;
}
return items;
}
You can use a Future function with a short delay, for example (one second) to call the insertSort() after each second till the rectHeights are sorted:
var _notSorted = true ;
var _shiftNotPressed = true ;
Future sortRectangles() async {
while(_notSorted & _shiftPressed) { // You have to provide a condition to know when to stop
await new Future.delayed(const Duration(seconds: 1), () {
insertSortOnce(rectHeights, 1);
}
);
}
}
Then checking for shuffling:
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
switch(_selectedIndex) {
case 0:
setState(() {
insertSortOnce(rectHeights, 1);
});
break;
case 1:
_shiftNotPressed = false ; // This is what you should add
setState(() {
shuffle(rectHeights);
n = 2;
});
}
}
Then Sorting completion:
void insertSortOnce(values, choice) {
_notSorted = false ; // if it didn't execute the loop it means sorted
int i, j;
double key, temp;
for (i = 1; i < n; i++) {
key = values[i];
j = i - 1;
switch (choice) {
case 1:
while (j >= 0 && key < values[j]) {
_notSorted = true ; //You should figure a more efficient way to do this
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
j--;
}
break;
case 2:
while (j >= 0 && key > values[j]) {
temp = values[j];
values[j] = values[j + 1];
values[j + 1] = temp;
j--;
}
break;
}
}
n++;
}
Also, You should include in your condition whether the shift button is pressed and to terminate accordingly.
I’m trying to figure out how to get the max threshold of an AudioInput in my Processing sketch using Minim. I am trying to accurately map the signal so that I can adjust other parameters.
import ddf.minim.*;
Minim minim;
AudioInput in;
void setup()
{
size(512, 200, P3D);
minim = new Minim(this);
// use the getLineIn method of the Minim object to get an AudioInput
in = minim.getLineIn();
}
void draw()
{
background(0);
stroke(255);
// draw the waveforms so we can see what we are monitoring
for(int i = 0; i < in.bufferSize() - 1; i++)
{
line( i, 50 + in.left.get(i)*50, i+1, 50 + in.left.get(i+1)*50 );
line( i, 150 + in.right.get(i)*50, i+1, 150 + in.right.get(i+1)*50 );
}
String monitoringState = in.isMonitoring() ? "enabled" : "disabled";
text( "Input monitoring is currently " + monitoringState + ".", 5, 15 );
}
void keyPressed()
{
if ( key == 'm' || key == 'M' )
{
if ( in.isMonitoring() )
{
in.disableMonitoring();
}
else
{
in.enableMonitoring();
}
}
}
You can use a couple of variable of keep track of the highest and lower values encountered then plug those later for mapping:
import ddf.minim.*;
Minim minim;
AudioInput in;
//keep track of the lowest and highest values
float minValue = Float.MAX_VALUE;
float maxValue = 0;
void setup()
{
size(512, 200, P3D);
minim = new Minim(this);
// use the getLineIn method of the Minim object to get an AudioInput
in = minim.getLineIn();
}
void draw()
{
background(0);
stroke(255);
strokeWeight(1);
// draw the waveforms so we can see what we are monitoring
for(int i = 0; i < in.bufferSize() - 1; i++)
{
line( i, 50 + in.left.get(i)*50, i+1, 50 + in.left.get(i+1)*50 );
line( i, 150 + in.right.get(i)*50, i+1, 150 + in.right.get(i+1)*50 );
//update min,max values
if(in.left.get(i) < minValue) {
minValue = in.left.get(i);
}
if(in.left.get(i) > maxValue) {
maxValue = in.left.get(i);
}
}
//simple demo, plot the min/max values, feel free to plug these into map()
strokeWeight(3);
stroke(192,0,0);
line(0,50 + minValue * 50,width,50 + minValue * 50);
stroke(0,192,0);
line(0,50 + maxValue * 50,width,50 + maxValue * 50);
String monitoringState = in.isMonitoring() ? "enabled" : "disabled";
text( "Input monitoring is currently " + monitoringState + ".", 5, 15 );
}
void keyPressed()
{
if ( key == 'm' || key == 'M' )
{
if ( in.isMonitoring() )
{
in.disableMonitoring();
}
else
{
in.enableMonitoring();
}
}
}
Note that if you clap or make a loud noise, the highest will jump and stay there. Depending on the length of your sketch, you might want to reset these values after a while. Additionally you may want to also add easing to these values, so the mapping doesn't feel too jumpy when going from silent to loud environments and back.
I'm having a ton of trouble making a simple video delay in processing. I looked around on the internet and I keep finding the same bit of code and I can't get it to work at all. When I first tried it, it did nothing (at all). Here's my modified version (which at least seems to load frames into the buffer), I really have no idea why it doesn't work and I'm getting really tired of pulling out my hair. Please... please, for the love of god, please somebody point out the stupid mistake I'm making here.
And now, without further delay (hah, get it?), the code:
import processing.video.*;
VideoBuffer vb;
Movie myMovie;
Capture cam;
float seconds = 1;
void setup() {
size(320,240, P3D);
frameRate(30);
String[] cameras = Capture.list();
if (cameras.length == 0) {
println("There are no cameras available for capture.");
exit();
} else {
println("Available cameras:");
for (int i = 0; i < cameras.length; i++) {
println(cameras[i]);
}
cam = new Capture(this, cameras[3]);
cam.start();
}
vb = new VideoBuffer(90, width, height);
}
void draw() {
if (cam.available() == true) {
cam.read();
vb.addFrame(cam);
}
image(cam, 0, 0);
image( vb.getFrame(), 150, 0 );
}
class VideoBuffer
{
PImage[] buffer;
int inputFrame = 0;
int outputFrame = 0;
int frameWidth = 0;
int frameHeight = 0;
VideoBuffer( int frames, int vWidth, int vHeight )
{
buffer = new PImage[frames];
for(int i = 0; i < frames; i++)
{
this.buffer[i] = new PImage(vWidth, vHeight);
}
this.inputFrame = 0;
this.outputFrame = 1;
this.frameWidth = vWidth;
this.frameHeight = vHeight;
}
// return the current "playback" frame.
PImage getFrame()
{
return this.buffer[this.outputFrame];
}
// Add a new frame to the buffer.
void addFrame( PImage frame )
{
// copy the new frame into the buffer.
this.buffer[this.inputFrame] = frame;
// advance the input and output indexes
this.inputFrame++;
this.outputFrame++;
println(this.inputFrame + " " + this.outputFrame);
// wrap the values..
if(this.inputFrame >= this.buffer.length)
{
this.inputFrame = 0;
}
if(this.outputFrame >= this.buffer.length)
{
this.outputFrame = 0;
}
}
}
This works in Processing 2.0.1.
import processing.video.*;
Capture cam;
PImage[] buffer;
int w = 640;
int h = 360;
int nFrames = 60;
int iWrite = 0, iRead = 1;
void setup(){
size(w, h);
cam = new Capture(this, w, h);
cam.start();
buffer = new PImage[nFrames];
}
void draw() {
if(cam.available()) {
cam.read();
buffer[iWrite] = cam.get();
if(buffer[iRead] != null){
image(buffer[iRead], 0, 0);
}
iWrite++;
iRead++;
if(iRead >= nFrames-1){
iRead = 0;
}
if(iWrite >= nFrames-1){
iWrite = 0;
}
}
}
There is a problem inside your addFrame-Method. You just store a reference to the PImage object, whose pixels get overwritten all the time. You have to use buffer[inputFrame] = frame.get() instead of buffer[inputFrame] = frame. The get() method returns a copy of the image.