pythonでGUIアプリ作成 ~画像表示・回転~

やりたいこと

pythonGUIアプリを作る

調べたこと

kivyのドキュメント pyky.github.io

作ったアプリ

  • ファイルパスを読み込む(FileChooser)
  • 画像を表示する(Image)
  • opencvで回転させる

コード(python部分)

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics.texture import Texture
from kivy.properties import ObjectProperty
from kivy.factory import Factory
from kivy.uix.popup import Popup
from kivy.core.window import Window

import os
import cv2

class LoadDialog(FloatLayout):
    load = ObjectProperty(None)
    cancel = ObjectProperty(None)

class Root(BoxLayout):
    # loadfile = ObjectProperty(None)
    image_texture = ObjectProperty(None)
    imgData = None
    
    def dismiss_popup(self):
        self._popup.dismiss()

    def show_load(self):
        content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
        self._popup = Popup(title="Load file", content=content,
                            size_hint=(0.9, 0.9))
        self._popup.open()

    def load(self, path, filename):
        imgFilepath = os.path.join(path, filename[0])
        # print(self.imgFliepath)
        # with open(os.path.join(path, filename[0])) as stream:
        #     self.text_input.text = stream.read()
        self.imgData = cv2.imread(imgFilepath)
        self.img_show()
        self.dismiss_popup()

    def img_show(self):
        # filename = "../guiapp/IMG_0162.JPG"
        img = self.imgData
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.flip(img, 0)
        texture = Texture.create(size=(img.shape[1], img.shape[0]))
        texture.blit_buffer(img.tobytes())
        self.image_texture = texture
    
    def img_show(self):
        img = self.imgData
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.flip(img, 0)
        texture = Texture.create(size=(img.shape[1], img.shape[0]))
        texture.blit_buffer(img.tobytes())
        self.image_texture = texture

    def rotation_clockwise(self):
        self.imgData = cv2.rotate(self.imgData, cv2.ROTATE_90_CLOCKWISE)
        self.img_show()

    def rotation_affine(self, value):
        img = self.imgData
        center = (img.shape[1] / 2,  img.shape[0] / 2)
        angle = -float(value)
        scale = 1.0
        print(center, angle, scale)
        trans = cv2.getRotationMatrix2D(center, angle, scale)
        img = cv2.warpAffine(img, trans, (img.shape[1], img.shape[0]))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.flip(img, 0)
        texture = Texture.create(size=(img.shape[1], img.shape[0]))
        texture.blit_buffer(img.tobytes())
        self.image_texture = texture

class mainApp(App):
    def build(self):
        return Root()

Factory.register('Root', cls=Root)
Factory.register('LoadDialog', cls=LoadDialog)
Window.size = (600, 600)

ap = mainApp()
ap.run()

コード(kv部分)

<Root>:
    BoxLayout:
        orientation: "vertical"

        ScrollView:
            bar_width: 10
            Image:
                id: im
                texture: root.image_texture
                size_hint: (1, 1)
        BoxLayout:
            orientation: "horizontal"
            size_hint: (1, 0.25)
            Slider:
                id: slider1
                size_hint: (0.75, 1)
                min: 0
                max: 90
                step: 0.1
                on_value: root.rotation_affine(self.value)
            TextInput:
                id: tx1
                size_hint: (0.25, 1)
                text: str(slider1.value)

        BoxLayout:
            orientation: "horizontal"
            size_hint: (1, 0.25)
            Slider:
                id: slider2
                size_hint: (0.75, 1)
                min: 0
                max: 100
                step: 0.1
        
            TextInput:
                id: tx2
                size_hint: (0.25, 1)
                text: str(slider2.value)
            
        ActionBar:
            background_color: 1, 0, 0, 1
            ActionView:
                use_separator: True
                ActionPrevious:
                    text: "new"
                    with_previous: False
                ActionGroup:
                    text: "menu"
                    ActionButton:
                        text: "open"
                        on_release: root.show_load()
                    ActionButton:
                        text: "clockwise"
                        on_release: root.rotation_clockwise()
                    ActionButton:
                        text: "button3"

<LoadDialog>:
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: "vertical"
        # FileChooserListView:
        #     id: filechooser

        FileChooser:
            id: filechooser
            rootpath: "../"
            path: "/mnt/c/workspace/"
            filters: ['*.jpg', '*.png','*.JPG', '*.PNG']
            FileChooserListLayout
            FileChooserIconLayout
        BoxLayout:
            size_hint_y: None
            height: 30
            Button:
                text: "Cancel"
                on_release: root.cancel()
            Button:
                text: "Load"
                on_release: root.load(filechooser.path, filechooser.selection)
            BoxLayout:
                orientation: "vertical"
                Button:
                    text: 'Icon View'
                    on_press: filechooser.view_mode = 'icon'
                Button:
                    text: 'List View'
                    on_press: filechooser.view_mode = 'list'

結果

起動時

f:id:techsho:20201004182917p:plain
起動時

ファイル選択

f:id:techsho:20201004183009p:plain
ファイル選択

スライダーでの回転量調整

f:id:techsho:20201004183035p:plain
スライダー

購入検討中の本