3
1

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 1 year has passed since last update.

Pythonista3Advent Calendar 2022

Day 4

Pythonista3 のKeyboard で、Pythonista3 のコーディングを楽にする。人生を豊かにする。

Last updated at Posted at 2022-12-03

この記事は、Pythonista3 Advent Calendar 2022 の04日目の記事です。

一方的な偏った目線で、Pythonista3 を紹介していきます。

ほぼ毎日iPhone(Pythonista3)で、コーディングをしている者です。よろしくお願いします。

以下、私の2022年12月時点の環境です。

--- SYSTEM INFORMATION ---
**System Information**

* Pythonista 3.3 (330025), Default interpreter 3.6.1
* iOS 16.0.2, model iPhone12,1, resolution (portrait) 828.0 x 1792.0 @ 2.0

他の環境(iPad や端末の種類、iOS のバージョン違い)では、意図としない挙動(エラーになる)なる場合もあります。ご了承ください。

ちなみに、model iPhone12,1 は、iPhone11 です。

先にこっちを紹介しろ😡

img221116_193630.gif

img221116_193950.png

img221116_193958.png

人生が豊かになるキーボードぉー☺️

img221116_194037.png

今回は私がPythonista3 にて、高頻度使用しているスクリプトを紹介します。

初回で紹介せず、本当に申し訳ない気持ちでいっぱいです。本当にそれくらい便利になるので、紹介するのが楽しみです。

みんなで人生豊かになりましょう!

PyKeys でカスタムして、人生豊かに

img221116_200131.gif

GIF では、何を押しているか分かりづらいですね。。。

自分で設定した要素を押し文字を入力し、コピーしたものを「📝」を押して、ペーストしています。

img221116_200502.png

設定する要素は、自分で選べます。ペーストも、わざわざ画面長押ししてpopup の「Paste」を選択せずに、ワンアクションで貼り付けができます。

keyboard モジュール

keyboard — Utilities for the Pythonista Keyboard — Python 3.6.1 documentation

keyboardモジュールは、Pythonistaのカスタムキーボード("PyKeys")を拡張するための様々な関数を提供します。

Pythonista3 にカスタムできるキーボードが用意されており、keyboard モジュールを使って色々とできるわけですね。

Documentation にはui.View と書かれていて、難しそうな印象がありますね。

大丈夫です。安心してください☺️

もう、Pythonista3 に最低限のコードは用意されています。

サンプルコードExamples/Keyboard/Special Characters.py

以前2日目の記事にて、Examples フォルダを紹介しました。Keyboard 内にたくさんのサンプルコードがあります。

Special Characters.py を借用 & 一部変更をし実装します。

コード全貌

基本的に変更点は

  • characters
    • 使いたい文字列
  • def layout
    • キーのサイズを気持ち小さめに
  • clipboard
    • (?) ヘルプの部分に、「📝」ペースト

程度で、残りはほぼサンプルコードを使っています。

私の環境では、Reformat Code を使うと、最終行に) が実現してしまいます。細かく追ってないのですが。。。

import keyboard
import ui
import clipboard

# You can modify or extend this list to change the characters that are shown in the keyboard extension:
characters = [
  '📝', '#', '-', '*', '/', '%', '`', '<', '>', '[', ']', '1', '2', '3', '4',
  '5', '6', '7', '8', '9', '0'
]


class CharsView(ui.View):
  def __init__(self, *args, **kwargs):
    super().__init__(self, *args, **kwargs)
    self.background_color = '#333'
    self.scroll_view = ui.ScrollView(frame=self.bounds, flex='WH')
    self.scroll_view.paging_enabled = True
    self.scroll_view.shows_horizontal_scroll_indicator = False
    self.add_subview(self.scroll_view)
    self.buttons = []
    for c in characters:
      button = ui.Button(title=c)
      button.font = ('<System>', 16)
      button.background_color = (1, 1, 1, 0.1)
      button.tint_color = 'white'
      button.corner_radius = 4
      button.action = self.button_action
      self.scroll_view.add_subview(button)
      self.buttons.append(button)

  def layout(self):
    rows = max(1, int(self.bounds.h / 36))
    bw = 32  # 44
    h = (self.bounds.h / rows) - 4
    x, y = 2.5, 2
    for button in self.buttons:
      button.frame = (x, y, bw, h)
      y += h + 4
      if y + h > self.bounds.h:
        y = 2
        x += bw + 4
    self.scroll_view.content_size = (
      (len(self.buttons) / rows + 1) * (bw + 4) + 40, 0)

  def button_action(self, sender):
    if sender.title == '📝':
      text = clipboard.get()
    else:
      text = sender.title

    if keyboard.is_keyboard():
      keyboard.play_input_click()
      keyboard.insert_text(text)
    else:
      print('Keyboard input:', text)


def main():
  v = CharsView(frame=(0, 0, 320, 40))
  if keyboard.is_keyboard():
    keyboard.set_view(v, 'current')
  else:
    # For debugging in the main app:
    v.name = 'Keyboard Preview'
    v.present('sheet')


