1#!/usr/local/bin/python3.8 2 3import sys 4import signal 5signal.signal(signal.SIGINT, signal.SIG_DFL) 6import _thread 7import gi 8gi.require_version('Gtk', '3.0') 9gi.require_version('Gdk', '3.0') 10from gi.repository import Gdk, Gtk, GObject, GLib, Pango 11 12import time 13from datetime import timedelta 14 15class Main: 16 def __init__(self): 17 if len(sys.argv) > 1 and sys.argv[1] == "--help": 18 self.end() 19 else: 20 self.start() 21 22 def end(self): 23 print(""" 24 git-monkey: a simple repo manager for debian-based projects. 25 26 Run git-monkey, then click the + to add git repos to work with. 27 28 """) 29 quit() 30 31 def start(self): 32 self.builder = Gtk.Builder() 33 self.builder.add_from_file("cinnamon-stap-monitor.glade") 34 self.treebox = self.builder.get_object("treebox") 35 self.window = self.builder.get_object("window") 36 self.term_button = self.builder.get_object("terminal") 37 self.output_scroller = self.builder.get_object("scroller") 38 self.output = self.builder.get_object("output_view") 39 self.timer_label = self.builder.get_object("timer_label") 40 41 self.builder.connect_signals(self) 42 43 self.treeview = Gtk.TreeView() 44 self.model = Gtk.ListStore(str, int, int) 45 46 color = Gdk.RGBA() 47 Gdk.RGBA.parse(color, "black") 48 self.output.override_background_color(Gtk.StateFlags.NORMAL, color) 49 Gdk.RGBA.parse(color, "#00CC00") 50 51 fontdesc = Pango.FontDescription("monospace") 52 self.output.override_font(fontdesc) 53 self.output.override_color(Gtk.StateFlags.NORMAL, color) 54 55 self.window.connect("destroy", Gtk.main_quit) 56 57 cell = Gtk.CellRendererText() 58 column = Gtk.TreeViewColumn("GObject Name", cell, text=0) 59 column.set_sort_column_id(0) 60 self.treeview.append_column(column) 61 62 cell = Gtk.CellRendererText() 63 column = Gtk.TreeViewColumn("Instance count", cell, text=1) 64 column.set_sort_column_id(1) 65 66 self.treeview.append_column(column) 67 68 cell = Gtk.CellRendererText() 69 column = Gtk.TreeViewColumn("Rate", cell, text=2) 70 column.set_sort_column_id(2) 71 72 self.treeview.append_column(column) 73 74 self.treeview.set_model(self.model) 75 76 self.treebox.add(self.treeview) 77 # self.treeview.get_selection().connect("changed", lambda x: self.selection_changed()) 78 # self.treeview.connect('button_press_event', self.on_button_press_event) 79 80 self.start_stdin_feed() 81 82 self.window.show_all() 83 84 GObject.timeout_add_seconds(1, self.update_timer_label) 85 86 # def on_button_press_event(self, widget, event): 87 # if event.button == 1: 88 # data=widget.get_path_at_pos(int(event.x),int(event.y)) 89 # if data: 90 # path, column, x, y = data 91 # if column.get_property('title')=="Abort": 92 # iter = self.model.get_iter(path) 93 # repo = self.model.get_value(iter, 0) 94 # self.job_manager.find_and_abort(repo) 95 # elif event.type == Gdk.EventType._2BUTTON_PRESS: 96 # iter = self.model.get_iter(path) 97 # repo = self.model.get_value(iter, 0) 98 # if len(repo.state) == 0: 99 # repoedit.EditRepo(repo.dir, repo.upstream_remote, repo.upstream_branch, repo.push_remote) 100 101 def reset_timer(self): 102 self.start_time = time.time() 103 104 def update_timer_label(self): 105 now = time.time() 106 107 elapsed = now - self.start_time 108 109 time_passed = timedelta(seconds=int(elapsed + .5)) 110 111 self.timer_label.set_text("Elapsed time: {}".format(time_passed)) 112 113 return True 114 115 def start_stdin_feed(self): 116 self.reset_timer() 117 self.cancel_lock = _thread.allocate_lock() 118 self.cancelled = False 119 self.thread = _thread.start_new_thread(self.stdin_feed_thread, ()) 120 121 def stdin_feed_thread(self): 122 for line in sys.stdin: 123 GObject.idle_add(self.handle_line, line) 124 self.cancel_lock.acquire() 125 cancelled = self.cancelled 126 self.cancel_lock.release() 127 if cancelled: 128 break 129 130 _thread.exit() 131 132 def handle_line(self, line): 133 if line[:7] == "GObject": 134 [prefix, name, delta] = line.split(":::") 135 136 [new, target_iter] = self.lookup_name_or_new(name) 137 138 if new: 139 self.model.set_value(target_iter, 0, name) 140 self.model.set_value(target_iter, 1, int(delta)) 141 self.model.set_value(target_iter, 2, int(delta)) 142 else: 143 current_val = self.model.get_value(target_iter, 1) 144 current_val = int(current_val) + int(delta) 145 self.model.set_value(target_iter, 1, current_val) 146 self.model.set_value(target_iter, 2, int(delta)) 147 148 else: 149 self.write_line_to_buffer(line) 150 151 def lookup_name_or_new(self, name): 152 row_iter = self.model.get_iter_first() 153 154 while row_iter != None: 155 existing_name = self.model.get_value(row_iter, 0) 156 if name == existing_name: 157 return [False, row_iter] 158 159 row_iter = self.model.iter_next(row_iter) 160 161 return [True, self.model.insert_before(None, None)] 162 163 164 # def selection_changed(self): 165 # model, treeiter = self.treeview.get_selection().get_selected() 166 # if treeiter: 167 # repo = self.model.get_value(treeiter, 0) 168 # self.current_repo = repo 169 # self.update_branch_combo(repo) 170 # self.clean_button.set_sensitive(len(repo.untracked_files) != 0) 171 # self.reset_button.set_sensitive(repo.is_dirty()) 172 # self.term_button.set_sensitive(True) 173 # self.full_build_button.set_sensitive(True) 174 # self.new_branch.set_sensitive(True) 175 # self.rebase_button.set_sensitive(True) 176 # self.pull_request_button.set_sensitive(True) 177 # no_active = len(repo.state) == 0 178 # self.branch_combo.set_sensitive(no_active) 179 # self.remove_repo_button.set_sensitive(no_active) 180 # self.refresh_button.set_sensitive(no_active) 181 # self.add_repo_button.set_sensitive(no_active) 182 # self.master_button.set_sensitive(no_active and repo.head.reference.name != repo.upstream_branch) 183 184 def on_terminal_clicked(self, button): 185 subprocess.Popen("gnome-terminal", cwd=self.current_repo.dir, shell=True) 186 187 def on_reset_clicked(self, button): 188 self.model.clear() 189 self.reset_timer() 190 191 def write_to_buffer(self, fd, condition): 192 if condition == GLib.IO_IN: 193 char = fd.readline() 194 buf = self.output.get_buffer() 195 iter = buf.get_end_iter() 196 buf.insert(iter, char) 197 iter = buf.get_end_iter() 198 self.output.scroll_to_iter(iter, .2, False, 0, 0) 199 # adj = self.output.get_vadjustment() 200 # if adj.get_value() >= adj.get_upper() - adj.get_page_size() - 200.0: 201 # adj.set_value(adj.get_upper()) 202 return True 203 else: 204 return False 205 206 def write_line_to_buffer(self, string): 207 buf = self.output.get_buffer() 208 iter = buf.get_end_iter() 209 buf.insert(iter, string) 210 iter = buf.get_end_iter() 211 self.output.scroll_to_iter(iter, .2, False, 0, 0) 212 213 def ask(self, msg): 214 dialog = Gtk.MessageDialog(None, 215 Gtk.DialogFlags.DESTROY_WITH_PARENT, 216 Gtk.MessageType.QUESTION, 217 Gtk.ButtonsType.YES_NO, 218 None) 219 dialog.set_default_size(400, 200) 220 dialog.set_markup(msg) 221 dialog.show_all() 222 response = dialog.run() 223 dialog.destroy() 224 return response == Gtk.ResponseType.YES 225 226 def inform_error(self, msg, detail): 227 dialog = Gtk.MessageDialog(None, 228 Gtk.DialogFlags.DESTROY_WITH_PARENT, 229 Gtk.MessageType.ERROR, 230 Gtk.ButtonsType.OK, 231 None) 232 dialog.set_default_size(400, 200) 233 dialog.set_markup(msg) 234 dialog.format_secondary_markup(detail) 235 dialog.show_all() 236 response = dialog.run() 237 dialog.destroy() 238 return 239 240 def inform(self, msg, detail): 241 dialog = Gtk.MessageDialog(None, 242 Gtk.DialogFlags.DESTROY_WITH_PARENT, 243 Gtk.MessageType.INFO, 244 Gtk.ButtonsType.OK, 245 None) 246 dialog.set_default_size(400, 200) 247 dialog.set_markup(msg) 248 dialog.format_secondary_markup(detail) 249 dialog.show_all() 250 response = dialog.run() 251 dialog.destroy() 252 return 253 254if __name__ == "__main__": 255 Main() 256 Gtk.main() 257