upt-gui: implement double-click in details
Stefan Schuermans

Stefan Schuermans commited on 2020-05-24 11:27:30
Showing 2 changed files, with 117 additions and 56 deletions.

... ...
@@ -182,6 +182,7 @@
182 182
                     <property name="search_column">0</property>
183 183
                     <property name="fixed_height_mode">True</property>
184 184
                     <property name="enable_tree_lines">True</property>
185
+                    <signal name="row-activated" handler="onDetailsRowActivated" swapped="no"/>
185 186
                     <child internal-child="selection">
186 187
                       <object class="GtkTreeSelection"/>
187 188
                     </child>
... ...
@@ -122,6 +122,7 @@ class UptGui:
122 122
         self.widProcessesView = self.builder.get_object('ProcessesView')
123 123
         handlers = {
124 124
             'onDestroy': self.onDestroy,
125
+            'onDetailsRowActivated': self.onDetailsRowActivated,
125 126
             'onProcessesCursorChanged': self.onProcessesCursorChanged
126 127
         }
127 128
         self.builder.connect_signals(handlers)
... ...
@@ -134,30 +135,131 @@ class UptGui:
134 135
         """
135 136
         Gtk.main_quit()
136 137
 
138
+    def onDetailsRowActivated(self, widget, row, col):
139
+        """
140
+        Row in details view has been activated.
141
+        """
142
+        # get proc_id of selected row (if any)
143
+        detail_sel = self.widDetailsView.get_selection()
144
+        if detail_sel is None:
145
+            return
146
+        detail_iter = detail_sel.get_selected()[1]
147
+        if detail_iter is None:
148
+            return
149
+        proc_id = self.widDetailsTree.get_value(detail_iter,
150
+                                                self.DETAIL_PROC_ID)
151
+        # do nothing for rows without valid proc_id
152
+        if proc_id < 0:
153
+            return
154
+        # select process
155
+        self.selectProcess(proc_id)
156
+        # show details of selected process
157
+        self.showDetails(proc_id)
158
+
137 159
     def onProcessesCursorChanged(self, widget):
138 160
         """
139 161
         Cursor changed in processes tree view.