if __name__ == '__main__':
  main()

おこだわりを聞いておくれー

Extended Keyboard で恩恵を受けつつ、Python とMarkdown 双方がいい感じに書けるような配置にしてみています。

Extended Keyboard 上で、長押しすれば出てくるけども、使用頻度が高いキーに簡単にアクセスできるように。

いま、こうしてAdvent Calendar をPythonista3 で書けているのも、PyKeys あってこそですね☺️

Key Python Markdown
# コメント ヘッダー
- 減算・マイナス 箇条書きリスト
* 乗算 箇条書きリスト・強調
/ 除算・ファイルパス区切り -
バッククォート - code
> - 引用
[] 配列 リンク

これを派生させGLSL やJavaScript 用のキーボード設定なども作っています。

設定で「フルアクセスを許可」

ペースト機能をPyKeys で使用には、許可設定が必要です。

  1. 設定App(Pythonista3 App 内のsetting ではありません)を開く
  2. (下にずっーとスクロールしていった)Pythonista3 アイコンをタップ
  3. 「キーボード」 をタップ
  4. 「PyKeys」と「フルアクセスを許可」をアクティブ

img221117_131257.png

img221117_131305.png

「PyKeysに設定しても、ペーストができない😭」って方は、設定を確認してみてください。

ファイル新規作成で人生豊かに

ファイル名はどうでもいいから「とりあえず実行して、挙動や結果を知りたい」って場面ありませんか?

Untitled.py でもいいけど(Pythonista3 のデフォルトファイル名)_1.py, _2.py... と続いていくのも気持ちがいいものではありません。また、見返してみると、Untitled 祭りで何がなんだかわからなくなりますし(矛盾)。

img221117_162650.gif

(またもや登場の)Editor Action を使って、いい感じに新規ファイル作成するスクリプトです。

  1. 新規ファイル作成Editor Action をタップ
  2. コンソール画面上に、作成案内のログが出現
  3. キーボードで、整数値入力
  4. return
  5. YYMMDD_HHmm.py として新規作成
  6. 新規タブで開かれる

img221117_163435.png

新規ファイルは、Editor Action を実行時にアクティブなファイル階層に作成されます(ファイルパス取得できない場合、Documents 直下)。

コードの全貌

コード量の多さに面食らってしまうかもですが、大半がテンプレ用に貼り付ける文字列たちなので、実際はさほど多くありません。

from pathlib import Path

import arrow

from objc_util import ObjCClass
import editor
import console

# todo: set code templates
blank_value = ''

copy_value = str(editor.get_text() if editor.get_text() else blank_value)

ui_value = '''\
import ui


class View(ui.View):
  def __init__(self):
    self.bg_color = 1

  def did_load(self):
    pass

  def will_close(self):
    pass

  def draw(self):
    pass

  def layout(self):
    pass

  def touch_began(self, touch):
    pass

  def touch_moved(self, touch):
    pass

  def touch_ended(self, touch):
    pass

  def keyboard_frame_will_change(self, frame):
    pass

  def keyboard_frame_did_change(self, frame):
    pass


if __name__ == '__main__':
  view = View()
  view.present()
  #view.present(hide_title_bar=True)
  #view.present(style='fullscreen', orientations=['portrait'])

'''

shader_value = '''\
precision highp float;

uniform float u_time;
uniform vec2 u_sprite_size;
uniform float u_scale;
uniform sampler2D u_texture;
uniform vec4 u_tint_color;
uniform vec4 u_fill_color;
varying vec2 v_tex_coord;


void main(){
  float t = u_time;
  vec2 uv = v_tex_coord;
  vec2 p = (uv- vec2(0.5)) *2.0;
  
  
  uv = uv / 2.0 + vec2(0.5);
  if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)discard;
  gl_FragColor = vec4(uv.x, uv.y, 0.0, 1.0);
  //gl_FragColor = texture2D(u_texture,uv.xy);
  
}

'''


def get_feedback_generator():
  """
  call feedback ex:
  `UIImpactFeedbackGenerator.impactOccurred()`
  """
  style = 4  # 0-4 
  UIImpactFeedbackGenerator = ObjCClass('UIImpactFeedbackGenerator').new()
  UIImpactFeedbackGenerator.prepare()
  UIImpactFeedbackGenerator.initWithStyle_(style)
  return UIImpactFeedbackGenerator


class TemplateItem:
  def __init__(self, prompt, code, extension='.py'):
    self.prompt = prompt
    self.code = code
    self.extension = extension


def get_prompt(templates):
  prompt = ''
  for n, template in enumerate(templates):
    prompt += f'{n} => {template.prompt},\n'
  prompt += 'select a num, create a template file.\n'
  return prompt


def get_dirpath():
  fliepath = editor.get_path()
  if fliepath:
    dir_path = Path(f'{fliepath}').parent
  else:
    _home = Path('/').home()
    dir_path = _home / 'Documents'
  return dir_path


