Hallo,

if you try to open a g-code file, what is not UTF-8 encoded, the hal_sourceview.py module does throw an UnicodeDecodeError.
This error is not handled in any way!

This does result in an empty sourceview window. The file will work, but no code will be shown.

To test, I do attach 3 ngc files (old files from my lathe, coded in latin1). please just place the files in the nc-files folder and load "hauptprog.ngc" with any GUI using hal_sourceview.py (Glade sourceview) like gscreen or gmoccapy, touchy is doing fine, as it uses no source highlight.

The file content will not been shown.

I found a "work-around" solution, but I am not sure if I should push it, as it would be better to get the encoding from the file instead of trying several ones. The coding may be found using python-magic

|import  magic

blob=  open('unknown-file').read()
m=  magic.open(magic.MAGIC_MIME_ENCODING)
m.load()
encoding=  m.buffer(blob)   # "utf-8" "us-ascii" etc


But magic is not standard, so we would have to add that one to the dependencies.

Please see my changes to hal-sourceview.py in def load_file from line 89 to 116.
The encodings list could easily be enlarged.

At least we should handle that error showing a message to the user, so he 
changes his file to UTF-8 encoding.

Waiting for your comments.

Norbert


|


#!/usr/bin/env python
# -*- coding:UTF-8 -*-
# vim: sts=4 sw=4 et
# GladeVcp actions
#
# Copyright (c) 2011  Pavel Shramov <shra...@mexmat.net>
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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.

import os, time

import gobject, gtk

from hal_widgets import _HalWidgetBase
import linuxcnc
from hal_glib import GStat
from hal_actions import _EMC_ActionBase, _EMC_Action
from hal_filechooser import _EMC_FileChooser

import gtksourceview2 as gtksourceview

class EMC_SourceView(gtksourceview.View, _EMC_ActionBase):
    __gtype_name__ = 'EMC_SourceView'
    def __init__(self, *a, **kw):
        gtksourceview.View.__init__(self, *a, **kw)
        self.filename = None
        self.mark = None
        self.offset = 0
        self.program_length = 0
        self.buf = gtksourceview.Buffer()
        self.buf.set_max_undo_levels(20)
        self.buf.connect('changed', self.update_iter)
        self.set_buffer(self.buf)
        self.lm = gtksourceview.LanguageManager()
        if 'EMC2_HOME' in os.environ:
            path = os.path.join(os.environ['EMC2_HOME'], 'share/gtksourceview-2.0/language-specs/')
            self.lm.set_search_path(self.lm.get_search_path() + [path])

        self.buf.set_language(self.lm.get_language('.ngc'))
        self.set_show_line_numbers(True)
        self.set_show_line_marks(True)
        self.set_highlight_current_line(True)
        self.set_mark_category_icon_from_icon_name('motion', 'gtk-forward')
        self.set_mark_category_background('motion', gtk.gdk.Color('#ff0'))
        self.found_text_tag = self.buf.create_tag(background = "yellow")
        self.update_iter()
        self.connect('button-release-event', self.button_pressed)

    def _hal_init(self):
        _EMC_ActionBase._hal_init(self)
        self.gstat.connect('file-loaded', lambda w, f: gobject.timeout_add(1, self.load_file, f))
        self.gstat.connect('line-changed', self.highlight_line)
        self.gstat.connect('interp_idle', lambda w: self.set_line_number(0))

    def set_language(self, lang, path = None):
        # path = the search path for the langauage file
        # if none, set to default
        # lang = the lang file to set
        if path == None:
            path = os.path.join(os.environ['EMC2_HOME'], 'share/gtksourceview-2.0/language-specs/')
        self.lm.set_search_path(path)
        self.buf.set_language(self.lm.get_language(lang))

    def get_filename(self):
        return self.filename

    # This load the file while not allowing undo buttons to unload the program.
    # It updates the iter because iters become invalid when anything changes.
    # We set the buffer-unmodified flag false after loading the file.
    # Set the hilight line to the line linuxcnc is looking at.
    # if one calls load_file without a filenname, We reload the exisiting file.
    def load_file(self, fn = None):
        self.buf.begin_not_undoable_action()
        if fn == None:
            fn = self.filename
        self.filename = fn
        if not fn:
            self.buf.set_text('')
            return

