前回のコードはダメダメだった、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...
Pythonのイテレータとジェネレータ – Qiita
ってジェネレーターを作って 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 ってこんなに便利だったのか、何を今更だが。