def get_nowtime():
  utc = arrow.utcnow().to('JST')
  now_str = utc.format('YYMMDD_HHmm')
  return now_str


def create_flie(templates):
  prompt = get_prompt(templates)
  feedback = get_feedback_generator()
  try:
    choose_num = int(input(prompt))
  except:
    print('Select the number(int) displayed. ')
    feedback.impactOccurred()
    return
  if choose_num == None:
    print('I can\'t find the number I chose.')
    feedback.impactOccurred()
    return

  choose_template = templates[choose_num]
  now_time = get_nowtime()

  root_dir = get_dirpath()
  new_file = Path(root_dir, f'{now_time}{choose_template.extension}')
  new_file.write_text(choose_template.code, encoding='utf-8')

  console.clear()
  editor.open_file(str(new_file), new_tab=True)
  console.hide_output()
  feedback.impactOccurred()


def main():
  blank = TemplateItem('blank', blank_value)
  copy = TemplateItem('copy', copy_value)
  ui = TemplateItem('ui', ui_value)
  shader = TemplateItem('shader', shader_value, '.js')

  template_list = [blank, copy, ui, shader]
  create_flie(template_list)


if __name__ == '__main__':
  main()

コードのないよう

上から下に順を追って説明していきます。文法よりも、何をしてるのかベースですので、書き換える際の参考になればと思います。

エラーハンドリングがかなりガバです。気軽に気になる所は教えてください🙇

copy_value

editor モジュールを使い、編集中ファイルのコードをコピーしています。

文字として取得できない場合や、ファイルパス失敗でも空のファイル生成するようにif で処理させています。

ui_value shader_value

ui モジュール(次回の記事予定)やシェーダー(GLSL)のテンプレートとして、準備してます。

使いいいテンプレートを作りたい場合は、ここを書き換えるか、参考にして追加する事も可能です。

class TemplateItem

不適切な書き方かもしれませんが、オブジェクトとして. 呼び出しをしたかったので、class で作っています。

辞書のdir['key'] がしっくりこなかったのが理由です。

def get_prompt(templates):

console画面に操作指示として出す役割です。enumerate でindex としてカウントさせて、テンプレート一覧の配列呼び出しと同期できるようにしてます。

def get_dirpath():

もしもファイルパス取得が失敗した場合、Script Library 直下にファイルを作成できるようにしてます。

def get_nowtime():

arrow — Python 3.6.1 documentation

arrow モジュールがPythonista3 には、標準で入っているのでtime モジュールがNG な理由はありません(雰囲気)。

def create_flie(templates):

このスクリプトの心臓部です。

Pythonista3 的な言及ですが、editor モジュールにもeditor.make_new_file([name, content]) として、ファイル作成関数は用意されています。

しかし、.py ファイルしか生成してくれないご様子なのです(自動保存してくれるので便利なのですが)。

今回の処理ではpathlib.Path.write_text で、事前に設定しているpathlib のオブジェクトにwrite_text してあげるかたちをとっています(なんとなくpathlib が好き)。

また、

console — Utilities for Console Output and Various System Services — Python 3.6.1 documentation

console モジュールを使い、console 画面の文字を消したり引っ込めたりしています。

def main():

テンプレートの選択ができるように、TemplateItem のインスタンスを配列に格納しています。

TemplateItem の引数として

  • prompt
    • console に表示させたい文字列
  • code
    • テンプレートとして、新規ファイルに書き込みたい文字列
  • extension
    • 新規ファイルの拡張子
    • 指定しない場合は.py

また、ui と変数名を指定しまっていますが、ui.View 使わないし、main 関数内スコープだからヨシ!としてしまってます(よくない)

次回は

このスクリプトたちで、私の人生は豊かになっています☺️

そんな、豊かになる秘訣をみなさまに共有ができて嬉しいです。

iPhone でコードを書くという行為が、苦行のように見える方もいらしゃるかもしれませんが、こうしたスクリプトに支えられ楽しくやってます😇

次回は、ui モジュールについての記事です。PyKeys カスタマイズの処理や、テンプレートの中でも登場してきたui を知るとPythonista3 の楽しさがより一層広がると思います。

ここまで、読んでいただきありがとうございました。

せんでん

Discord

Pythonista3 の日本語コミュニティーがあります。みなさん優しくて、わからないところも親身に教えてくれるのでこの機会に覗いてみてください。

書籍

iPhone/iPad でプログラミングする最強の本。

その他

  • サンプルコード

Pythonista3 Advent Calendar 2022 でのコードをまとめているリポジトリがあります。

コードのエラーや変なところや改善点など。ご指摘やPR お待ちしておりますー

  • Twitter

なんしかガチャガチャしていますが、お気兼ねなくお声がけくださいませー

  • GitHub

基本的にGitHub にコードをあげているので、何にハマって何を実装しているのか観測できると思います。

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?