1. 程式人生 > >Flutter學習指南:檔案、儲存和網路

Flutter學習指南:檔案、儲存和網路

Flutter學習指南
互動、手勢和動畫
UI佈局和控制元件
熟悉Dart語言
編寫第一個應用
開發環境搭建

本篇文章我們先學習 Flutter IO 相關的基礎知識,然後在 Flutter學習指南:互動、手勢和動畫 的基礎上,繼續開發一個 echo 客戶端。由於日常開發中 HTTP 比 socket 更常見,我們的 echo 客戶端將會使用 HTTP 協議跟服務端通訊。Echo 伺服器也會使用 Dart 來實現。

檔案

為了執行檔案操作,我們可以使用 Dart 的 io 包:

import 'dart:io';
複製程式碼

建立檔案

在 Dart 裡,我們通過類 File 來執行檔案操作:

void foo() async {
  const filepath = "path to your file";
  var file = File(filepath);
  try {
    bool exists = await file.exists();
    if (!exists) {
      await
 file.create();
    }
  } catch (e) {
    print(e);
  }
}
複製程式碼

相對於 CPU,IO 總是很慢的,所以大部分檔案操作都返回一個 Future,並在出錯的時候丟擲一個異常。如果你需要,也可以使用同步版本,這些方法都帶一個字尾 Sync:

void foo() {
  const filepath = "path to your file"
;
  var file = File(filepath);
  try {
    bool exists = file.existsSync();
    if (!exists) {
      file.createSync();
    }
  } catch (e) {
    print(e);
  }
}
複製程式碼

async 方法使得我們可以像寫同步方法一樣寫非同步程式碼,同步版本的 io 方法已經沒有太多使用的必要了(Dart 1 不支援 async 函式,所以同步版本的方法的存在是有必要的)。

寫檔案

寫 String 時我們可以使用 writeAsString 和 writeAsBytes 方法:

const filepath = "path to your file";
var file = File(filepath);
await file.writeAsString('Hello, Dart IO');
List<int> toBeWritten = [123];
await file.writeAsBytes(toBeWritten);
複製程式碼

如果只是為了寫檔案,還可以使用 openWrite 開啟一個 IOSink:

void foo() async {
  const filepath = "path to your file";
  var file = File(filepath);
  IOSink sink;
  try {
    sink = file.openWrite();
    // 預設的寫檔案操作會覆蓋原有內容;如果要追究內容,用 append 模式
    // sink = file.openWrite(mode: FileMode.append);

    // write() 的引數是一個 Object,他會執行 obj.toString() 把轉換後
    // 的 String 寫入檔案
    sink.write('Hello, Dart');
    //呼叫 flush 後才會真的把資料寫出去
    await sink.flush();
  } catch (e) {
    print(e);
  } finally {
    sink?.close();
  }
}
複製程式碼

讀檔案

讀寫原始的 bytes 也是相當簡單的:

var msg = await file.readAsString();
List<int> content = await file.readAsBytes();
複製程式碼

和寫檔案類似,它還有一個 openRead 方法:

// Stream 是 async 包裡的類
import 'dart:async';
// utf8、LineSplitter 屬於 convert 包
import 'dart:convert';
import 'dart:io';

void foo() async {
  const filepath = "path to your file";
  var file = File(filepath);
  try {
    Stream<List<int>> stream = file.openRead();
    var lines = stream
        // 把內容用 utf-8 解碼
        .transform(utf8.decoder)
        // 每次返回一行
        .transform(LineSplitter());
    await for (var line in lines) {
      print(line);
    }
  } catch (e) {
    print(e);
  }
}
複製程式碼

最後需要注意的是,我們讀寫 bytes 的時候,使用的物件是 List<int>,而一個 int 在 Dart 裡面有 64 位。Dart 一開始設計就是用於 Web,這部分的效率也就不那麼高了。

JSON

JSON 相關的 API 放在了 convert 包裡面:

import 'dart:convert';
複製程式碼

把物件轉換為 JSON

假設我們有這樣一個物件:

class Point {
  int x;
  int y;
  String description;

  Point(this.x, this.y, this.description);
}
複製程式碼

為了把他轉換為 JSON,我們給他定義一個 toJson 方法(注意,不能改變他的方法簽名):

class Point {
  // ...

  // 注意,我們的方法只有一個語句,這個語句定義了一個 map。
  // 使用這種語法的時候,Dart 會自動把這個 map 當做方法的返回值
  Map<Stringdynamic> toJson() => {
    'x': x,
    'y': y,
    'desc': description
  };
}
複製程式碼

