そして、それはすべてこのように始まりました。メッセージ処理シーケンスを編集する必要がある1つのプロジェクトのUIを作成する必要がありました。 Simulinkのようなもの。 したがって、既製のライブラリ/フレームワークを探すと便利でした。 最初は、このタスクは人気があり、誰かがすでにこの自転車を使い、検索し、検索し、...見つけられなかったと思いました。 もっと正確に言えば、私はたくさんのアンティーク自転車を見つけましたが、もしあなたがあなた自身の新しい自転車を作ることができれば、だれかが誰かの古い自転車を使うでしょう。 しかし、新しい自転車を作るので、なぜ普遍的にしないのか、あなたはそれがどこで便利になるのか決して知らない。
そのため、いくつかの記事で、ゼロから実用的な例までの開発プロセスについて説明します。 まあ、それを面白くするために、そして農作業が普遍的であるために、それの最初のタスクはSimulinkのSimulinkではなく、Visioに似たフローチャートを描くためのソフトウェアですが、独自のブラックジャックと他の参加者がいます:)
パート1:描画の学習
パート2:マウスイベントの処理
パート3:機能の追加とキーボード処理の継続
パート4:ドラッグアンドドロップの実装
パート5:ノードの接続
誰も気にせず、猫の下で歓迎…
ちょっとしたコメント:コードでは英語の名前を使用し、多くの場合、音訳を使用します。 「キャンバス」や「キャンバス」と書く代わりに、「キャンバス」と書きます。 はい、私はこれが悪く見えて正しくないかもしれないことを知っていますが、ロシア語と英語を混ぜる理想的な方法はありません、すべての方法には欠点があります(私の個人的な意見)。
1.最初のテスト
簡単な当たり前のことから始めましょう。 これはフレームワークであるため、誰かがそれに基づいてアプリケーションを作成するか、それらを作成するアプリケーションの一部を作成することを意味します。 つまり 最も単純なテストでは、最も単純なアプリケーションを作成する必要があります。 余り長く考えることなく、私は互いに接続された長方形を描くことを決め、このコードから始めました(「ConnectedBoxes.py」ファイルにあります):
import wx from MoveMe.Canvas.Canvas import Canvas class CanvasWindow(wx.Frame): def __init__(self, *args, **kw): wx.Frame.__init__(self, *args, **kw) s = wx.BoxSizer(wx.VERTICAL) s.Add(Canvas(self), 1, wx.EXPAND) self.SetSizer(s) if __name__ == '__main__': app = wx.PySimpleApp() CanvasWindow(None).Show() app.MainLoop()
ここにあるすべては、いくつかの点を除いて非常に簡単で明白です。MoveMeはフレームワークの名前であり、Canvasはフレームワークのメインクラスであり、全体をレンダリングする役割を果たします。
2.描くことを学ぶ
実際にキャンバスを使用して、フレームワークが始まります。 彼は、オブジェクト(ノードと呼びます)の保存、ユーザーインタラクションのレンダリングと処理を担当します。 したがって、単純な描画から始めます。
import wx class Canvas(wx.PyScrolledWindow): """ Canvas stores and renders all nodes and node connections. It also handles all user interaction. """ def __init__(self, *args, **kw): super(Canvas, self).__init__(*args, **kw) self.scrollStep = kw.get("scrollStep", 10) self.canvasDimensions = kw.get("canvasDimensions", [800, 800]) self.SetScrollbars(self.scrollStep, self.scrollStep, self.canvasDimensions[0]/self.scrollStep, self.canvasDimensions[1]/self.scrollStep) self._dcBuffer = wx.EmptyBitmap(*self.canvasDimensions) self.Render() self.Bind(wx.EVT_PAINT, lambda evt: wx.BufferedPaintDC(self, self._dcBuffer, wx.BUFFER_VIRTUAL_AREA) ) def Render(self): """Render all nodes and their connection in depth order.""" cdc = wx.ClientDC(self) self.PrepareDC(cdc) dc = wx.BufferedDC(cdc, self._dcBuffer) dc.Clear() gc = wx.GraphicsContext.Create(dc) gc.SetPen(wx.Pen('#000000', 2, wx.SOLID)) gc.DrawRoundedRectangle(12, 34, 56, 78, 10) gc.DrawRoundedRectangle(112, 134, 156, 178, 10)
ここで、すべてがもう少し面白くなります。
- 最初に、「wx.PyScrolledWindow」を継承して、ウィンドウをスクロールし、「self.SetScrollbars」でスクロールパラメーターを設定できるようにします。
- 第二に、直接描画するのではなく、バッファに描画するため、これらすべてがより速く、ちらつきなく行われます。 これを行うには、「wx.BufferedDC」とビットマップであるバッファーを使用します。
- そして第三に、状態の保存に便利な「wx.GraphicsContext」を使用します。 「PushState」および「PopState」メソッドがあり、ブラシ、フォントなどの設定を保存します。ユーザーコードは画面上にブロックを描画し、ユーザーがすべてをその場所に戻すことを保証するものはないため、特に便利です。
3.シーンの配置
最も簡単な描画を考え出しました。このプロセスを何らかの形で合理化し、ステージ上のオブジェクトの概念を紹介するときです。 すべての可視オブジェクトはリストに保存され、オブジェクトの順序が決定されます。 これをサポートするために、「_ canvasObjects」フィールドをキャンバスクラスに追加し、レンダリングプロセスをわずかに変更します。 直接描画する代わりに、シーン上のすべてのオブジェクトのRenderメソッドを呼び出します。 これで、コードは次のようになります。
import wx from MoveMe.Canvas.Objects.SimpleBoxNode import SimpleBoxNode class Canvas(wx.PyScrolledWindow): """ Canvas stores and renders all nodes and node connections. It also handles all user interaction. """ def __init__(self, *args, **kw): super(Canvas, self).__init__(*args, **kw) self.scrollStep = kw.get("scrollStep", 10) self.canvasDimensions = kw.get("canvasDimensions", [800, 800]) self.SetScrollbars(self.scrollStep, self.scrollStep, self.canvasDimensions[0]/self.scrollStep, self.canvasDimensions[1]/self.scrollStep) self._canvasObjects = [SimpleBoxNode([20,20]), SimpleBoxNode([140,40]), SimpleBoxNode([60,120])] self._dcBuffer = wx.EmptyBitmap(*self.canvasDimensions) self.Render() self.Bind(wx.EVT_PAINT, lambda evt: wx.BufferedPaintDC(self, self._dcBuffer, wx.BUFFER_VIRTUAL_AREA) ) def Render(self): """Render all nodes and their connection in depth order.""" cdc = wx.ClientDC(self) self.PrepareDC(cdc) dc = wx.BufferedDC(cdc, self._dcBuffer) dc.Clear() gc = wx.GraphicsContext.Create(dc) for obj in self._canvasObjects: gc.PushState() obj.Render(gc) gc.PopState()
ちなみに、シーンオブジェクトのリストには、SimpleBoxNodeクラスのオブジェクトがすぐに含まれていますが、現時点では、オブジェクトの座標を使用して単純に四角形を描画します。
import wx class SimpleBoxNode(object): """ SimpleBoxNode class represents a simplest possible canvas object that is basically a rectangular box. """ def __init__(self, pos): self.position = pos self.boundingBoxDimensions = [90, 60] def Render(self, gc): gc.SetPen(wx.Pen('#000000', 2, wx.SOLID)) gc.DrawRoundedRectangle(self.position[0], self.position[1], self.boundingBoxDimensions[0], self.boundingBoxDimensions[1], 10) gc.SetFont(wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)) gc.DrawText("(%d, %d)"%(self.position[0], self.position[1]), self.position[0]+10, self.position[1]+10)
ここではすべてが非常に簡単なようです。 「GraphicsContext」にはデフォルト設定がないため、フォントを指定する必要がない限り(この事実とその修正に戻ります)。 現在、私たちのコードはこの絵を描いています:
奇跡はありませんが、開始するには十分です。 これで最初の部分が終了し、次の部分ではマウスイベントとキーボードイベントの処理を追加し、ユーザーが四角形を移動、接続、削除できるようにします。
続行するには...
PS1:コードはGitHubにあります
PS2:PMにタイプミスについて書いてください。