前回のコードはダメダメだった、ZIP 展開が遅すぎる!
やはり Python の zipfile は使い物にならないのだろうか。
いやまて、ZIP の展開は非同期にすればよくね?
g_idle_add を使って一枚目以降はバックグラウンド読み込みにすればイケるかも。
そういえば yield ってどう使うんだっけ。
clipoli では for 文で使っていたけど今回の場合はそれじゃダメだし。
class Clipoli(Gtk.Menu): def __init__(self, confpath): # etc... def read_lines(self, filename): """ ini file Read Function """ f = Gio.file_new_for_path(filename) fstream = f.read(None) dstream = Gio.DataInputStream.new(fstream) while 1: line, length = dstream.read_line_utf8(None) if line == None: break if line == "": continue if line[0] == ";": continue if len(line) > 2 and line[0] =="[" and line[-1] == "]": section = line[1:-1] elif section == "": pass # Nothing elif "=" in line: pos = line.index("=") yield section, line[:pos], line[pos+1:] fstream.close(None) def load_inifile(self): """ Inifile Read and Create Menu """ for section, key, value in self.read_lines(self.confpath): # etc...
ってジェネレーターを作って next() を呼んだら例外だ。
って上記は Python2 だし、Python3 では変わっているのかな?
Pythonのジェネレータ、コルーチン、ネイティブコルーチン、そしてasync/await | プログラミング | POSTD
next はメソッドではなく関数になっているのね。
それさえ解ればなんとかなりそうだ。
#!/usr/bin/env python3 import sys, zipfile, gi gi.require_version('Gtk', '3.0') gi.require_version('Clutter', '1.0') gi.require_version('GtkClutter', '1.0') from gi.repository import Gtk, Gio, GLib, Gdk, GdkPixbuf, Clutter, GtkClutter, Cogl PATH = "gf(kari).cbz"; class ComipoliWindow(Gtk.ApplicationWindow): def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app) # var self.num = 0 self.datas = [] self.is_fullscreen = False # Dark Theme settings = Gtk.Settings.get_default() settings.props.gtk_application_prefer_dark_theme = True # DnD self.drag_dest_set( Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, [Gtk.TargetEntry.new("text/uri-list", 0, 0)], Gdk.DragAction.MOVE ) # Clutter embed = GtkClutter.Embed() self.add(embed) self.stage = embed.get_stage() # self.actor1 = Clutter.Actor() self.stage.add_child(self.actor1) self.image1 = Clutter.Image() self.actor1.set_content(self.image1) # signal self.stage.connect("allocation-changed", self.on_stage_allocation_changed) # self.show_all() def set_uri(self, uri): if uri == None: return # Check CBZ self.zip = Gio.file_new_for_uri(uri) info = self.zip.query_info("standard::content-type", Gio.FileQueryInfoFlags.NONE) if info.get_content_type() == "application/x-cbz": self.gen = self.iter_zip() GLib.idle_add(self.iter_idle) def iter_idle(self): try: p = next(self.gen) #print(p) # check self.datas.append(p) if len(self.datas) == 1: self.set_pixbuf(0) return True except Exception as e: return False def iter_zip(self): # unzip with zipfile.ZipFile(self.zip.get_path()) as o: l = o.namelist() l.sort() self.datas.clear() for name in l: try: data = o.read(name) stream = Gio.MemoryInputStream.new_from_data(data) p = GdkPixbuf.Pixbuf.new_from_stream(stream) yield p except Exception as e: pass def set_pixbuf(self, num): pixbuf = self.datas[num] self.image1.set_data( pixbuf.get_pixels(), Cogl.PixelFormat.RGB_888, pixbuf.get_width(), pixbuf.get_height(), pixbuf.get_rowstride() ) self.num = num def change_pixbuf(self, bool_next): """ TODO: Spread display """ if bool_next: if len(self.datas) > self.num + 1: self.set_pixbuf(self.num + 1) else: if self.num > 0: self.set_pixbuf(self.num - 1) def on_stage_allocation_changed(self, actor, box, flags): """ TODO: Aspect ratio """ self.actor1.set_size(box.x2 , box.y2) def do_key_press_event(self, event): """ eog like Key Bind """ if event.keyval == Gdk.KEY_Down: self.change_pixbuf(True) elif event.keyval == Gdk.KEY_Right: self.change_pixbuf(True) elif event.keyval == Gdk.KEY_space: self.change_pixbuf(True) elif event.keyval == Gdk.KEY_Up: self.change_pixbuf(False) elif event.keyval == Gdk.KEY_Left: self.change_pixbuf(False) elif event.keyval == Gdk.KEY_BackSpace: self.change_pixbuf(False) elif event.keyval == Gdk.KEY_F11: if self.is_fullscreen: self.unfullscreen() self.is_fullscreen = False else: self.fullscreen() self.is_fullscreen = True elif event.keyval == Gdk.KEY_Escape: if self.is_fullscreen: self.unfullscreen() self.is_fullscreen = False else: self.close() def do_drag_data_received(self, drag_context, x, y, data, info, time): drop = data.get_uris()[0] self.set_uri(drop) self.present() class ComipoliApp(Gtk.Application): def __init__(self): GLib.set_prgname("Comipoli"); Gtk.Application.__init__( self, application_id="apps.sasakima.comipoli", flags=Gio.ApplicationFlags.FLAGS_NONE ) def do_activate(self): ComipoliWindow(self) GtkClutter.init(); app = ComipoliApp() app.run(sys.argv)
GLib.idle_add でムリムリに非同期にして。
yield を利用して self.datas 配列になるべく早く GdkPixbuf を詰め込む。
next() は yield が終ると例外なのを利用して idle 監視を止める。
とやっています、物凄く解り辛いと思うけど。
しかし class 内で使うとガベージコレクション回避で self だらけに。
試す、よし実用で問題ない程度には使えるようになった。
実際は絶望的に遅いんですけどね。
バックグラウンド処理が追いつかない速度でマンガを読む人はいない、かも。
yield ってこんなに便利だったのか、何を今更だが。