# -*- coding: utf-8 -*- """ $Id$ Copyright 2011 Lars Kruse <devel@sumpfralle.de> This file is part of PyCAM. PyCAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PyCAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with PyCAM. If not, see <http://www.gnu.org/licenses/>. """ import os import datetime import pycam.Plugins import pycam.Utils class Log(pycam.Plugins.PluginBase): UI_FILE = "log.ui" DEPENDS = ["Clipboard"] CATEGORIES = ["System"] def setup(self): if self.gui: import gtk self._gtk = gtk # menu item and shortcut log_action = self.gui.get_object("ToggleLogWindow") self._gtk_handlers = [] self._gtk_handlers.append((log_action, "toggled", self.toggle_log_window)) self.register_gtk_accelerator("log", log_action, "<Control>l", "ToggleLogWindow") self.core.register_ui("view_menu", "ToggleLogWindow", log_action, 100) # status bar self.status_bar = self.gui.get_object("StatusBar") event_bar = self.gui.get_object("StatusBarEventBox") self._gtk_handlers.append((event_bar, "button-press-event", self.toggle_log_window)) event_bar.unparent() self.core.register_ui("main_window", "Status", event_bar, 100) # "log" window self.log_window = self.gui.get_object("LogWindow") self.log_window.set_default_size(500, 400) hide_window = lambda *args: self.toggle_log_window(value=False) self._gtk_handlers.extend([ (self.log_window, "delete-event", hide_window), (self.log_window, "destroy", hide_window), (self.gui.get_object("LogWindowClose"), "clicked", hide_window), (self.gui.get_object("LogWindowClear"), "clicked", self.clear_log_window), (self.gui.get_object("LogWindowCopyToClipboard"), "clicked", self.copy_log_to_clipboard)]) self.log_model = self.gui.get_object("LogWindowList") # window state self._log_window_position = None # register a callback for the log window pycam.Utils.log.add_hook(self.add_log_message) self.register_gtk_handlers(self._gtk_handlers) return True def teardown(self): if self.gui: self.log_window.hide() log_action = self.gui.get_object("ToggleLogWindow") self.core.unregister_ui("view_menu", log_action) self.unregister_gtk_accelerator("log", log_action) self.core.unregister_ui("main_window", self.gui.get_object("StatusBarEventBox")) self.core.unregister_ui("view_menu", self.gui.get_object("ToggleLogWindow")) self.unregister_gtk_handlers(self._gtk_handlers) # TODO: disconnect the log handler def add_log_message(self, title, message, record=None): timestamp = datetime.datetime.fromtimestamp( record.created).strftime("%H:%M") # avoid the ugly character for a linefeed message = " ".join(message.splitlines()) try: message = message.encode("utf-8") except UnicodeDecodeError: # remove all non-ascii characters clean_char = lambda c: (32 <= ord(c) < 128) and c or " " message = "".join([clean_char(char) for char in message]) self.log_model.append((timestamp, title, message)) # update the status bar (if the GTK interface is still active) if not self.status_bar.window is None: # remove the last message from the stack (probably not necessary) self.status_bar.pop(0) # push the new message try: self.status_bar.push(0, message) except TypeError: new_message = re.sub("[^\w\s]", "", message) self.status_bar.push(0, new_message) # highlight the "warning" icon for warnings/errors if record and record.levelno > 20: self.gui.get_object("StatusBarWarning").show() def copy_log_to_clipboard(self, widget=None): content = [] def copy_row(model, path, it, content): columns = [] for column in range(model.get_n_columns()): columns.append(model.get_value(it, column)) content.append(" ".join(columns)) self.log_model.foreach(copy_row, content) self.core.get("clipboard-set")(os.linesep.join(content)) self.gui.get_object("StatusBarWarning").hide() def clear_log_window(self, widget=None): self.log_model.clear() self.gui.get_object("StatusBarWarning").hide() def toggle_log_window(self, widget=None, value=None, action=None): toggle_log_checkbox = self.gui.get_object("ToggleLogWindow") checkbox_state = toggle_log_checkbox.get_active() if value is None: new_state = checkbox_state elif isinstance(value, self._gtk.gdk.Event): # someone clicked at the status bar -> toggle the window state new_state = not checkbox_state else: if action is None: new_state = value else: new_state = action if new_state: if self._log_window_position: self.log_window.move(*self._log_window_position) self.log_window.show() else: self._log_window_position = self.log_window.get_position() self.log_window.hide() toggle_log_checkbox.set_active(new_state) self.gui.get_object("StatusBarWarning").hide() # don't destroy the window with a "destroy" event return True