author | Laurent Bessard |
Wed, 05 Jun 2013 23:23:29 +0200 | |
changeset 1225 | ed8a89171a6c |
parent 1223 | d51cea72baa7 |
child 1230 | 5a4c5724788e |
permissions | -rw-r--r-- |
814 | 1 |
#!/usr/bin/env python |
2 |
# -*- coding: utf-8 -*- |
|
3 |
||
4 |
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor |
|
5 |
#based on the plcopen standard. |
|
6 |
# |
|
7 |
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD |
|
8 |
# |
|
9 |
#See COPYING file for copyrights details. |
|
10 |
# |
|
11 |
#This library is free software; you can redistribute it and/or |
|
12 |
#modify it under the terms of the GNU General Public |
|
13 |
#License as published by the Free Software Foundation; either |
|
14 |
#version 2.1 of the License, or (at your option) any later version. |
|
15 |
# |
|
16 |
#This library is distributed in the hope that it will be useful, |
|
17 |
#but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 |
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
19 |
#General Public License for more details. |
|
20 |
# |
|
21 |
#You should have received a copy of the GNU General Public |
|
22 |
#License along with this library; if not, write to the Free Software |
|
23 |
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
24 |
||
25 |
import wx |
|
26 |
||
27 |
#------------------------------------------------------------------------------- |
|
28 |
# Helpers |
|
29 |
#------------------------------------------------------------------------------- |
|
30 |
||
31 |
[CATEGORY, BLOCK] = range(2) |
|
32 |
||
33 |
#------------------------------------------------------------------------------- |
|
34 |
# Library Panel |
|
35 |
#------------------------------------------------------------------------------- |
|
36 |
||
37 |
class LibraryPanel(wx.Panel): |
|
38 |
||
39 |
def __init__(self, parent, enable_drag=False): |
|
40 |
wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) |
|
41 |
||
42 |
main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) |
|
43 |
main_sizer.AddGrowableCol(0) |
|
44 |
main_sizer.AddGrowableRow(1) |
|
45 |
||
46 |
self.SearchCtrl = wx.SearchCtrl(self) |
|
47 |
self.SearchCtrl.ShowSearchButton(True) |
|
48 |
self.Bind(wx.EVT_TEXT, self.OnSearchCtrlChanged, self.SearchCtrl) |
|
49 |
self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, |
|
50 |
self.OnSearchButtonClick, self.SearchCtrl) |
|
51 |
search_textctrl = self.SearchCtrl.GetChildren()[0] |
|
52 |
search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown) |
|
53 |
main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW) |
|
54 |
||
55 |
splitter_window = wx.SplitterWindow(self) |
|
56 |
splitter_window.SetSashGravity(1.0) |
|
57 |
main_sizer.AddWindow(splitter_window, flag=wx.GROW) |
|
58 |
||
59 |
self.Tree = wx.TreeCtrl(splitter_window, |
|
60 |
size=wx.Size(0, 0), |
|
61 |
style=wx.TR_HAS_BUTTONS| |
|
62 |
wx.TR_SINGLE| |
|
63 |
wx.SUNKEN_BORDER| |
|
64 |
wx.TR_HIDE_ROOT| |
|
65 |
wx.TR_LINES_AT_ROOT) |
|
66 |
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemSelected, self.Tree) |
|
67 |
self.Tree.Bind(wx.EVT_CHAR, self.OnKeyDown) |
|
68 |
if enable_drag: |
|
69 |
self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, self.Tree) |
|
70 |
||
71 |
self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80), |
|
72 |
style=wx.TE_READONLY|wx.TE_MULTILINE) |
|
73 |
||
74 |
splitter_window.SplitHorizontally(self.Tree, self.Comment, -80) |
|
75 |
||
76 |
self.SetSizer(main_sizer) |
|
77 |
||
78 |
self.Controller = None |
|
79 |
||
80 |
self.BlockList = None |
|
81 |
||
82 |
def __del__(self): |
|
83 |
self.Controller = None |
|
84 |
||
85 |
def SetController(self, controller): |
|
86 |
self.Controller = controller |
|
87 |
||
88 |
def SetBlockList(self, blocklist): |
|
89 |
self.BlockList = blocklist |
|
90 |
self.RefreshTree() |
|
91 |
||
92 |
def SetFocus(self): |
|
93 |
self.SearchCtrl.SetFocus() |
|
94 |
||
95 |
def ResetTree(self): |
|
96 |
self.SearchCtrl.SetValue("") |
|
97 |
self.Tree.DeleteAllItems() |
|
98 |
self.Comment.SetValue("") |
|
99 |
||
100 |
def RefreshTree(self): |
|
101 |
if self.Controller is not None: |
|
102 |
to_delete = [] |
|
103 |
selected_name = None |
|
104 |
selected = self.Tree.GetSelection() |
|
105 |
if selected.IsOk(): |
|
106 |
selected_pydata = self.Tree.GetPyData(selected) |
|
107 |
if selected_pydata is not None and selected_pydata["type"] != CATEGORY: |
|
108 |
selected_name = self.Tree.GetItemText(selected) |
|
109 |
if self.BlockList is not None: |
|
110 |
blocktypes = self.BlockList |
|
111 |
else: |
|
112 |
blocktypes = self.Controller.GetBlockTypes() |
|
113 |
root = self.Tree.GetRootItem() |
|
114 |
if not root.IsOk(): |
|
115 |
root = self.Tree.AddRoot("") |
|
116 |
category_item, root_cookie = self.Tree.GetFirstChild(root) |
|
117 |
for category in blocktypes: |
|
118 |
category_name = category["name"] |
|
119 |
if not category_item.IsOk(): |
|
120 |
category_item = self.Tree.AppendItem(root, _(category_name)) |
|
121 |
if wx.Platform != '__WXMSW__': |
|
122 |
category_item, root_cookie = self.Tree.GetNextChild(root, root_cookie) |
|
123 |
else: |
|
124 |
self.Tree.SetItemText(category_item, _(category_name)) |
|
125 |
self.Tree.SetPyData(category_item, {"type" : CATEGORY}) |
|
126 |
blocktype_item, category_cookie = self.Tree.GetFirstChild(category_item) |
|
127 |
for blocktype in category["list"]: |
|
128 |
if not blocktype_item.IsOk(): |
|
129 |
blocktype_item = self.Tree.AppendItem(category_item, blocktype["name"]) |
|
130 |
if wx.Platform != '__WXMSW__': |
|
131 |
blocktype_item, category_cookie = self.Tree.GetNextChild(category_item, category_cookie) |
|
132 |
else: |
|
133 |
self.Tree.SetItemText(blocktype_item, blocktype["name"]) |
|
134 |
block_data = {"type" : BLOCK, |
|
135 |
"block_type" : blocktype["type"], |
|
136 |
"inputs" : tuple([type for name, type, modifier in blocktype["inputs"]]), |
|
137 |
"extension" : None} |
|
138 |
if blocktype["extensible"]: |
|
139 |
block_data["extension"] = len(blocktype["inputs"]) |
|
140 |
self.Tree.SetPyData(blocktype_item, block_data) |
|
141 |
if selected_name == blocktype["name"]: |
|
142 |
self.Tree.SelectItem(blocktype_item) |
|
143 |
comment = blocktype["comment"] |
|
144 |
self.Comment.SetValue(_(comment) + blocktype.get("usage", "")) |
|
145 |
blocktype_item, category_cookie = self.Tree.GetNextChild(category_item, category_cookie) |
|
146 |
while blocktype_item.IsOk(): |
|
147 |
to_delete.append(blocktype_item) |
|
148 |
blocktype_item, category_cookie = self.Tree.GetNextChild(category_item, category_cookie) |
|
149 |
category_item, root_cookie = self.Tree.GetNextChild(root, root_cookie) |
|
150 |
while category_item.IsOk(): |
|
151 |
to_delete.append(category_item) |
|
152 |
category_item, root_cookie = self.Tree.GetNextChild(root, root_cookie) |
|
153 |
for item in to_delete: |
|
154 |
self.Tree.Delete(item) |
|
155 |
||
156 |
def GetSelectedBlock(self): |
|
157 |
selected = self.Tree.GetSelection() |
|
158 |
if (selected.IsOk() and |
|
159 |
self.Tree.GetItemParent(selected) != self.Tree.GetRootItem() and |
|
160 |
selected != self.Tree.GetRootItem()): |
|
161 |
selected_data = self.Tree.GetPyData(selected) |
|
162 |
return {"type": self.Tree.GetItemText(selected), |
|
163 |
"inputs": selected_data["inputs"]} |
|
164 |
return None |
|
165 |
||
166 |
def SelectTreeItem(self, name, inputs): |
|
167 |
item = self.FindTreeItem(self.Tree.GetRootItem(), name, inputs) |
|
168 |
if item is not None and item.IsOk(): |
|
169 |
self.Tree.SelectItem(item) |
|
170 |
self.Tree.EnsureVisible(item) |
|
171 |
||
172 |
def FindTreeItem(self, root, name, inputs = None): |
|
173 |
if root.IsOk(): |
|
174 |
pydata = self.Tree.GetPyData(root) |
|
175 |
if pydata is not None: |
|
176 |
type_inputs = pydata.get("inputs", None) |
|
177 |
type_extension = pydata.get("extension", None) |
|
178 |
if inputs is not None and type_inputs is not None: |
|
179 |
if type_extension is not None: |
|
180 |
same_inputs = type_inputs == inputs[:type_extension] |
|
181 |
else: |
|
182 |
same_inputs = type_inputs == inputs |
|
183 |
else: |
|
184 |
same_inputs = True |
|
185 |
if pydata is not None and self.Tree.GetItemText(root) == name and same_inputs: |
|
186 |
return root |
|
187 |
else: |
|
1223
d51cea72baa7
Fixed bug when adding standard function like ADD, SUB, MUL,... that are overloaded. Block type was not selected and shown when opening FBDBlockDialog to edit it.
Laurent Bessard
parents:
1068
diff
changeset
|
188 |
item, root_cookie = self.Tree.GetFirstChild(root) |
814 | 189 |
while item.IsOk(): |
190 |
result = self.FindTreeItem(item, name, inputs) |
|
191 |
if result: |
|
192 |
return result |
|
193 |
item, root_cookie = self.Tree.GetNextChild(root, root_cookie) |
|
194 |
return None |
|
195 |
||
196 |
def SearchInTree(self, value, mode="first"): |
|
197 |
root = self.Tree.GetRootItem() |
|
198 |
if not root.IsOk(): |
|
199 |
return False |
|
200 |
||
201 |
if mode == "first": |
|
202 |
item, item_cookie = self.Tree.GetFirstChild(root) |
|
203 |
selected = None |
|
204 |
else: |
|
205 |
item = self.Tree.GetSelection() |
|
206 |
selected = item |
|
207 |
if not item.IsOk(): |
|
208 |
item, item_cookie = self.Tree.GetFirstChild(root) |
|
209 |
while item.IsOk(): |
|
210 |
item_pydata = self.Tree.GetPyData(item) |
|
211 |
if item_pydata["type"] == CATEGORY: |
|
212 |
if mode == "previous": |
|
213 |
child = self.Tree.GetLastChild(item) |
|
214 |
else: |
|
215 |
child, child_cookie = self.Tree.GetFirstChild(item) |
|
216 |
if child.IsOk(): |
|
217 |
item = child |
|
218 |
elif mode == "previous": |
|
219 |
item = self.Tree.GetPrevSibling(item) |
|
220 |
else: |
|
221 |
item = self.Tree.GetNextSibling(item) |
|
222 |
else: |
|
223 |
name = self.Tree.GetItemText(item) |
|
1068
ef088254ba4b
Modify search algorithm in LibraryPanel to search match in whole block name, not only at beginning
Laurent Bessard
parents:
814
diff
changeset
|
224 |
if name.upper().find(value.upper()) != -1 and item != selected: |
814 | 225 |
child, child_cookie = self.Tree.GetFirstChild(root) |
226 |
while child.IsOk(): |
|
227 |
self.Tree.CollapseAllChildren(child) |
|
228 |
child, child_cookie = self.Tree.GetNextChild(root, child_cookie) |
|
229 |
self.Tree.SelectItem(item) |
|
230 |
self.Tree.EnsureVisible(item) |
|
231 |
return True |
|
232 |
||
233 |
elif mode == "previous": |
|
234 |
previous = self.Tree.GetPrevSibling(item) |
|
235 |
if previous.IsOk(): |
|
236 |
item = previous |
|
237 |
else: |
|
238 |
parent = self.Tree.GetItemParent(item) |
|
239 |
item = self.Tree.GetPrevSibling(parent) |
|
240 |
||
241 |
else: |
|
242 |
next = self.Tree.GetNextSibling(item) |
|
243 |
if next.IsOk(): |
|
244 |
item = next |
|
245 |
else: |
|
246 |
parent = self.Tree.GetItemParent(item) |
|
247 |
item = self.Tree.GetNextSibling(parent) |
|
248 |
return False |
|
249 |
||
250 |
def OnSearchCtrlChanged(self, event): |
|
251 |
self.SearchInTree(self.SearchCtrl.GetValue()) |
|
252 |
event.Skip() |
|
253 |
||
254 |
def OnSearchButtonClick(self, event): |
|
255 |
self.SearchInTree(self.SearchCtrl.GetValue(), "next") |
|
256 |
event.Skip() |
|
257 |
||
258 |
def OnTreeItemSelected(self, event): |
|
259 |
selected = event.GetItem() |
|
260 |
pydata = self.Tree.GetPyData(selected) |
|
261 |
if pydata is not None and pydata["type"] != CATEGORY: |
|
262 |
blocktype = self.Controller.GetBlockType(self.Tree.GetItemText(selected), pydata["inputs"]) |
|
263 |
if blocktype: |
|
264 |
comment = blocktype["comment"] |
|
265 |
self.Comment.SetValue(_(comment) + blocktype.get("usage", "")) |
|
266 |
else: |
|
267 |
self.Comment.SetValue("") |
|
268 |
else: |
|
269 |
self.Comment.SetValue("") |
|
270 |
if getattr(self, "_OnTreeItemSelected", None) is not None: |
|
271 |
self._OnTreeItemSelected(event) |
|
272 |
event.Skip() |
|
273 |
||
274 |
def OnTreeBeginDrag(self, event): |
|
275 |
selected = event.GetItem() |
|
276 |
pydata = self.Tree.GetPyData(selected) |
|
277 |
if pydata is not None and pydata["type"] == BLOCK: |
|
278 |
data = wx.TextDataObject(str((self.Tree.GetItemText(selected), |
|
279 |
pydata["block_type"], "", pydata["inputs"]))) |
|
280 |
dragSource = wx.DropSource(self.Tree) |
|
281 |
dragSource.SetData(data) |
|
282 |
dragSource.DoDragDrop() |
|
283 |
||
284 |
def OnKeyDown(self, event): |
|
285 |
keycode = event.GetKeyCode() |
|
286 |
search_value = self.SearchCtrl.GetValue() |
|
287 |
if keycode == wx.WXK_UP and search_value != "": |
|
288 |
self.SearchInTree(search_value, "previous") |
|
289 |
elif keycode == wx.WXK_DOWN and search_value != "": |
|
290 |
self.SearchInTree(search_value, "next") |
|
291 |
else: |
|
292 |
event.Skip() |