接下來我們呼叫 json.encode 方法把物件轉換為 JSON:

void main() {
  var point = Point(212'Some point');
  var pointJson = json.encode(point);
  print('pointJson = $pointJson');

  // List, Map 都是支援的
  var points = [point, point];
  var pointsJson = json.encode(points);
  print('pointsJson = $pointsJson');
}

// 執行後打印出:
// pointJson = {"x":2,"y":12,"desc":"Some point"}
// pointsJson = [{"x":2,"y":12,"desc":"Some point"},{"x":2,"y":12,"desc":"Some point"}]
複製程式碼

把 JSON 轉換為物件

首先,我們給 Point 類再加多一個建構函式:

class Point {
  // ...

  Point.fromJson(Map<Stringdynamic> map)
      : x = map['x'], y = map['y'], description = map['desc'];

  // 為了方便後面演示,也加入一個 toString
  @override
  String toString() {
    return "Point{x=$x, y=$y, desc=$description}";
  }
}
複製程式碼

為了解析 JSON 字串,我們可以用 json.decode 方法:

dynamic obj = json.decode(jsonString);
複製程式碼

返回一個 dynamic 的原因在於,Dart 不知道傳進去的 JSON 是什麼。如果是一個 JSON 物件,返回值將是一個 Map;如果是 JSON 陣列,則會返回 List<dynamic>:

void main() {
  var point = Point(212'Some point');
  var pointJson = json.encode(point);
  print('pointJson = $pointJson');
  var points = [point, point];
  var pointsJson = json.encode(points);
  print('pointsJson = $pointsJson');
  print('');

  var decoded = json.decode(pointJson);
  print('decoded.runtimeType = ${decoded.runtimeType}');
  var point2 = Point.fromJson(decoded);
  print('point2 = $point2');

  decoded = json.decode(pointsJson);
  print('decoded.runtimeType = ${decoded.runtimeType}');
  var points2 = <Point>[];
  for (var map in decoded) {
    points2.add(Point.fromJson(map));
  }
  print('points2 = $points2');
}
複製程式碼

執行結果如下:

pointJson = {"x":2,"y":12,"desc":"Some point"}
pointsJson = [{"x":2,"y":12,"desc":"Some point"},{"x":2,"y":12,"desc":"Some point"}]

decoded.runtimeType = _InternalLinkedHashMap<String, dynamic>
point2 = Point{x=2, y=12, desc=Some point}
decoded.runtimeType = List<dynamic>
points2 = [Point{x=2, y=12, desc=Some point}, Point{x=2, y=12, desc=Some point}]
複製程式碼

需要說明的是,我們把 Map 轉化為物件時使用時定義了一個建構函式,但這個是任意的,使用靜態方法、Dart 工廠方法等都是可行的。之所以限定 toJson 方法的原型,是因為 json.encode 只支援 Map、List、String、int 等內建型別。當它遇到不認識的型別時,如果沒有給它設定引數 toEncodable,就會呼叫物件的 toJson 方法(所以方法的原型不能改變)。

HTTP

為了向伺服器傳送 HTTP 請求,我們可以使用 io 包裡面的 HttpClient。但它實在不是那麼好用,於是就有人弄出了一個 http 包。為了使用 http 包,需要修改 pubspec.yaml:

# pubspec.yaml
dependencies:
  http: ^0.11.3+17
複製程式碼

http 包的使用非常直接,為了發出一個 GET,可以使用 http.get 方法;對應的,還有 post、put 等。

import 'package:http/http.dart' as http;

Future<String> getMessage() async {
  try {
    final response = await http.get('http://www.xxx.com/yyy/zzz');
    if (response.statusCode == 200) {
      return response.body;
    }
  } catch (e) {
    print('getMessage: $e');
  }
  return null;
}
複製程式碼

HTTP POST 的例子我們在下面實現 echo 客戶端的時候再看。

使用 SQLite 資料庫

包 sqflite 可以讓我們使用 SQLite:

dependencies:
  sqflite: any
複製程式碼

sqflite 的 API 跟 Android 的那些非常像,下面我們直接用一個例子來演示:

import 'package:sqflite/sqflite.dart';

class Todo {
  static const columnId = 'id';
  static const columnTitle = 'title';
  static const columnContent = 'content';

  int id;
  String title;
  String content;

  Todo(this.title, this.content, [this.id]);

  Todo.fromMap(Map<Stringdynamic> map)
      : id = map[columnId], title = map[columnTitle], content = map[columnContent];

