Widgetを等倍で中央寄せさせておきたい!
QScrollAreaのresizeEventを継承してゴニョゴニョしたら割と簡単にできた
import sys from PySide import QtCore, QtGui class AlwaysCenterAlign(QtGui.QScrollArea): def __init__(self): super().__init__() self.setAlignment(QtCore.Qt.AlignCenter) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) def resizeEvent(self, event): super().resizeEvent(event) v = self.verticalScrollBar() h = self.horizontalScrollBar() v.setValue(v.minimum() + (v.maximum() - v.minimum()) / 2) h.setValue(h.minimum() + (h.maximum() - h.minimum()) / 2) def wheelEvent(self, _): pass def keyPressEvent(self, _): pass if __name__ == '__main__': a = QtGui.QApplication(sys.argv) s = AlwaysCenterAlign() l = QtGui.QLabel() l.setPixmap(QtGui.QPixmap('lena.jpg')) s.setWidget(l) s.show() sys.exit(a.exec_())
RaspberryPi 3 RPIOでサーボモーターを動かす
RPIOをインストール
モーターは デジタル・マイクロサーボ SG90 を使いました
下記サイトを参考に github.com
こんな感じでインストール
cd ~ git clone https://github.com/metachris/RPIO.git --branch v2 --single-branch cd RPIO sudo python3.4 setup.py install
スクリプトを書く
root権限で実行しないといけないっぽい
from RPIO import PWM import time servo = PWM.Servo() pin = 14 def deg_to_width(deg): ''' 角度からパルス幅に変換 :param: deg 角度(-90~+90) :return: パルス幅 ''' center = 127 margin = 45 step = (center - margin) / 90.0 deg = max(-90.0, min(deg, 90.0)) return int(center + deg * step) * 10 while True: for x in [-90, 0, 90, 0]: servo.set_servo(pin, deg_to_width(x)) time.sleep(1)
ちなみにPWMとPCMは同時に使えないのでサーボ制御しながら音も出したいときはBluetoothスピーカーとかUSBサウンドカードを使ったほうがよさげ
QActionでコードもすっきり
メニューやツールバーを実装するときはQPushButtonやコールバックをガリガリ書くのではなくQActionを作る。QActionオブジェクトを作成しておけば、複数のメニューやツールバーをまたいでも、ひとつのQActionオブジェクトで挙動を定義できるし、QActionGroupを使えばラジオボタンも作れる。ショートカットキーやIconも設定できる。
import sys from PySide.QtCore import * from PySide.QtGui import * # ラジオボタン的な挙動をするメニューを作る例 if __name__ == '__main__': myapp = QApplication(sys.argv) window = QMainWindow() # まずはExclusiveなグループを作成 exclusive_group = QActionGroup(window) exclusive_group.setExclusive(True) # グループに属するアクションを作る def createExclusiveAction(in_name, in_icon, in_shortcut): action = QAction(in_name, window) action.setActionGroup(exclusive_group) action.setIcon(myapp.style().standardIcon(in_icon)) action.setShortcut(in_shortcut) action.setCheckable(True) return action data = [ ('Play', QStyle.SP_MediaPlay, 'Q'), ('Pause', QStyle.SP_MediaPause, 'W'), ('Stop', QStyle.SP_MediaStop, 'E'), ('Forward', QStyle.SP_MediaSeekForward, 'R'), ('Backward', QStyle.SP_MediaSeekBackward, 'T'), ] actions = [] for name, icon, shortcut in data: actions.append(createExclusiveAction(name, icon, shortcut)) # メニューバーとツールバー両方に追加して動作テスト menubar = window.menuBar() mediamenu = menubar.addMenu('Media') toolbar = window.addToolBar('ToolBar') for x in actions: mediamenu.addAction(x) toolbar.addAction(x) window.show() sys.exit(myapp.exec_())
ラズパイ起動時にBluetooth接続してwav再生
自分の環境でちゃんと動く記事が無かったのでメモ。設定ファイルをあちこち編集するのは好きじゃないのでbash_profileで頑張った。Linuxめんどい。
環境
- Raspberry Pi 3 Model B
- OS 2016-11-25-raspbian-jessie
1. モジュールインストール
sudo apt-get install pulseaudio pavucontrol pulseaudio-module-bluetooth reboot
2. 準備(GUIからTerminal起動してやった)
pulseaudioを起動
pulseaudio -D
ペアリング
bluetoothctl > scan on > (BluetoothスピーカーをペアリングモードにしてMACアドレスが表示されるまで待つ) > scan off > pair FF:FF:FF:FF:FF:FF # BluetoothスピーカーのMACアドレス > connect FF:FF:FF:FF:FF:FF # BluetoothスピーカーのMACアドレス > trust FF:FF:FF:FF:FF:FF # BluetoothスピーカーのMACアドレス > quit
デバイスの設定
- メニュー ⇒ Sound & Video ⇒ PulseAudio Volume Control
- ConfigurationメニューからALSAの出力先をOffにしておく
3. 起動スクリプトを用意
.bash_profileをユーザーのホームディレクトリに作成
pulseaudio -D sleep 5 # 適当にウェイト bluetoothctl << EOF power on connect FF:FF:FF:FF:FF:FF # BluetoothスピーカーのMACアドレス quit EOF sleep 5 # 適当にウェイト pacmd set-sink-volume 1 32767 # 音量調節(デバイス番号、音量~65565) aplay test.wav # wavを再生
CUIモードにしてreboot
Python lambda式の落とし穴
Pythonのクロージャ内部で使われている変数は通常、実行時に値が評価されます。for文などと組み合わせてlambda式やローカル関数を使うときは、ちょっと気を付けないといけません。
例えば、0~4まで出力する関数を5つ用意したいとき
# 関数作る functions = [] for i in range(0, 5): functions.append(lambda: print(i)) # 関数呼び出す for func in functions: func() # この時点では i は 4 ですよ?
このように書いてしまうと、下記のような結果が出力されてしまいます。
4 4 4 4 4
こいつを回避するためには、デフォルト引数を使ってちょいと工夫してやる必要があります。
# 関数作る functions = [] for i in range(0, 5): functions.append(lambda x=i: print(x)) # デフォルト引数は関数定義時に評価される # 関数呼び出す for func in functions: func()
これで意図した結果が得られます。
0 1 2 3 4
functools.partialを使う方法もあるようですね。
QDockWidgetに追加したWidgetが表示されないとき
setWidget()を使え
QListWidget コンテキストメニュー
# conding: utf-8 import sys from PySide.QtCore import * from PySide.QtGui import * # 右クリックでカレントの行を削除 def customMenu(widget, pos): if len(widget.selectedItems()) == 0: return action = QAction('Remove', widget) action.triggered.connect(lambda : widget.takeItem(widget.currentRow())) menu = QMenu() menu.addAction(action) menu.exec_(widget.mapToGlobal(pos)) # entry point if __name__ == '__main__': myapp = QApplication(sys.argv) widget = QListWidget() widget.setContextMenuPolicy(Qt.CustomContextMenu) # Policyも設定しないといけない widget.customContextMenuRequested.connect(lambda pos : customMenu(widget, pos)) widget.addItem('item1') widget.addItem('item2') widget.addItem('item3') widget.show() sys.exit(myapp.exec_())