# # Norberts Changes Start
        # if the file is not UTF-8 coded, we get an error and the text will not be displayed
        # finding the correct encoding from a text file is not every time possible
        # so we first read the text from the file and then we try to decode it.
        raw_text = open(fn).read()
        # this is the list of encodings we try
        encodings = ['utf-8', 'latin1', 'ascii', 'cp1250', 'cp1251', 'cp1252']
        for code in encodings:
            try:
                text = raw_text.decode(code)
            except UnicodeDecodeError:
                print("Got an error trying to decode the file %s with %s" % (fn, code))
                error = True
            else:
                print("Opening file %s with encoding %s)" % (fn, code))
                error = False
                break

        if error:
            text = "The encoding of the file is not supported, \n \
                    please encode the file in \n \
                    UTF-8\n \
                    latin1 or \n \
                    asciii"

        self.buf.set_text(text)

# # Norberts Changes End

        self.buf.end_not_undoable_action()
        self.buf.set_modified(False)
        self.update_iter()
        self.highlight_line(self.gstat, self.gstat.stat.motion_line)
        self.offset = self.gstat.stat.motion_line
        f = file(fn, 'r')
        p = f.readlines()
        f.close()
        self.program_length = len(p)

    # This moves the highlight line to a lower numbered line.
    # useful for run-at-line selection
    def line_down(self):
        self.offset += 1
        self.check_offset()
        self.highlight_line(self.gstat, self.offset)

    # This moves the highlight line to a higher numbered line.
    # useful for run-at-line selection
    def line_up(self):
        self.offset -= 1
        self.check_offset()
        self.highlight_line(self.gstat, self.offset)

    def get_line_number(self):
        return self.offset

    # sets the highlight line to a specified line.
    def set_line_number(self, linenum):
        self.offset = linenum
        self.check_offset()
        self.highlight_line(self.gstat, self.offset)

    def check_offset(self):
        if self.offset < 0:
            self.offset = 0
        elif  self.offset > self.program_length:
            self.offset = self.program_length

    def highlight_line(self, w, l):
        self.offset = l
        if not l:
            if self.mark:
                self.buf.delete_mark(self.mark)
                self.mark = None
            return
        line = self.buf.get_iter_at_line(l - 1)
        if not self.mark:
            self.mark = self.buf.create_source_mark('motion', 'motion', line)
            self.mark.set_visible(True)
        else:
            self.buf.move_mark(self.mark, line)
        self.scroll_to_mark(self.mark, 0, True, 0, 0.5)

    def button_pressed(self, widget, event):
        self.update_iter()

    # iters are invalid (and will cause a complete crash) after any changes.
    # so we have to update them after a change or the user clicks on view with mouse
    # re-establish start and end of text
    # current_iter is the cursor position
    # cancel the last search match
    def update_iter(self, widget = None):
        self.start_iter = self.buf.get_start_iter()
        self.end_iter = self.buf.get_end_iter()
        self.current_iter = self.buf.get_iter_at_mark(self.buf.get_insert())
        self.match_start = self.match_end = None
        start, end = self.buf.get_bounds()
        self.buf.remove_tag(self.found_text_tag, start, end)

    # This will search the buffer for a specified text string.
    # You can search forward or back, with mixed case or exact text.
    # if it searches to either end, if search is pressed again, it will start at the other end.
    # This will grab focus and set the cursor active, while highlighting the line.
    # It automatically scrolls if it must.
    # it primes self.match_start for replacing text
    def text_search(self, direction = True, mixed_case = True, text = "t"):
        CASEFLAG = 0
        if mixed_case:
            CASEFLAG = gtksourceview.SEARCH_CASE_INSENSITIVE
        if direction:
            if self.current_iter.is_end():
                self.current_iter = self.start_iter.copy()
            found = gtksourceview.iter_forward_search(self.current_iter, text, CASEFLAG, None)
        else:
            if self.current_iter.is_start():
                self.current_iter = self.end_iter.copy()
            found = gtksourceview.iter_backward_search(self.current_iter, text, CASEFLAG, None)
        if found:
            self.match_start, self.match_end = found
            self.buf.apply_tag(self.found_text_tag, self.match_start, self.match_end)
            self.buf.select_range(self.match_start, self.match_end)

            if direction:
                self.buf.place_cursor(self.match_start)
                self.grab_focus()
                self.current_iter = self.match_end.copy()
            else:
                self.buf.place_cursor(self.match_start)
                self.grab_focus()
                self.current_iter = self.match_start.copy()
            self.scroll_to_iter(self.match_start, 0, True, 0, 0.5)
            self.set_highlight_current_line(True)
        else:
            self.current_iter = self.start_iter.copy()
            self.set_highlight_current_line(False)
            self.match_start = self.match_end = None

    # check if we already have a match
    # if so and we are replacing-all, delete and insert without individular undo moves
    # if so but not replace-all, delete and insert with individulat undo moves
    # do a search to prime self.match_start
    # if we have gone to the end, stop searching
    # if not replace-all stop searching, otherwise start again
    def replace_text_search(self, direction = True, mixed_case = True, text = "t", re_text = "T", replace_all = False):
        while True:
            if self.match_start:
                if replace_all:
                    self.buf.delete(self.match_start, self.match_end)
                    self.buf.insert_at_cursor(re_text)
                else:
                    self.buf.delete_interactive(self.match_start, self.match_end, True)
                    self.buf.insert_interactive_at_cursor(re_text, True)
            self.text_search(direction, mixed_case, text)
            if self.current_iter.is_start(): break
            if not replace_all: break

    # undo one level of changes
    def undo(self):
        if self.buf.can_undo():
            self.buf.undo()

    # redo one level of changes
    def redo(self):
        if self.buf.can_redo():
            self.buf.redo()