  Map<Stringdynamic> toMap() => {
    columnTitle: title,
    columnContent: content,
  };

  @override
  String toString() {
    return 'Todo{id=$id, title=$title, content=$content}';
  }
}

void foo() async {
  const table = 'Todo';
  // getDatabasesPath() 的 sqflite 提供的函式
  var path = await getDatabasesPath() + '/demo.db';
  // 使用 openDatabase 開啟資料庫
  var database = await openDatabase(
      path,
      version: 1,
      onCreate: (db, version) async {
        var sql ='''
            CREATE TABLE $table ('
            ${Todo.columnId} INTEGER PRIMARY KEY,'
            ${Todo.columnTitle} TEXT,'
            ${Todo.columnContent} TEXT'
            )
            '''
;
        // execute 方法可以執行任意的 SQL
        await db.execute(sql);
      }
  );
  // 為了讓每次執行的結果都一樣,先把資料清掉
  await database.delete(table);

  var todo1 = Todo('Flutter''Learn Flutter widgets.');
  var todo2 = Todo('Flutter''Learn how to to IO in Flutter.');

  // 插入資料
  await database.insert(table, todo1.toMap());
  await database.insert(table, todo2.toMap());

  List<Map> list = await database.query(table);
  // 重新賦值,這樣 todo.id 才不會為 0
  todo1 = Todo.fromMap(list[0]);
  todo2 = Todo.fromMap(list[1]);
  print('query: todo1 = $todo1');
  print('query: todo2 = $todo2');

  todo1.content += ' Come on!';
  todo2.content += ' I\'m tired';
  // 使用事務
  await database.transaction((txn) async {
    // 注意,這裡面只能用 txn。直接使用 database 將導致死鎖
    await txn.update(table, todo1.toMap(),
        // where 的引數裡,我們可以使用 ? 作為佔位符,對應的值按順序放在 whereArgs

        // 注意,whereArgs 的引數型別是 List,這裡不能寫成 todo1.id.toString()。
        // 不然就變成了用 String 和 int 比較,這樣一來就匹配不到待更新的那一行了
        where: '${Todo.columnId} = ?', whereArgs: [todo1.id]);
    await txn.update(table, todo2.toMap(),
        where: '${Todo.columnId} = ?', whereArgs: [todo2.id]);
  });

  list = await database.query(table);
  for (var map in list) {
    var todo = Todo.fromMap(map);
    print('updated: todo = $todo');
  }

  // 最後,別忘了關閉資料庫
  await database.close();
}
複製程式碼

執行結果如下:

query: todo1 = Todo{id=1, title=Flutter, content=Learn Flutter widgets}
query: todo2 = Todo{id=2, title=Flutter, content=Learn how to to IO in Flutter}
updated: todo = Todo{id=1, title=Flutter, content=Learn Flutter widgets. Come on!}
updated: todo = Todo{id=2, title=Flutter, content=Learn how to to IO in Flutter. I'm tired}
複製程式碼

有 Android 經驗的讀者會發現,使用 Dart 編寫資料庫相關程式碼的時候舒服很多。如果讀者對資料庫不太熟悉,可以參考《SQL必知必會》。本篇的主要知識點到這裡的就講完了,作為練習,下面我們就一起來實現 echo 客戶端的後端。

echo 客戶端

HTTP 服務端

在開始之前,你可以在 GitHub 上找到上篇文章的程式碼,我們將在它的基礎上進行開發。

git clone https://github.com/Jekton/flutter_demo.git
cd flutter_demo
git checkout ux-basic
複製程式碼

服務端架構

首先我們來看看服務端的架構(說是架構,但其實非常的簡單,或者說很簡陋):

import 'dart:async';
import 'dart:io';

class HttpEchoServer {

  final int port;
  HttpServer httpServer;
  // 在 Dart 裡面,函式也是 first class object,所以我們可以直接把
  // 函式放到 Map 裡面
  Map<Stringvoid Function(HttpRequest)> routes;

  HttpEchoServer(this.port) {
    _initRoutes();
  }

  void _initRoutes() {
    routes = {
      // 我們只支援 path 為 '/history' 和 '/echo' 的請求。
      // history 用於獲取歷史記錄;
      // echo 則提供 echo 服務。
      '/history': _history,
      '/echo': _echo,
    };
  }

  // 返回一個 Future,這樣客戶端就能夠在 start 完成後做一些事
  Future start() async {
    // 1. 建立一個 HttpServer
    httpServer = await HttpServer.bind(InternetAddress.loopbackIPv4, port);
    // 2. 開始監聽客戶請求
    return httpServer.listen((request) {
      final path = request.uri.path;
      final handler = routes[path];
      if (handler != null) {
        handler(request);
      } else {
        // 給客戶返回一個 404
        request.response.statusCode = HttpStatus.notFound;
        request.response.close();
      }
    });
  }

  void _history(HttpRequest request) {
    // ...
  }

  void _echo(HttpRequest request) async {
    // ...
  }

  void close() async {
    var server = httpServer;
    httpServer = null;
    await server?.close();
  }
}
複製程式碼

在服務端框架裡,我們把支援的所有路徑都加到 routes 裡面,當收到客戶請求的時候,只需要直接從 routes 裡取出對應的處理函式,把請求分發給他就可以了。如果讀者對服務端程式設計沒有太大興趣或不太瞭解,這部分可以不用太關注。

將物件序列化為 JSON

為了把 Message 物件序列化為 JSON,這裡我們對 Message 做一些小修改:

class Message {
  final String msg;
  final int timestamp;

  Message(this.msg, this.timestamp);
  Message.create(String msg)
      : msg = msg, timestamp = DateTime.now().millisecondsSinceEpoch;

  Map<Stringdynamic> toJson() => {
    "msg""$msg",
    "timestamp": timestamp
  };

  @override
  String toString() {
    return 'Message{msg: $msg, timestamp: $timestamp}';
  }
}
複製程式碼

這裡我們加入一個 toJson 方法。下面是服務端的 _echo 方法:

class HttpEchoServer {
  static const GET = 'GET';
  static const POST = 'POST';

  const List<Message> messages = [];

  // ...

  _unsupportedMethod(HttpRequest request) {
    request.response.statusCode = HttpStatus.methodNotAllowed;
    request.response.close();
  }

  void _echo(HttpRequest request) async {
    if (request.method != POST) {
      _unsupportedMethod(request);
      return;
    }

    // 獲取從客戶端 POST 請求的 body,更多的知識,參考
    // https://www.dartlang.org/tutorials/dart-vm/httpserver
    String body = await request.transform(utf8.decoder).join();
    if (body != null) {
      var message = Message.create(body);
      messages.add(message);
      request.response.statusCode = HttpStatus.ok;
      // json 是 convert 包裡的物件,encode 方法還有第二個引數 toEncodable。當遇到物件不是
      // Dart 的內建物件時,如果提供這個引數,就會呼叫它對物件進行序列化;這裡我們沒有提供,
      // 所以 encode 方法會呼叫物件的 toJson 方法,這個方法在前面我們已經定義了
      var data = json.encode(message);
      // 把響應寫回給客戶端
      request.response.write(data);
    } else {
      request.response.statusCode = HttpStatus.badRequest;
    }
    request.response.close();
  }
}
複製程式碼

HTTP 客戶端

我們的 echo 伺服器使用了 dart:io 包裡面 HttpServer 來開發。對應的,我們也可以使用這個包裡的 HttpRequest 來執行 HTTP 請求,但這裡我們並不打算這麼做。第三方庫 http 提供了更簡單易用的介面。

首先把依賴新增到 pubspec 裡:

# pubspec.yaml
dependencies:
  # ...

  http: ^0.11.3+17
複製程式碼

客戶端實現如下:

import 'package:http/http.dart' as http;

class HttpEchoClient {
  final int port;
  final String host;

  HttpEchoClient(this.port): host = 'http://localhost:$port';

  Future<Message> send(String msg) async {
    // http.post 用來執行一個 HTTP POST 請求。
    // 它的 body 引數是一個 dynamic,可以支援不同型別的 body,這裡我們
    // 只是直接把客戶輸入的訊息發給服務端就可以了。由於 msg 是一個 String,
    // post 方法會自動設定 HTTP 的 Content-Type 為 text/plain
    final response = await http.post(host + '/echo', body: msg);
    if (response.statusCode == 200) {
      Map<Stringdynamic> msgJson = json.decode(response.body);
      // Dart 並不知道我們的 Message 長什麼樣,我們需要自己通過
      // Map<String, dynamic> 來構造物件
      var message = Message.fromJson(msgJson);
      return message;
    } else {
      return null;
    }
  }
}

class Message {
  final String msg;
  final int timestamp;

  Message.fromJson(Map<Stringdynamic> json)
    : msg = json['msg'], timestamp = json['timestamp'];

  // ...
}
複製程式碼

現在,讓我們把他們和上一節的 UI 結合到一起。首先啟動伺服器,然後建立客戶端:

HttpEchoServer _server;
HttpEchoClient _client;

class _MessageListState extends State<MessageList{
  final List<Message> messages = [];

  @override
  void initState() {
    super.initState();

    const port = 6060;
    _server = HttpEchoServer(port);
    // initState 不是一個 async 函式,這裡我們不能直接 await _server.start(),
    // future.then(...) 跟 await 是等價的
    _server.start().then((_) {
      // 等伺服器啟動後才建立客戶端
      _client = HttpEchoClient(port);
    });
  }

  // ...
}
複製程式碼
class MessageListScreen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ...

      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          final result = await Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => AddMessageScreen())
          );
          // 以下是修改了的地方
          if (_client == nullreturn;
          // 現在,我們不是直接構造一個 Message,而是通過 _client 把訊息
          // 傳送給伺服器
          var msg = await _client.send(result);
          if (msg != null) {
            messageListKey.currentState.addMessage(msg);
          } else {
            debugPrint('fail to send $result');
          }
        },
        // ...
      )
    );
  }
}
複製程式碼

