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