def safe_write(filename, data, mode = 0644):
    import os, tempfile
    fd, fn = tempfile.mkstemp(dir = os.path.dirname(filename), prefix = os.path.basename(filename))
    try:
        os.write(fd, data)
        os.close(fd)
        fd = None
        os.rename(fn, filename)
    finally:
        if fd is not None:
            os.close(fd)
        if os.path.isfile(fn):
            os.unlink(fn)

class EMC_Action_Save(_EMC_Action, _EMC_FileChooser):
    __gtype_name__ = 'EMC_Action_Save'
    __gproperties__ = { 'textview' : (EMC_SourceView.__gtype__, 'Textview',
                    "Corresponding textview widget", gobject.PARAM_READWRITE),
    }
    def __init__(self, *a, **kw):
        _EMC_Action.__init__(self, *a, **kw)
        self.textview = None

    def _hal_init(self):
        _EMC_Action._hal_init(self)

    def on_activate(self, w):
        if not self.textview or not self.textview.filename:
            return
        self.save(self.textview.filename)

    def save(self, fn):
        b = self.textview.get_buffer()
        b.set_modified(False)
        safe_write(fn, b.get_text(b.get_start_iter(), b.get_end_iter()))
        self._load_file(fn)

    def do_set_property(self, property, value):
        name = property.name.replace('-', '_')
        if name == 'textview':
            self.textview = value
        else:
            return _EMC_Action.do_set_property(self, property, value)

    def do_get_property(self, property):
        name = property.name.replace('-', '_')
        if name == 'textview':
            return self.textview
        else:
            return _EMC_Action.do_get_property(self, property)


