18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Python×Kivy de Kinoppy(紀伊国屋書店アプリ)を真似してみた

Last updated at Posted at 2019-12-13

本記事は 個人開発 Advent Calendar 2019 14日目の記事です。
そしてQiita初投稿です。はじまして。

Python×Kivyというフレームワークを使用して、Windowsアプリを作成しています。

###背景

  1. 紙本を自炊してPDFにした。スッキリ!
  • さぁ、iPadで読もう
  • 子供にiPad取られた・・・
  • Surface買った!さぁ、Surfaceで読もう
  • iPadと違ってWindows用の良いアプリ無いな
  • Kinoppy(紀伊国屋書店アプリ)良いね!
  • これ今勉強中のKivyで作れるかな。

Kinoppy(紀伊国屋書店アプリ)はとても良いアプリで、おすすめです。

###アプリ概要
本棚とPDFビューワ機能を持っています。
pdfapp2-min.gif

###開発環境
AnacondaNavigator
VSCode
Python3.7
Kivy1.11

###使用ライブラリ等
他人に頼りすぎて沢山ありますが、メインで使用しているものは以下です。
PDFレンダリング:pdf2image
設定ファイル:ruamel.yaml
アイコンフォント:iconfonts

###大変お世話になったサイト
Kinoppy
Kivy
stackoverflow
荒川光線ブログ 本館
そして
Qiita:Kivy

###Todo
結構奥が深くて志半ばですが、以下を目指してます。遠い。。

  • PDF読込(ローカルPC)
  • PDF読込(DropBox)
  • 本棚管理(本棚追加&削除)
  • D&Dで本の移動(本棚内)
  • D&Dで本の移動(別の本棚)
  • ビューワ機能(1or2ページ表示、右or左開き指定)
  • ビューワ機能(自動目次作成)
  • ビューワ機能(検索)

###ソースコード他
以下のKivyのソースコードを2つ抜粋して紹介します。

  1. Scatterによるサムネイルのドラッグアンドドロップ
  • Sleiderの右→左の実現
  • (おまけ)本棚の作り方

####1.Scatterによるサムネイルのドラッグアンドドロップ
サムネイル(本棚に表示される画像)を何秒か掴んだらドラッグアンドドロップします、という事で、画像枠に色を付けます。もしドラッグアンドドロップに入る前に離したら、普通のクリックと見なしてビューワに遷移します。

Library.kv

<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
Library.py

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しないとサイズ変更出来ない
        Clock.schedule_once(self.drag_after_init)

    def drag_after_init(self, dt):
        self.size_hint = None,None
        self.size = self.image.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

            touch.grab(self)
            return True

    def on_touch_up(self, touch):
        Clock.unschedule(self.time_count)
        self.t = 0

        if touch.grab_current is self:
            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:
                # ドロップされたのでサムネイル整列
                app= App.get_running_app()
                app.root.Library.lineup_thumbnail(self,touch)
            touch.ungrab(self)

        self.state = 'released'
        return True

    def on_touch_move(self, touch):
        Clock.unschedule(self.time_count)
        self.t = 0
        if self.state == 'grabed':
            self.pos = touch.x - self.image.width / 2, touch.y - self.image.height / 2
            return True
        else:
            self.img_touched = ''


####2.Sleiderの右→左の実現
海外のPDFViewerで不便に思うのが縦書き(右→左)に対応していない事です。Kivyのスライダーは左→右(下→上)は実現できますが、右から左の設定がありません。あとスライドしている時にページ数も欲しかったのでこちらは部品化しました。ソースはGithubにアップしてあります。こうやって標準部品をカスタマイズするのかーと勉強になりました。もし良ければソースご覧ください。

Example.gif

####3.(おまけ)本棚の作り方
ちょうどいい本棚画像が無かったので、作りました。この画像の事です。
shelf2.png

作り方はこちらの自分のブログにて、プログラムとは関係ありませんし、どこにも需要が無さそうですが。

###最後に
はじめてのQiita投稿とはじめてのAdvent Calendar、12月1日から自分の担当まで時間が過ぎるのが早いこと!!毎日「あーもうすぐ順番が…」のドキドキが、とても良いモチベーションになりました。このイベントが無かったらここまで仕上がってなかったです。参加出来て良かったです。お読み頂きありがとうございました。

18
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?