本記事は [個人開発 Advent Calendar 2019]の14日目の記事です。
Python×Kivyというフレームワークを使用して、Windowsアプリを作成しています。
背景
- 紙本を自炊してPDFにした。スッキリ!
- さぁ、iPadで読もう
- 子供にiPad取られた・・・
- Surface買った!さぁ、Surfaceで読もう
- iPadと違ってWindows用の良いアプリ無いな
- Kinoppy(紀伊国屋書店アプリ)良いね!
- これ今勉強中のKivyで作れるかな。
Kinoppy(紀伊国屋書店アプリ)はとても良いアプリで、おすすめです。
アプリ概要
本棚とPDFビューワ機能を持っています。
開発環境
AnacondaNavigator
VSCode
Python3.7
Kivy1.11
使用ライブラリ等
他人に頼りすぎて沢山ありますが、メインで使用しているものは以下です。
PDFレンダリング:pdf2image (https://github.com/Belval/pdf2image)
設定ファイル:ruamel.yaml
アイコンフォント:iconfonts (https://github.com/kivy-garden/garden.iconfonts)
大変お世話になったサイト
Kinoppy (https://k-kinoppy.jp/)
Kivy (https://kivy.org/)
stackoverflow (https://stackoverflow.com/)
荒川光線ブログ 本館 (https://koh-sen.jp/)
そして
Qiita:Kivy (https://qiita.com/tags/kivy)
Todo
結構奥が深くて志半ばですが、以下を目指してます。遠い。。
- ★済★ PDF読込(ローカルPC)
- ☆未☆ PDF読込(DropBox)
- ★済★ 本棚管理(本棚追加&削除)
- ★済★ D&Dで本の移動(本棚内)
- ☆未☆ D&Dで本の移動(別の本棚)
- ★済★ ビューワ機能(1or2ページ表示、右or左開き指定)
- ☆未☆ ビューワ機能(自動目次作成)
- ☆未☆ ビューワ機能(検索)
ソースコード他
以下のKivyのソースコードを2つ抜粋して紹介します。
- Scatterによるサムネイルのドラッグアンドドロップ
- Sleiderの右→左の実現
- (おまけ)本棚の作り方
①Scatterによるサムネイルのドラッグアンドドロップ
サムネイル(本棚に表示される画像)を何秒か掴んだらドラッグアンドドロップします、という事で、画像枠に色を付けます。もしドラッグアンドドロップに入る前に離したら、普通のクリックと見なしてビューワに遷移します。
<DraggableThumbnail@Scatter>: | |
image_name: '' | |
image_path: '' | |
image: image_id | |
shelf_row: None | |
shelf_col: None | |
do_rotation: False | |
do_scale: False | |
auto_bring_to_front: True | |
do_collide_after_children: True | |
background_color: 0,0,0,0 | |
background_normal: '' | |
Image: | |
id: image_id | |
source: root.image_path | |
allow_stretch:True | |
keep_ratio: True | |
canvas: | |
Color: | |
rgb: (1, 1, 1) | |
Rectangle: | |
texture: self.texture | |
pos: self.pos | |
size: self.size |
class DraggableThumbnail(Scatter): | |
image_name = StringProperty(None) | |
image_path = StringProperty(None) | |
shelf_row = NumericProperty(None) | |
shelf_col = NumericProperty(None) | |
state = OptionProperty('released',options=['grabed','released']) | |
img_touched = StringProperty(None) | |
t = NumericProperty(0) | |
def __init__(self,**kwargs): | |
super(DraggableThumbnail,self).__init__(**kwargs) | |
Clock.schedule_once(self.drag_after_init) | |
# self.bind(size=self.update_canvas) | |
def drag_after_init(self, dt): | |
# print('DraggableThumbnail drag_after_init 1 : %s'% self.image.size) | |
self.size_hint = None,None | |
self.size = self.image.size | |
# print('DraggableThumbnail drag_after_init 2 : %s'% self.size) | |
with self.canvas: | |
self.color = Color(rgba = (1,1,1,0)) | |
self.frame = Line(rectangle=(0,0,self.image.width,self.image.height)) | |
def on_state(self,*args): | |
if self.state == 'grabed': | |
self.color.rgba = (0.5,0.5,0.5,1) | |
elif self.state == 'released': | |
self.color.rgba = (1,1,1,0) | |
def time_count(self,*args): | |
self.t += 0.1 | |
def on_t(self,*args): | |
if self.t >=0.5: | |
Clock.unschedule(self.time_count) | |
self.t = 0 | |
self.state = 'grabed' | |
self.img_touched = '' | |
def on_touch_down(self, touch): | |
self.on_state(touch) | |
if self.collide_point(*touch.pos): | |
Clock.schedule_interval(self.time_count, 0.1) | |
self.img_touched = self.image_name | |
print('DraggableThumbnail on_touch_down: %s' % self.img_touched) | |
# if the touch collides with our widget, let's grab it | |
touch.grab(self) | |
# and accept the touch. | |
return True | |
def on_touch_up(self, touch): | |
Clock.unschedule(self.time_count) | |
self.t = 0 | |
# you just need to check if it's a grabbed touch event | |
if touch.grab_current is self: | |
print('DraggableThumbnail on_touch_up : %s' % self.image_name) | |
if self.img_touched == self.image_name: | |
self.img_touched = '' | |
if self.state == 'released': | |
app= App.get_running_app() | |
app.root.chgdisp_viewer(self.image_name) | |
else: | |
print('DraggableThumbnail set_thumbnail') | |
app= App.get_running_app() | |
app.root.Library.lineup_thumbnail(self,touch) | |
# don't forget to ungrab ourself, or you might have side effects | |
touch.ungrab(self) | |
self.state = 'released' | |
# and accept the last up | |
return True | |
def on_touch_move(self, touch): | |
Clock.unschedule(self.time_count) | |
self.t = 0 | |
if self.state == 'grabed': | |
# print('DraggableThumbnail on_touch_move : %s' % self.image_name) | |
self.pos = touch.x - self.image.width / 2, touch.y - self.image.height / 2 | |
return True | |
else: | |
self.img_touched = '' | |
②Sleiderの右→左の実現
海外のPDFViewerで不便に思うのが縦書き(右→左)に対応していない事です。Kivyのスライダーは左→右(下→上)は実現できますが、右から左の設定がありません。あとスライドしている時にページ数も欲しかったのでこちらは部品化しました。ソースはGithubにアップしてあります。こうやって標準部品をカスタマイズするのかーと勉強になりました。もし良ければソースご覧ください。
③(おまけ)本棚の作り方
ちょうどいい本棚画像が無かったので、作りました。この画像の事です。
作り方はこちらのページにて、プログラムとは関係ありませんし、どこにも需要が無さそうですが。

コメント