ラベル wxPython の投稿を表示しています。 すべての投稿を表示
ラベル wxPython の投稿を表示しています。 すべての投稿を表示

2024年6月23日日曜日

lubuntu 24.04 LTSでwxPython4.2.1

古いPCにLinuxを入れて使えるようにしています。 Lubuntu 24.04 LTSを古いデスクトップPCに入れて使っています。 リモートデスクトップとpythonくらいしか利用しませんので 古いパソコンでも十分な用途です。 pythonは、python3が利用でます。 さて、GUIにwxPythonをよく利用していたのですが、 そのようなスクリプトを動かすときにwxPythonをインストールする必要があります。 Windowsでは、pipを用いてインストールしていますが、 lubuntuでは、aptを使ってインストールするようです。
apt install python3-wxtools
でインストールできました。

さて、これで使えるなら問題よいのですが、しばしば 仮想環境を利用することが増えています。python3では、 すぐにvenvが利用できるようになっています。 たとえば、

mkdir myproject
cd myproject
python3 -m venv myenv
として、myprojectフォルダの中に、 そのプロジェクト用のmyenvという仮想環境を作って利用したりします。 その仮想環境を利用するには、
cd myproject
source myenv/bin/activate
として、仮想環境のフォルダ内にあるactivateスクリプトを用いて利用できるようにします。 アクティベートするといいます。 ここで pip install numpy とすると仮想環境にnumpyがインストールされるわけです。 ある環境がアクティベートされた状態で起動されるpythonでは、 その環境にインストールされたモジュールが利用できるというわけです。 環境を戻すには、deactivateを実行します。

さて、この仮想環境へはpipを用いてモジュールをインストールします。 pip install wxPythonとしてwxPythonををpipでインストールしようとすると、 ビルドエラーが出て停止してしまいました。 こちら https://wxpython.org/blog/2017-08-17-builds-for-linux-with-pip/index.html のサイトを参考にしてインストールをしました。

"What You Need"の箇所にビルドに必要なライブラリが記載されています。 地道にひとつづつインストールしていきます。 私の環境では、

sudo apt install python-dev
sudo apt install libgtk-3-dev
sudo apt install libgstreamer1.0-dev
sudo apt install libgstreamer-plugins-base1.0-dev
sudo apt install libglu1-mesa-dev mesa-common-dev freeglut3-dev
sudo apt list |grep libwebkit2gtk
sudo apt install libwebkit2gtk-4.1-dev
sudo apt install libjpeg-dev
sudo apt install libpng-dev
sudo apt install libtiff-dev
sudo apt install libsdl-dev
sudo apt install libnotify-dev
sudo apt install libsm-dev
としていきました。すでにインストールされているものもいくつかありましたが、
順番に検索エンジンを利用しながら確認していきました。
途中どれをインストールするかlistを見て確認したりもしています。

あとは、"Build Steps"にあることを順番にしていくだけです。もちろん、 ファイル名はダウンロードされたものに合わせて修正します。 書いてある通りにすると無事にwheelファイルがビルドされ、 wxPythonを利用することができるようになりました。

このようにしてpython3.12でwxPython4.2.1が動きました。 ただし、現在ダウンロードされたファイルをコンパイルしたものは、 pythonを起動してimport wxのあとすぐにquit()してもセグメンテーションフォールトを起こします。 "wxpython 4.2.1 segmentation fault"で検索してみるとすでに報告はあるようです。 とりあえずは利用できるので、修正を待ちます。 ちなみに、仮想環境ではなくaptでインストールした環境ではそのようなエラーは起こっていません。

2020年10月19日月曜日

wxPython 4.1のwx.ListCtrlでCheckBox

wxPythonで行の頭にCheckBoxのついたリストコントールを使いたいとき、 wx.ListCtrlにCheckListCtrlMixinを使っていた。 wxPythonの4.1で、以前のスクリプトを動かしたところこれが動かなかった。

どうやら、CheckListCtrlMixinで定義されていたCheckItem()メソッドが、 wx.ListCtrlのbuilt-ins functionになったために、 そちらが呼び出されて動作しない様子である。 新しい、wx.ListCtrlには、CheckListCtrlMixinを利用しなくても、 CheckBox付きのwx.ListCtrlが利用できるようになったらしい。

wxPythonのページに、EnableCheckBoxes(enable=True)のメソッドが説明されていた。 これを使えばよいが、これまで使いたいスクリプトをとりあえす 動かしたいということであれば、 CheckItem()をMixinのものに上書きすると動作するようになった。

