前回はpygameの音声ファイル再生をちょちょいと試した内容を書いたが、今回はGUIです。 予め用意しておいたものがこちらです。
描画・ボタンとしての実装はクソ簡単だったんだけど、無駄にドラッグできるように凝ろうとしたせいでめちゃくちゃ(職場で)時間使った。
うまいことドラッグ先に思ったように並ばなくてログを出してやっとこさ期待通りの動作になった。
実装・・・乗せる必要ある?
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を保持して座標を変えながら描画される。 もっといい構造にならんのかなぁ、場数踏むしかないんか。
ここまでは実装済みの内容であるわけだが、これを前回の音楽再生と組み合わせていい感じのものにしたい。 もうラストスパートだし余裕でしょ(フラグ)。