wxPythonで文字やボタンがぼやける時の対処法(高DPI対応)

公開日:  

Python wxPython


wxPythonでGUIを作ったらなぜか下の画像のように表示がぼやけてしまったので、その時の対処法をメモしときます。

ぼやけたGUI

ぼやけたGUI

環境

  • Windows10
  • Python 3.6.2
  • wxPython 4.0.6

なぜ、ぼやけるのか?

どうやらdpi(dot per inch)というのが関係しているようです。このdpiというは1インチの中にどれだけのドットを表現できるかという単位だそうです。
今回問題となったのは、私が使用していたディスプレイのdpi値が高かった(マイクロソフトのSurfaceシリーズです)のが原因でした。

高dpiの環境で従来のdpi設定で作成されたGUIをそのまま開くと表示が小さすぎてしまいます。そのためシステムがGUIを拡大して表示してくれてます。この拡大の過程でGUIがぼやけてしまったのです。

ちなみに私のWindowsのディスプレイ設定は以下になります。150%拡大の設定になっています。

Windowsのディスプレイ設定(150%の拡大率となっている)

Windowsのディスプレイ設定(150%の拡大率となっている)

wxPythonはそのままでは高dpiに対応してくれていないので、表示がぼやけてしまいます。
では、どうすれば高dpiに対応してくれるのかというと、下記コードをプログラムの最初に入れるとうまくいきました。

import ctypes
try:
    ctypes.windll.shcore.SetProcessDpiAwareness(True)
except:
    pass

これでぼやけなくなりました。

ぼやけなくなった!

ぼやけなくなった!

最後に今回のソースコードを乗せときます。
wxGladeというツールを使ってソースを生成していますので、そういうコメントがついています。とくにイベントとかはつけてないのでハリボテです。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# generated by wxGlade 0.9.3 on Mon Aug  5 20:38:41 2019
#

import wx

# begin wxGlade: dependencies
# end wxGlade

# begin wxGlade: extracode
# end wxGlade


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MyFrame.__init__
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.SetSize((470, 300))
        self.text_ctrl_1 = wx.TextCtrl(self, wx.ID_ANY, "")
        self.button_1 = wx.Button(self, wx.ID_ANY, "...")
        self.button_2 = wx.Button(self, wx.ID_ANY, u"実行")
        self.button_3 = wx.Button(self, wx.ID_ANY, u"キャンセル")

        self.__set_properties()
        self.__do_layout()
        # end wxGlade

    def __set_properties(self):
        # begin wxGlade: MyFrame.__set_properties
        self.SetTitle("frame")
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.button_1.SetMinSize((65, 34))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MyFrame.__do_layout
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
        label_1 = wx.StaticText(self, wx.ID_ANY, u"ファイル:")
        sizer_2.Add(label_1, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        sizer_2.Add(self.text_ctrl_1, 2, wx.ALIGN_CENTER | wx.ALL, 5)
        sizer_2.Add(self.button_1, 1, wx.ALIGN_CENTER | wx.ALL, 5)
        sizer_1.Add(sizer_2, 1, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 10)
        sizer_3.Add((146, 20), 0, wx.ALIGN_CENTER | wx.ALL, 5)
        sizer_3.Add(self.button_2, 1, wx.ALIGN_CENTER | wx.ALL, 5)
        sizer_3.Add(self.button_3, 1, wx.ALIGN_CENTER | wx.ALL, 5)
        sizer_1.Add(sizer_3, 1, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 10)
        self.SetSizer(sizer_1)
        self.Layout()
        # end wxGlade

# end of class MyFrame

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

# end of class MyApp

if __name__ == "__main__":
    # 高dpi対応
    import ctypes
    try:
        ctypes.windll.shcore.SetProcessDpiAwareness(True)
    except:
        pass
    app = MyApp(0)
    app.MainLoop()


関連記事