オタクの落書き(改)

オタクがぶつぶつ言うだけ

オタク工作奮闘記その2

前回はpygameの音声ファイル再生をちょちょいと試した内容を書いたが、今回はGUIです。 予め用意しておいたものがこちらです。

f:id:someone34o2:20190401214441p:plain

描画・ボタンとしての実装はクソ簡単だったんだけど、無駄にドラッグできるように凝ろうとしたせいでめちゃくちゃ(職場で)時間使った。

f:id:someone34o2:20190401214443p:plain

うまいことドラッグ先に思ったように並ばなくてログを出してやっとこさ期待通りの動作になった。

実装・・・乗せる必要ある?

import pygame
import pygame.display as disp


WINDOW_WIDTH = 600
WINDOW_HEIGHT = 500

SIDE_MARGIN = 50
CIRCLE_INTERVAL = 15
CIRCLE_RADIUS = 20
CIRCLE_Y = 100

NAMES = ["CHI", "RIK", "KAN", "DIA", "YOU", "YOS", "HAN", "MAR", "RUB"]

class IconSlot:
    def __init__(self, icon, center):
        self.icon = icon
        self.centerx = center # static


class DraggableIcon:
    def __init__(self, path, id):
        self.pic = pygame.image.load(path)
        self.id = id
        self.rect = self.pic.get_rect()

    def _set_icon(self, disp):
        disp.blit(self.pic, self.rect)
    

def get_x_from_pos(index):
    return SIDE_MARGIN + CIRCLE_RADIUS + index * (CIRCLE_INTERVAL + 2 * CIRCLE_RADIUS)


def paint_all(d, above=8):
    d.fill((0,255,255))
    if above < 0:
        above = 0
    elif above > 8:
        above = 8
    for i in range(0, 9):
        if(i == above):
            continue
        d_icons[i]._set_icon(d)
    d_icons[above]._set_icon(d)


def get_dst(src, now):
    delta = now - (SIDE_MARGIN + CIRCLE_RADIUS + src * (CIRCLE_INTERVAL + 2 * CIRCLE_RADIUS))
    delta_pos = int(delta / (CIRCLE_INTERVAL + 2 * CIRCLE_RADIUS))
    ret = src + delta_pos
    if ret > 8:
        ret = 8
    elif ret < 0:
        ret = 0
    return ret


def move_icons(target, dest):
    if target == dest:
        slots[target].icon.rect.centerx = slots[target].centerx
        return
    
    temp = slots[target].icon

    i = target
    if target > dest: # right to left
        while i > dest:
            slots[i].icon = slots[i-1].icon
            slots[i].icon.rect.centerx = slots[i].centerx
            print("move icon {} from {} to {}".format(slots[i-1].icon.id, i-1, i))
            i -= 1
    elif target < dest: # left to right
        while i < dest:
            slots[i].icon = slots[i+1].icon
            slots[i].icon.rect.centerx = slots[i].centerx
            print("move icon {} from {} to {}".format(slots[i+1].icon.id, i+1, i))
            i += 1

    slots[dest].icon = temp
    slots[dest].icon.rect.centerx = slots[dest].centerx
    #print("move icon {} from {} to {}".format(temp.id, target, dest))

    out = "["
    for slot in slots:
        out += "{}, ".format(NAMES[slot.icon.id])
    out = out[:len(out)-2]
    out += "]"
    print(out)


d_icons = []
slots = [] # position to be put d_icon


def main():
    last_x = 0
    last_y = 0
    flag_clicked = False
    dragging = -1 # index of slots

    pygame.init()
    disp.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
    d = disp.get_surface()
    d.fill((0,255,255))

    for i in range(0, 9):
        d_icons.append(DraggableIcon("icon\\" + str(i) + ".png", i))
        slots.append(IconSlot(d_icons[i], get_x_from_pos(i)))
        d_icons[i].rect.center = (slots[i].centerx, CIRCLE_Y)

    done = False

    paint_all(d)
    disp.flip()

    while done == False:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            elif event.type == pygame.MOUSEBUTTONDOWN:
                for i in range(0, 9):
                    if slots[i].icon.rect.collidepoint(event.pos):
                        if not flag_clicked:
                            dragging = i
                            print("icon {} clicked.".format(slots[dragging].icon.id))
                            last_x, last_y = event.pos
                            flag_clicked = True
                            break
            elif event.type == pygame.MOUSEMOTION:
                if flag_clicked == True:
                    now_x, now_y = event.pos
                    slots[i].icon.rect.move_ip(now_x - last_x, 0)
                    last_x = now_x
                    last_y = now_y
                    break
            elif event.type == pygame.MOUSEBUTTONUP:
                if flag_clicked:
                    target = slots[dragging].icon
                    now_x, now_y = target.rect.center
                    print("icon {} x = {}".format(slots[dragging].icon.id, now_x))
                    dst = get_dst(dragging, now_x)
                    print("target = {}, dst = {}".format(slots[dragging].icon.id, dst))
                    move_icons(dragging, dst)

                    out = "["
                    for icon in d_icons:
                        out += "{:>3}, ".format(str(icon.rect.centerx))
                    out = out[:len(out)-2]
                    out += "]"
                    print(out)

                    dragging = -1
                    last_x = 0
                    last_y = 0
                    flag_clicked = False
                    print("release")
                    break

        paint_all(d, slots[dragging].icon.id)
        disp.update()

if __name__ == "__main__":
    main()

将来自分で見直したときにアホな実装してるな~~~って頭を抱えるために残してみよう。 IconSlotは固定座標の9つ、DraggableIconがスロットと結びつきながら各々の画像とidを保持して座標を変えながら描画される。 もっといい構造にならんのかなぁ、場数踏むしかないんか。

ここまでは実装済みの内容であるわけだが、これを前回の音楽再生と組み合わせていい感じのものにしたい。 もうラストスパートだし余裕でしょ(フラグ)。