def MyCheckItem(self, index, check=True):
    img_idx = self.GetItem(index).GetImage()
    if img_idx == 0 and check:
        self.SetItemImage(index, 1)
        self.OnCheckItem(index, True)
    elif img_idx == 1 and not check:
        self.SetItemImage(index, 0)
        self.OnCheckItem(index, False)

wx.ListCtrl.CheckItem = MyCheckItem
しかしながら、そのうち直さなくてはならないということで、 EnableCheckBoxes()を使ったものを書いてみた。
import wx
import wx.lib.mixins.listctrl

class CheckListCtrl(wx.ListCtrl):
    def __init__(self, panel, style):
        wx.ListCtrl.__init__(self, panel, -1, 
                style=wx.LC_REPORT,size=wx.Size(400, 300))
        self.EnableCheckBoxes(enable=True)
        self.Bind(wx.EVT_LIST_ITEM_CHECKED, self._OnCheckItem)
        self.Bind(wx.EVT_LIST_ITEM_UNCHECKED, self._OnUnCheckItem)

    def _OnCheckItem(self, evt):
        self.OnCheckItem(evt.Index, True)

    def _OnUnCheckItem(self, evt):
        self.OnCheckItem(evt.Index, False)
        
    def OnCheckItem(self, index, flag):
        print(index, flag)

class AppFrame(wx.Frame):
    def __init__(self, *args, **kwargs):

        wx.Frame.__init__(self, None ,wx.ID_ANY, 'CheckListCtrl', size=(400,300))

        self.list = CheckListCtrl(self, style=wx.LC_REPORT)

        titles = ("check", "title1", "title2")

        for index, title in enumerate(titles):
            self.list.InsertColumn(index, title, wx.LIST_FORMAT_LEFT)

        for index, title in enumerate(titles):
            self.list.Append(["name%d" % index, "value", "value" ]) 

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.list, 1, wx.EXPAND, border=0)
        self.SetSizer(self.sizer)
        self.Show()

if __name__ == "__main__":
    app = wx.App()
    win = AppFrame()
    app.MainLoop()
wx.EVT_LIST_ITEM_CHECKEDとwx.EVT_LIST_ITEM_UNCHECKEDをハンドルして、 従来のMixInを利用した場合と同じ引数で、 OnCheckItem()メソッドを呼び出すようにしてみた。

2020年7月22日水曜日

python3 wxPythonでドラッグアンドドロップ

これまでPython2.7でwxPythonを用いて、ファイルをドラッグアンドドロップで開くスクリプトを 書いて用いていた。Python3.8を使い始めて、改めてドラッグアンドドロップの部分を抜き出して、 動作を確認してみた。 環境は、Windows10 64bit, Python3.8.3, wxPython4.1.0。

tkinterにくらべて、ウィンドウ作成の部分に手間をかけているが、やっていることは以下の通り。

まず、wx.FileDropTargetを継承したクラスを作成して、 ファイルがドロップされた時に実行されるOnDropFiles()を 自分の望むように書き換える。

さらに、wx.PanelのSetDropTarget()メソッドを用いて、 作成したFileDropTargetを継承したクラスのインスタンスを設定するだけである。 実行すると、ウィンドウが開き、その中に配置されたパネルにファイルをドロップすると、 そのパスがコンソールに表示される。

import wx

class MyFileDropTarget(wx.FileDropTarget):
    def __init__(self):
        super(MyFileDropTarget, self).__init__()

    def OnDropFiles(self, x, y, filenames):
        for n in filenames:
            print(x, y, n)
        return True

class DropPanel(wx.Panel):
    def __init__(self, parent, *args, **kwargs):
        super(DropPanel, self).__init__(parent, *args, **kwargs)
        self.droptarget = MyFileDropTarget()
        self.SetDropTarget(self.droptarget)
        
class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(MyFrame, self).__init__(*args, **kwargs)
        
        self.panel = DropPanel(self, size=(300, 300))       
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel,  1, wx.EXPAND)
        self.SetSizer(sizer)
        self.Fit()

class MyApp(wx.App):
    def __init__(self):
        wx.App.__init__(self, False)

    def OnInit(self):
        self.frame = MyFrame(None, title=AppName)
        #self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

if __name__ == '__main__':
    AppName = "DnD Test"
    app = MyApp()
    app.MainLoop()