3
3
QStandardItemModel ,
4
4
QStandardItem ,
5
5
)
6
+ from PySide6 .QtCore import QSize , Qt
6
7
from PySide6 .QtWidgets import QTreeView
8
+ from PySide6 .QtWidgets import (
9
+ QVBoxLayout ,
10
+ QHBoxLayout ,
11
+ QPushButton ,
12
+ QLineEdit ,
13
+ QSpinBox ,
14
+ QTextEdit ,
15
+ )
7
16
8
17
9
18
class BNFuncItem (QStandardItem ):
@@ -13,25 +22,109 @@ def __init__(self, func):
13
22
self .setText (func .name )
14
23
15
24
16
- class CallTreeWidget :
17
- def __init__ (self , label_name , depth ):
18
- self .treeview = QTreeView ()
19
- self .model = QStandardItemModel ()
20
- self .proxy_model = QSortFilterProxyModel (self .treeview )
25
+ class CurrentFunctionLayout (QHBoxLayout ):
26
+ def __init__ (self ):
27
+ super ().__init__ ()
28
+ self .cur_func_text = QTextEdit ()
29
+ self .cur_func_text .setReadOnly (True )
30
+ self .cur_func_text .setMaximumHeight (40 )
31
+ self .cur_func_text .setAlignment (Qt .AlignLeft | Qt .AlignTop )
32
+ self .cur_func_text .setLineWrapMode (QTextEdit .NoWrap )
33
+ super ().addWidget (self .cur_func_text )
34
+
35
+
36
+ # Layout with search bar and expand/collapse buttons
37
+ # Takes CallTreeLayout as a parameter
38
+ class CallTreeUtilLayout (QHBoxLayout ):
39
+ def __init__ (self , calltree : object ):
40
+ super ().__init__ ()
41
+ self .calltree = calltree
42
+ btn_size = QSize (25 , 25 )
43
+ self .expand_all_button = QPushButton ("E" )
44
+ self .expand_all_button .setFixedSize (btn_size )
45
+ self .expand_all_button .clicked .connect (self .calltree .expand_all )
46
+
47
+ self .collapse_all_button = QPushButton ("C" )
48
+ self .collapse_all_button .setFixedSize (btn_size )
49
+ self .collapse_all_button .clicked .connect (self .calltree .collapse_all )
50
+
51
+ self .func_filter = QLineEdit ()
52
+ self .func_filter .textChanged .connect (self .calltree .onTextChanged )
53
+
54
+ self .spinbox = QSpinBox ()
55
+ self .spinbox .valueChanged .connect (self .spinbox_changed )
56
+ self .spinbox .setValue (self .calltree .func_depth )
57
+ super ().addWidget (self .func_filter )
58
+ super ().addWidget (self .expand_all_button )
59
+ super ().addWidget (self .collapse_all_button )
60
+ super ().addWidget (self .spinbox )
61
+
62
+ def spinbox_changed (self ):
63
+ self .calltree .func_depth = self .spinbox .value ()
64
+ if self .calltree .cur_func is not None :
65
+ self .calltree .update_widget (self .calltree .cur_func )
66
+
67
+
68
+ class CallTreeLayout (QVBoxLayout ):
69
+ def __init__ (self , label_name : str , depth : int , is_caller : bool ):
70
+ super ().__init__ ()
71
+ self ._cur_func = None
72
+ self ._is_caller = is_caller
73
+ # Creates treeview for all the function calls
74
+ self ._treeview = QTreeView ()
75
+ self ._model = QStandardItemModel ()
76
+ self ._proxy_model = QSortFilterProxyModel (self .treeview )
21
77
self .proxy_model .setSourceModel (self .model )
22
78
23
79
self .treeview .setModel (self .proxy_model )
80
+
81
+ # Clicking function on treeview will take you to the function
24
82
self .treeview .doubleClicked .connect (self .goto_func )
25
83
self ._func_depth = depth
26
84
self ._binary_view = None
27
- self .label_name = label_name
85
+ self ._label_name = label_name
28
86
self .set_label (self .label_name )
87
+ super ().addWidget (self .treeview )
88
+ self .util = CallTreeUtilLayout (self )
89
+ super ().addLayout (self .util )
29
90
30
91
def onTextChanged (self , text ):
31
92
self .proxy_model .setRecursiveFilteringEnabled (True )
32
93
self .proxy_model .setFilterRegularExpression (text )
33
94
self .expand_all ()
34
95
96
+ @property
97
+ def proxy_model (self ):
98
+ return self ._proxy_model
99
+
100
+ @property
101
+ def label_name (self ):
102
+ return self ._label_name
103
+
104
+ @property
105
+ def cur_func (self ):
106
+ return self ._cur_func
107
+
108
+ @cur_func .setter
109
+ def cur_func (self , cur_func ):
110
+ self ._cur_func = cur_func
111
+
112
+ @property
113
+ def is_caller (self ):
114
+ return self ._is_caller
115
+
116
+ @property
117
+ def treeview (self ):
118
+ return self ._treeview
119
+
120
+ @property
121
+ def func_depth (self ):
122
+ return self ._func_depth
123
+
124
+ @property
125
+ def model (self ):
126
+ return self ._model
127
+
35
128
@property
36
129
def binary_view (self ):
37
130
return self ._binary_view
@@ -79,12 +172,12 @@ def set_func_calls(self, cur_func, cur_std_item, is_caller: bool, depth=0):
79
172
cur_func_call , new_std_item , is_caller , depth + 1
80
173
)
81
174
82
- def update_widget (self , cur_func , is_caller ):
175
+ def update_widget (self , cur_func ):
83
176
# Clear previous calls
84
177
self .clear ()
85
178
call_root_node = self .model .invisibleRootItem ()
86
179
87
- if is_caller :
180
+ if self . is_caller :
88
181
cur_func_calls = list (set (cur_func .callers ))
89
182
else :
90
183
cur_func_calls = list (set (cur_func .callees ))
@@ -97,7 +190,7 @@ def update_widget(self, cur_func, is_caller):
97
190
root_std_items .append (BNFuncItem (cur_func_call ))
98
191
cur_std_item = root_std_items [- 1 ]
99
192
if cur_func != cur_func_call :
100
- self .set_func_calls (cur_func_call , cur_std_item , is_caller )
193
+ self .set_func_calls (cur_func_call , cur_std_item , self . is_caller )
101
194
102
195
call_root_node .appendRows (root_std_items )
103
196
self .expand_all ()
0 commit comments