140 162
         """
141 163
         # get proc_id of selected process
142
-        proc_id = None
143 164
         proc_sel = self.widProcessesView.get_selection()
144
-        if proc_sel is not None:
165
+        if proc_sel is None:
166
+            self.showDetails(None)
167
+            return
145 168
         proc_iter = proc_sel.get_selected()[1]
146
-            if proc_iter is not None:
147
-                proc_id = self.widProcessesTree.get_value(
148
-                    proc_iter, self.PROC_PROC_ID)
169
+        if proc_iter is None:
170
+            self.showDetails(None)
171
+            return
172
+        proc_id = self.widProcessesTree.get_value(proc_iter, self.PROC_PROC_ID)
173
+        # show details of selected process
174
+        self.showDetails(proc_id)
175
+
176
+    def openTrace(self, proto_filename: str):
177
+        """
178
+        Open a trace file.
179
+        """
180
+        # forget old processes
181
+        self.widProcessesTree.clear()
182
+        # lead new data
183
+        with open(proto_filename, 'rb') as proto_file:
184
+            self.processes = uproctrace.processes.Processes(proto_file)
185
+        # add processes to processes tree store
186
+        to_be_output = [(self.processes.toplevel, None)]
187
+        while to_be_output:
188
+            procs, parent_iter = to_be_output[-1]
189
+            if not procs:
190
+                del to_be_output[-1]
191
+                continue
192
+            proc = procs[0]
193
+            del procs[0]
194
+            proc_iter = self.widProcessesTree.append(parent_iter)
195
+            self.widProcessesTree.set_value(proc_iter, self.PROC_PROC_ID,
196
+                                            proc.proc_id)
197
+            self.widProcessesTree.set_value(proc_iter,
198
+                                            self.PROC_BEGIN_TIMESTAMP,
199
+                                            proc.begin_timestamp)
200
+            self.widProcessesTree.set_value(
201
+                proc_iter, self.PROC_BEGIN_TIMESTAMP_TEXT,
202
+                timestamp2str(proc.begin_timestamp))
203
+            self.widProcessesTree.set_value(proc_iter, self.PROC_END_TIMESTAMP,
204
+                                            proc.end_timestamp)
205
+            self.widProcessesTree.set_value(proc_iter,
206
+                                            self.PROC_END_TIMESTAMP_TEXT,
207
+                                            timestamp2str(proc.end_timestamp))
208
+            self.widProcessesTree.set_value(proc_iter, self.PROC_CMDLINE,
209
+                                            cmdline2str(proc.cmdline))
210
+            self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME,
211
+                                            proc.cpu_time)
212
+            self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME_TEXT,
213
+                                            duration2str(proc.cpu_time))
214
+            self.widProcessesTree.set_value(proc_iter, self.PROC_MAX_RSS_KB,
215
+                                            proc.max_rss_kb)
216
+            self.widProcessesTree.set_value(proc_iter,
217
+                                            self.PROC_MAX_RSS_KB_TEXT,
218
+                                            kb2str(proc.max_rss_kb))
219
+            to_be_output.append((proc.children, proc_iter))
220
+        # show all processes
221
+        self.widProcessesView.expand_all()
222
+
223
+    def selectProcess(self, proc_id: int):
224
+        """
225
+        Select a process.
226
+        """
227
+        # get selection
228
+        proc_sel = self.widProcessesView.get_selection()
229
+        if proc_sel is None:
230
+            return
231
+        # deselect all processes
232
+        proc_sel.unselect_all()
233
+        # leave if invalid proc_id
234
+        if proc_id is None or proc_id < 0:
235
+            return
236
+        # select process with proc_id
237
+        # scroll the process into view
238
+        def update(proc_store, proc_path, proc_iter, _ctx):
239
+            if proc_store.get_value(proc_iter, self.PROC_PROC_ID) != proc_id:
240
+                return
241
+            proc_sel.select_iter(proc_iter)
242
+            self.widProcessesView.scroll_to_cell(proc_path)
243
+
244
+        self.widProcessesTree.foreach(update, None)
245
+
246
+    def showDetails(self, proc_id: int):
247
+        """
248
+        Show details of process.
249
+        """
149 250
         # forget old details
150 251
         self.widDetailsTree.clear()
151
-        # leave if no process selected
152
-        if proc_id is None:
252
+        # leave if invalid proc_id
253
+        # get process
254
+        if proc_id is None or proc_id < 0:
153 255
             return
154
-        # get process, leave if not found
155 256
         proc = self.processes.getProcess(proc_id)
156 257
         if proc is None:
157 258
             return
158 259
         # add details of new process
159 260
         def add(key: str, value: str, parent_iter=None):
160 261
             detail_iter = self.widDetailsTree.append(parent_iter)
262
+            self.widDetailsTree.set_value(detail_iter, self.DETAIL_PROC_ID, -1)
161 263
             self.widDetailsTree.set_value(detail_iter, self.DETAIL_KEY, key)
162 264
             self.widDetailsTree.set_value(detail_iter, self.DETAIL_VALUE,
163 265
                                           value)
... ...
@@ -169,9 +271,12 @@ class UptGui:
169 271
             list_iter = add(key, f'{len(values):d} entries', parent_iter)
170 272
             for i, value in enumerate(values):
171 273
                 add(f'{key} {i:d}', value, list_iter)
274
+            return list_iter
172 275
 
173 276
         add('begin time', timestamp2str(proc.begin_timestamp))
174
-        add_list('command line', proc.cmdline)
277
+        cmdline_iter = add_list('command line', proc.cmdline)
278
+        self.widDetailsView.expand_row(
279
+            self.widDetailsTree.get_path(cmdline_iter), True)
175 280
         add('CPU time', duration2str(proc.cpu_time))
176 281
         add('end time', timestamp2str(proc.end_timestamp))
177 282
         add_list('environment', sorted(proc.environ))
... ...
@@ -199,53 +304,8 @@ class UptGui:
199 304
                                  cmdline2str(child_proc.cmdline), list_iter)
200 305
                 self.widDetailsTree.set_value(child_iter, self.DETAIL_PROC_ID,
201 306
                                               child_proc.proc_id)
202
-
203
-    def openTrace(self, proto_filename: str):
204
-        """
205
-        Open a trace file.
206
-        """
207
-        # forget old processes
208
-        self.widProcessesTree.clear()
209
-        # lead new data
210
-        with open(proto_filename, 'rb') as proto_file:
211
-            self.processes = uproctrace.processes.Processes(proto_file)
212
-        # add processes to processes tree store
213
-        to_be_output = [(self.processes.toplevel, None)]
214
-        while to_be_output:
215
-            procs, parent_iter = to_be_output[-1]
216
-            if not procs:
217
-                del to_be_output[-1]
218
-                continue
219
-            proc = procs[0]
220
-            del procs[0]
221
-            proc_iter = self.widProcessesTree.append(parent_iter)
222
-            self.widProcessesTree.set_value(proc_iter, self.PROC_PROC_ID,
223
-                                            proc.proc_id)
224
-            self.widProcessesTree.set_value(proc_iter,
225
-                                            self.PROC_BEGIN_TIMESTAMP,
226
-                                            proc.begin_timestamp)
227
-            self.widProcessesTree.set_value(
228
-                proc_iter, self.PROC_BEGIN_TIMESTAMP_TEXT,
229
-                timestamp2str(proc.begin_timestamp))
230
-            self.widProcessesTree.set_value(proc_iter, self.PROC_END_TIMESTAMP,
231
-                                            proc.end_timestamp)
232
-            self.widProcessesTree.set_value(proc_iter,
233
-                                            self.PROC_END_TIMESTAMP_TEXT,
234
-                                            timestamp2str(proc.end_timestamp))
235
-            self.widProcessesTree.set_value(proc_iter, self.PROC_CMDLINE,
236
-                                            cmdline2str(proc.cmdline))
237
-            self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME,
238
-                                            proc.cpu_time)
239
-            self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME_TEXT,
240
-                                            duration2str(proc.cpu_time))
241
-            self.widProcessesTree.set_value(proc_iter, self.PROC_MAX_RSS_KB,
242
-                                            proc.max_rss_kb)
243
-            self.widProcessesTree.set_value(proc_iter,
244
-                                            self.PROC_MAX_RSS_KB_TEXT,
245
-                                            kb2str(proc.max_rss_kb))
246
-            to_be_output.append((proc.children, proc_iter))
247
-        # show all processes
248
-        self.widProcessesView.expand_all()
307
+            self.widDetailsView.expand_row(
308
+                self.widDetailsTree.get_path(list_iter), True)
249 309
 
250 310
 
251 311
 def main(argv):
252 312