大功告成,在做了這麼多工作以後,我們的應用現在是真正的 echo 客戶端了,雖然看起來跟之前沒什麼兩樣。接下來,我們就做一些跟之前不一樣的——把歷史記錄儲存下來。

歷史記錄儲存、恢復

獲取應用的儲存路徑

為了獲得應用的檔案儲存路徑,我們引入多一個庫:

# pubspec.yaml
dependencies:
  # ...

  path_provider: ^0.4.1
複製程式碼

通過它我們可以拿到應用的 file、cache 和 external storage 的路徑:


            
           

相關推薦

Flutter學習指南檔案儲存網路

Flutter學習指南 互動、手勢和動畫 UI佈局和控制元件 熟悉Dart語言 編寫第一個應用 開發環境搭建 本篇文章我們先學習 Flutter IO 相關的基礎知識,然後在 Flutter學習指南:互動、手勢和動畫 的基礎上,繼續開發一個 echo 客戶端。由於日常開發中 HTTP 比 socket

Flutter學習指南互動手勢動畫

Flutter學習指南 系列文章 UI佈局和控制元件 熟悉Dart語言 編寫第一個應用 開發環境搭建 在這一篇文章中,我們首先介紹手勢事件的處理和頁面跳轉的基礎知識,然後通過實現一個 echo 客戶端的前端頁面來加強學習;最後我們再學習內建的動畫 Widget 以及如何自定義動畫效果。 手勢處理

Flutter學習指南編寫第一個應用

這是個系列文章,後面還有很多篇,希望對大家能有幫助。 Flutter 是 Google 推出的移動端跨平臺開發框架,使用的程式語言是 Dart。從 React Native 到 Flutter,開發者對跨平臺解決方案的探索從未停止,畢竟,它可以讓我們節省移動端一半的人力。本篇文章中,我們就通過編寫一

Android原始碼學習筆記ContextActivityThreadActivity的生命週期

總結: ①在應用啟動的時候,首先會建立一個程序process,然後建立ActivityThread這個物件。 ②根據我們之前學習的Handler,可以知道,在ActivityThread的main方法中,會建立一個Looper和MessageQueue物件。 ③在建

Flutter學習指南封裝 API 外掛

Flutter學習指南6.檔案儲存和網路5.互動、手勢和動畫4.UI佈局和控制元件3.熟悉Dart語言2.編寫第一個應用1.開發環境搭建 本文是學習指南系列的第7篇文章,建議大家收藏起來,歡迎分享給他人。 本篇文章我們先一起學習 Flutter 外掛的使用,然後通過開發一個 toast 外掛來學習

python爬蟲學習5_cookie的獲取儲存使用

python爬蟲學習5_cookie的獲取、儲存和使用 Cookie,指某些網站為了辨別使用者身份、進行session跟蹤而儲存在使用者本地終端上的資料(通常經過加密)。 比如說有些網站需要登入後才能訪問某個頁面,在登入之前,你想抓取某個頁面內容,登陸前與登陸後是不同的,或者不允許的。

製作英文學習詞典。編寫程式製作英文學習詞典,詞典有3個基本功能新增查詢退出。程式讀取原始檔路徑下的txt格式詞典檔案,若沒有就建立一個(Python)