class EMC_Action_SaveAs(EMC_Action_Save):
    __gtype_name__ = 'EMC_Action_SaveAs'

    def __init__(self, *a, **kw):
        _EMC_Action.__init__(self, *a, **kw)
        self.textview = None
        self.currentfolder = os.path.expanduser("~/linuxcnc/nc_files")

    def on_activate(self, w):
        if not self.textview:
            return
        dialog = gtk.FileChooserDialog(title = "Save As", action = gtk.FILE_CHOOSER_ACTION_SAVE,
                    buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        dialog.set_do_overwrite_confirmation(True)
        dialog.set_current_folder(self.currentfolder)
        if self.textview.filename:
            dialog.set_current_name(os.path.basename(self.textview.filename))
        dialog.show()
        r = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()
        if r == gtk.RESPONSE_OK:
            self.save(fn)
            self.currentfolder = os.path.dirname(fn)
 (Programm parametrisiertes Drehen)

 G18    (XZ Ebene verwenden ; G17 = XY ; G19 = YZ)
 G21    (Einheit mm verwenden ; G20 = inch)
 G40    (Radiuskompensation aus ; G41 = links ; G42 = rechts)
 G49    (Werkzeugl�ngenkompensation G43 = an ; G49 = aus)
 G54    (Koordinatensystem w�hlen ; G54 bis G59 sind unterschiedliche 
Aufspannungen)
 G80    (Zyklen abschalten ; Siehe Bohrzyklen)
 G90    (Distanzen in Absolutma�en ; G91 = incrementale Ma�angaben)
 G94    (Vorschub in mm/min / G95 = mm/U)

 G7     (G8 = Radiusformat / G7 = Durchmesserformat)

 M9     (alle K�hlmittel abschalten)
 M5     (Spindel aus)
 
 T1 M06  (Werkzeug 1 verlangen)
 G43     (Werkzeugl�ngenkompensatuion ein)
 S 2000  (Spindeldrehzahl 2000 U/min)
 F 100   (Vorschubgeschwindigkeit einstellen ; Achtung G94 / G95 beachten)

 M03    (Spindel im Uhrzeigersinn an ; M04 = gegen den Uhrzeigersinn)
 M08    (K�hlung ein ; M07 = Spr�hnebel ; M09 = ale aus)

 (Teilebeschreibung beginnt)

 G64 P0.001
 
 G8 (Radius Modus f�r die Unterprogramme einschalten )

G96 D6000 S150

 F 200
( O<plandrehen> call [Anfangsradius] [Endradius] [Z-Start] [Z-End] 
[Schnitttiefe] [Aufma�] )
 O<plandrehen> call [20.0] [0.0] [0.0] [-2.0] [2.5] [0.0]

G96 D6000 S250

 F 100
( O<langdrehen> call [Anfangsradius] [Endradius] [Z-Start] [Z-End] 
[Schnitttiefe] [Aufma�] )
 O<langdrehen> call [20.0] [9.05] [0.0] [-28.2] [1.5] [0.0]

 G97
 S2500
 F 100
( O<langdrehen> call [Anfangsradius] [Endradius] [Z-Start] [Z-End] 
[Schnitttiefe] [Aufma�] )
 O<langdrehen> call [9.0] [5.22] [0.0] [-18.2] [1.5] [0.0]

 F 100
( O<langdrehen> call [Anfangsradius] [Endradius] [Z-Start] [Z-End] 
[Schnitttiefe] [Aufma�] )
 O<langdrehen> call [5.22] [5.0] [0.0] [-9.2] [1.5] [0.0]

 F 1500
( O<konusaussenschruppen> call [gro�er Radius] [kleiner Radius] [Z-Start] 
[Z-End] [Rohteil-Start-Z] [Schnitttiefe] [Aufma�] )
; O<konusaussenschruppen> call [100.0] [45.0] [0.0] [-105.0] [0.0] [5.0] [1.5]

 F800
( O<aussenradius> call [Radius] [X-Mitte] [Z-Mitte] [X-Start] [X-End] 
[Schnitttiefe] [Aufma�] )
; O<aussenradius> call [50.0] [14.746] [-50] [59.038] [14.746] [5.0] [1.5]
 
 G7 (Durchmesser Modus einschalten )

 (Teilebeschreibung endet)

 M09    (Alle K�hlungen ausschalten)
 M05    (Spindel aus)
 M2     (Programmende und R�cklauf ; M2 = Programm Ende ; M1 = optionaler Stop 
; M0 = Pause)
(Programm zum parametrisierten Laengsdrehen)

( [#1] = Startduchmesser )
( [#2] = Endduchmesser )
( [#3] = Z-Start )
( [#4] = Z-End )
( [#5] = Zustellung je Durchgang )
( [#6] = Schlichaufmass )

( z.B. Abdrehen eines Durchmessers 115 Außen auf 100, von Z 0 auf Z -125 mit 
Zustellung 2.5 )
( und Schlichtaufmass 0.2 )
( Aufruf erfolgt mit O<langdrehen> call [115] [100] [0] [-125] [2.5] [0.2] )

O<langdrehen> sub

        #<aktrad> = #1                   ( Der Variablen 31 den größten 
Durchmessser zuweisen )

        O<Schleife> while [ #<aktrad> gt #2 + #6] 
                                         ( Solange der resultierende Radius 
größer als der große Radius ist )
                G0 X[ #<aktrad> + #5 ]   ( Gehe zu Startdurchmesser + 
Sicherheitsebene )
                G0 Z[ #3 + #5 ]          ( Gehe zur aktuellen Z Position + 
Sicherheitsebene )
                G0 X #<aktrad>           ( Mit Vorschubgeschwindigkeit den 
derzeitigen Startpunkt anfahren )
                G1 Z #4                  ( Längsdrehen bis Z_Ende )
                G0 X[ #<aktrad> + #5 ]   ( Freifahren in X )
                G0 Z[ #3 + #5 ]          ( Auf Z Start zurückfahren )
                #<aktrad> = [ #<aktrad> - #5 ] ( Position für nächsten 
Durchgang neu einstellen )

        O<Schleife> endwhile

        G0 X[ #<aktrad> + #5 ]           ( Gehe zu Startdurchmesser + 
Sicherheitsebene )
        G0 Z[ #3 + #5 ]                  ( Gehe zur aktuellen Z Position + 
Sicherheitsebene )
        
        O<Abfrage> if [#<aktrad> + #5 gt #2 + #6]
                                         ( Wenn noch Material steht, dann den 
Rest wegnehmen )
                G1 X [#2 + #6]
                G1 Z #4
                G0 X[ #2 + #5 ]          ( Gehe zu Startdurchmesser + 
Sicherheitsebene )
                G0 Z[ #3 + #5 ]          ( Gehe zur aktuellen Z Position + 
Sicherheitsebene )

        O<Abfrage> endif
        
        G00 X[ #1 + #5] Z[ #3 + #5 ]     ( Auf Anfang zurück, ist dann 
einfacher im Hauptprog. weiter zu programieren )

O<langdrehen> endsub


(Programm zum parametrisierten Plandrehen)

( [#1] = Startduchmesser )
( [#2] = Endduchmesser )
( [#3] = Z-Start )
( [#4] = Z-End )
( [#5] = Zustellung je Durchgang )
( [#6] = Schlichtaufma� )

( z.B. Abdrehen eines Rohres 40 Au�en, 30 innen um 3 mm mit Zustellung 1 mm )
( und Schlichtaufma� 0.5 )
( Aufruf erfolgt mit O<plandrehen> call [40] [30] [3] [0] [1] [0.5])

O<plandrehen> sub
        
        #<aktrad> = #3                         ( Der Variablen 31 den ZSTART 
Wert zuweisen )

        O<Schleife> while [ #<aktrad> ge #4 + #6 ] 
                                               ( Solange der aktuelle Radius 
gr��er als der Endradius + Schlichtaufma� ist )
                G0 X [ #1 + #5 ]               ( Gehe zu Startdurchmesser + 
Sicherheitsebene )
                G0 Z [ #<aktrad> + #5 ]        ( Gehe zur aktuellen Z Position 
+ Sicherheitsebene )
                G1 Z #<aktrad>                 ( Mit Vorschubgeschwindigkeit 
den derzeitigen Startpunkt anfahren )
                G1 X #1                        ( Mit Vorschubgeschwindigkeit 
den derzeitigen Startpunkt anfahren )
                G1 X #2                        ( Mit Vorschubgeschwindigkeit 
Plandrehen )
                G0 Z [ #<aktrad> + #5 ]        ( Freifahren )
                #<aktrad> = [ #<aktrad> - #5 ] ( Position f�r n�chsten 
Durchgang neu einstellen )

        O<Schleife> endwhile

        O<Abfrage> if [ #<aktrad> + #5 gt #4 + #6 ]
                                               ( Wenn noch Material steht, dann 
den Rest wegnehmen )

                G0 X [ #1 + #5 ]               ( Gehe auf sicheren Durchmesser )
                G0 Z [ #4 + #6 ]                       ( Fahre auf  Z-End )
                G1 X #2                        ( Mit Vorschubgeschwindigkeit 
Plandrehen )
                G0 Z [ #4 + #5 ]               ( Freifahren )
                G0 X [ #1 + #5 ]               ( Freifahren )

        O<Abfrage> endif

        G00 X #1 Z #3             ( Auf Anfang zur�ck, ist dann einfacher im 
Hauptprog. weiter zu programieren )

O<plandrehen> endsub
------------------------------------------------------------------------------
Dive into the World of Parallel Programming! The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net
_______________________________________________
Emc-developers mailing list
Emc-developers@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/emc-developers

Reply via email to