以下路徑可更換為你自己的路徑,本程式採用Python語言大致實現了serach()查詢函式和add()新增函式。細節有待完善,謝謝 def search(): w=input("請輸入要查詢的單詞:") fr=open("C:\\Users

衡量機器學習模型的三大指標準確率精度召回率。

美國 ext 另一個 IE blank 進行 style 監測 最好 連接來源:http://mp.weixin.qq.com/s/rXX0Edo8jU3kjUUfJhnyGw   傾向於使用準確率,是因為熟悉它的定義,而不是因為它是評估模型的最佳工具! 精度(查準率)和

python學習listtupledict

長度 刪除指定元素 但是 內容 指定 insert 指定元素 append 獲取 list:列表、數據類型可以不同 定義:(例) classmates = [‘Michael‘, ‘Bob‘, ‘Tracy‘] 訪問某一元素:(例) classmates[0]表示訪問第一個

JavaFutureCallableFutureTask原理解析(學習筆記)

Future表示一個任務的生命週期,並提供了方法來判斷是否已經完成或取消,以及獲取任務的結果和取消任務等。Future介面: public interface Future<V> { boolean cancel(boolean mayInterruptIfRunni

Qt 學習之路 2(42)QListWidgetQTreeWidget QTableWidget

上一章我們瞭解了 model/view 架構的基本概念。現在我們從最簡單的QListWidget、QTreeWidget和QTableWidget三個類開始瞭解最簡單的 model/view 的使用。這部分內容的確很難組織。首先,從最標準的 model/view 開始,往往會糾結於複雜的程式碼;但是

學習筆記】Hands-on ML with sklearn&tensorflow [TF] [1]模型的訓練儲存載入

本篇內容:一個簡單的預測模型的建立、訓練、儲存和載入。 匯入必要模組: import numpy as np import pandas as pd import tensorflow as tf import ssl #解決資料來源網站簽名認證失敗的問題 from sklearn.data

HTTP學習記錄頭資訊(請求響應)

學習資源主要為:@小坦克HTTP相關部落格 一、請求頭資訊(Request Header) 請求頭資訊包含比較多,如下: 1、Cache頭域   if-modified-Since   作用:把瀏覽器端快取頁面的最後修改時間傳送到伺服器去,伺服器會把這個時間與伺服器上的實際檔案的最後修改時間進行對比

Java的GUI學習十二(檔案的開啟儲存)

學習來自星雲:https://www.cnblogs.com/xingyunblog/p/3871611.html  程式碼: import java.awt.FileDialog; import java.awt.FlowLayout; import java.awt.Fram

OpenCV學習2載入顯示修改儲存影象

載入影象(用cv::imread) imread功能是載入影象檔案成為一個Mat物件,其中第一個引數表示影象檔名稱 第二個引數,表示載入的影象是什麼型別,支援常見的三個引數值 IMREAD_UNCHANGED (<0) 表示載入原圖,不做任何改變

多執行緒學習(4)三種實現Java多執行緒的方法ThreadCallableRunable 的比較與區別

2018年10月03日 目錄 前言 前言 JVM允許應用程式併發執行多執行緒:最常用的是兩個方法:(1)基礎Thread類,重寫run()方法;(2)或實現Runnable 介面,實現介面的run()方法;(3)另外一種方法是:實現callable 介面

tensorflow教程變數建立初始化儲存載入

變數儲存到檔案 import tensorflow as tf import numpy as np # Create two variables. x_data = np.float32([1,2,3,4,5,6,7,8,9,0]) weights = tf.Variable(tf.random_norma

Python學習筆記檔案操作類基礎派生與繼承入門

#檔案操作open、close     開啟一個檔案供讀寫     file = open(file, mode=xx)          用完之後一定要記得關閉  

OpenCV 學習筆記 02 處理檔案攝像頭圖形使用者介面

  在處理檔案前需要引入OpenCV庫,同時也引入unmpy庫 import cv2 import numpy as np 1 基本的讀寫操作 1.1 影象檔案的讀寫操作 1.1.1 影象檔案的讀取操作 opencv 的 imread() 函式和 imwrite() 函式支援

OpenCV 學習筆記 02 使用opencv處理影象 OpenCV 學習筆記 02 處理檔案攝像頭圖形使用者介面

1 不同色彩空間的轉換 opencv 中有數百種關於不同色彩空間的轉換方法,但常用的有三種色彩空間:灰度、BRG、HSV(Hue-Saturation-Value) 灰度 - 灰度色彩空間是通過去除彩色資訊來將其轉換成灰階,灰度色彩空間對中間處理特別有效,比如人臉檢測 BGR - 藍-綠-紅 彩