Ruby Console hell... no more!
-
Hello all. I am not sure if this post should be under "Plugins" or "developers" but i feel the groundbreaking and enlighting power of the following code better serves a developers eyes!
Ok folks, as you all know i vehemently despise the ruby console (and some other things) so i now intend to put my money where my mouth is -- so to speak! My personal belief is that the Ruby console in SketchUp is completely usless. But at the same time i realize that the SU dev team cannot spend any valuable time making the console better, but i can! I have created a template of sorts for you guys to build from in the form of a Python Tkinter (thats TCL/Tk for those who know) multi-line simplistic Ruby console.
@unknownuser said:
"Why is the Ruby Console so useless JesseJames?"
Thats a good question my inquisitive friend!
Well for starters it confines you to a 'one-line-at-a-time' input. This is very frustrating and renders the console completely useless. I intend to change that and i have included source code! To run this code you will need the awesome Python programming language (Google it!).
My hope is that some fellow Ruby scripter will take this code and write it up with Ruby+Tk or Ruby+wx or whatever. However if one or of you are interested i may be able to make this work with Supy, just let me know...?
The script is just the most basic of multi-line consoles. I could eaisly add...
*Auto Complete
*Auto Indent/Dedent
*Syntax highlight
*your wish hereThe software was created for to reasons...
-To show how easy it is to build GUI applications with Tkinter and Python
-To try and persuade the Sketchup dev team to create a better Ruby Console using this script as a template and release it with SketchUp.But if we could just get this simple muti-line console into Sketchup it would be one thousand times better than the current situation. So now the ball is in your court Ruby masters. I have done the all dirty work. You can help me send the current Ruby console to the Google trash heap where it belongs, enjoy!
# --------------------------------------------------------------------------------- # Copyright 2010 jessejames # # Permission to use, copy, modify, and distribute this software for # any purpose and without fee is hereby granted, provided that the above # copyright notice appear in all copies. # # THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # ---------------------------------------------------------------------------------- # # This software was created for to reasons... # -To show how easy it is to build GUI applications with Tkinter and Python # -To try and persuade the Sketchup dev team to create a better Ruby Console # using this script as a template and release it with SketchUp. # # My personal belief is that the Ruby console in SketchUp is completely usless. # But at the same time i realize that the SU dev team cannot spend any valuable time # making the console better, but i can! You may ask... "Why is the Ruby Console so # usless JesseJames?" Well for starters it confines you to a 'one-line-at-a-time' # input. This is very frustrating and renders the console usless. I intend to change # that and here is my simple solution. To run this code you will need the Python # programming languge (Google it!). My hope is that some fellow Ruby scripter will # take this code and write it up with Ruby+Tk or Ruby+wx or whatever. However if one or # of you are interested i can make this work with Supy, just let me know...? # # This script is just the most basic of multi-line consoles. I could eaisly add... # *Auto Complete # *Auto Indent/Dedent # *Syntax highlight # *your wish here # # But if we could just get this simple muti-line console into Sketchup it would be # one thousand times better than the current situation. Enjoy ;) import sys, traceback import Tkinter as tk from tkMessageBox import showinfo __all__ = ['Cmd'] INSERT = 'insert' END = 'end' SEL = 'sel' SEL_FIRST = 'sel.first' SEL_LAST = 'sel.last' def _showsyntaxerror(self, filename=None); # taken form IDLE type, value, sys.last_traceback = sys.exc_info() sys.last_type = type sys.last_value = value if filename and type is SyntaxError; # Work hard to stuff the correct filename in the exception try; msg, (dummy_filename, lineno, offset, line) = value except; # Not the format we expect; leave it alone pass else; # Stuff in the right filename value = SyntaxError(msg, (filename, lineno, offset, line)) sys.last_value = value list = traceback.format_exception_only(type, value) return ''.join(list) def _showtraceback(self); # taken from IDLE try; type, value, tb = sys.exc_info() sys.last_type = type sys.last_value = value sys.last_traceback = tb tblist = traceback.extract_tb(tb) del tblist[;1] list = traceback.format_list(tblist) if list; list.insert(0, "Traceback (most recent call last);\n") list[len(list);] = traceback.format_exception_only(type, value) finally; tblist = tb = None return ''.join(list) def _selectchars(text, chars='>>> ', back=False, forw=False); if back; start = text.index('insert linestart') stop = '1.0' elif forw; start = text.index(INSERT) stop = text.index(END) else; raise Exception('I cant search forwards *and* backwards you nitwit!') idx = text.search(chars, start, backwards=back, forwards=forw, stopindex=stop) if idx; text.mark_set(INSERT, text.index(idx+' + 4 c')) text.tag_remove('sel', '1.0', END) text.tag_add('sel', 'insert', 'insert lineend') text.see(INSERT) def _getwhite(s); # yes i know in-place concat of a string is slow # but for this it'll do just fine, trust me! And yes # i could use an regexp but i'm writing this code TYVM! ;) S = '' for x in s; if x == ' '; S += ' ' else; return S class _ScrolledText(tk.Text); def __init__(self, master, **kw); self.frame = tk.Frame(master) self.frame.rowconfigure(0, weight=1) self.frame.columnconfigure(0, weight=1) kw.setdefault('undo', 1) kw.setdefault('autoseparators', 1) kw.setdefault('highlightthickness', 1) self.vbar = tk.Scrollbar(self.frame, orient='vertical') tk.Text.__init__(self, self.frame, **kw) self.grid(row=0, column=0, sticky='nswe') self.vbar.configure(command=self.yview) self.config(yscrollcommand=self.vbar.set) self.vbar.grid(row=0, column=1, sticky='ns') self.grid = lambda **kw; self.frame.grid(**kw) def gets(self); return self.get(1.0, END) def sets(self, arg); self.delete(1.0, END) self.insert(END, arg) self.mark_set(INSERT, '1.0') self.see(INSERT) class Cmd(tk.Toplevel); def __init__(self, master, glo, loc, widget=None, **kw); self.glo = glo self.loc = loc self.glo['commandprompt'] = self self.startidx = '1.4' self.v = tk.StringVar() self.pixels = None self.buffer = '' self.saveHeight = None tk.Toplevel.__init__(self, master) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) #self.attributes('-topmost', True) self.attributes('-toolwindow', True) self.transient(master) self.lift(master) self.title('Command Prompt') self.protocol("WM_DELETE_WINDOW", self.onQuit) #-- Widgets --# kw.setdefault('width',60) kw.setdefault('height',10) kw.setdefault('font',('Courier New',10)) kw.setdefault('wrap','word') self.text = _ScrolledText(self, **kw) self.text.grid(row=0, column=0, sticky='nswe') #-- Bindings --# # Kill some default bindings that we don't need. for seq in ('Delete','Button-2','Button-3','B2-Motion', 'B3-Motion','ButtonRelease-2','ButtonRelease-3',); self.text.bind("<%s>"%(seq), lambda e; "break") self.text.bind("<KeyPress>", self.onKeyPress) # Had to bind control-up and control-down directly because # the value for "event.state" keeps changing! ;-? self.text.bind("<Control-Up>", self.onControlUp) self.text.bind("<Control-Down>", self.onControlDown) #-- Setup --# self.text.insert('end', '>>> ') self.focus_set() self.text.focus_set() def onQuit(self); self.grab_release() if self.master; self.master.focus_set() self.destroy() def command_hook(self); # override me please. ;) print 'Cmd.command_hook()' def write(self, arg); self.buffer+=arg def flush(self); if self.buffer; self.out('out', 'blue', '\n'+self.buffer) self.buffer = '' def out(self, name, color, textstr); text = self.text start = text.index('end') text.insert('end', '\n%s' %(textstr)) end = text.index('end - 1 c') text.tag_add(name, start, end) text.tag_configure(name, foreground=color, font=('Courier', 8)) def execute(self, cmd); #XXX need support for sys.exit here! if 'print' in cmd; # `commandprompt` variable was injected in contructor cmd = cmd.replace('print', 'print>>commandprompt, ') print cmd try; e = str(eval(cmd, self.glo, self.loc)) self.out('out', 'blue', e) self.command_hook() except Exception; try; exec(cmd, self.glo, self.loc) self.flush() self.command_hook() except SyntaxError; self.out('err', 'red', _showsyntaxerror(None)) except Exception; self.out('err', 'red', _showtraceback(None)) def onControlUp(self, event); #print 'onControlUp' _selectchars(self.text, back=True) return "break" def onControlDown(self, event); #print 'onControlDown' _selectchars(self.text, forw=True) return "break" def onKeyPress(self, event); text = self.text outOfZone = text.compare(text.index(INSERT), '<', self.startidx) key = event.keysym if key.lower() in 'c' and event.state == 12; # let copy action thru pass elif key == 'F12'; self.onF12() self.text.edit_reset() elif key in ('Up', 'Down', 'Left', 'Right', 'Home', 'End'); pass elif outOfZone; if key == 'Return'; text.tag_remove('sel', '1.0', END) text.insert(END, text.get('insert', 'insert lineend')) text.mark_set(INSERT, END) text.see(END) return "break" elif key == 'Tab'; lineStart, cursor = text.index('insert linestart'), text.index(INSERT) text.insert(INSERT, ' ') return "break" elif key =='BackSpace'; return self.onBackSpace() elif key == 'Return'; return self.onReturn() def onF12(self); #print 'f11' text = self.text start = text.index('insert linestart') if text.get(start) == '>'; start = text.index(start+' - 1 line') while 1; if text.compare(start, '<', '1.0'); #text.compare(start, '<=', '1.0'); break s = text.get(start, start+' lineend') if s.startswith('>>>') or s.startswith('...'); start = text.index(start+'lineend') text.mark_set(INSERT, start) text.delete(start, END) break else; start = text.index(start+' - 1 line') idx = text.search('>>> ', start, forwards=False, backwards=True) if idx; self.startidx = idx+' + 4 c' def onBackSpace(self); text = self.text cursor = text.index(INSERT) curline = text.index('insert linestart') #plus4 = text.index('insert linestart + 4 c') atStart = text.compare(cursor, '==', self.startidx) afterStart = text.compare(cursor, '>=', self.startidx) selected = text.tag_ranges("sel") if text.compare(cursor, '<', self.startidx); return "break" elif atStart and not selected; return "break" elif selected and afterStart; if text.compare(text.index(SEL_FIRST), '<', self.startidx); return "break" text.delete(SEL_FIRST, SEL_LAST) elif afterStart and not selected; if cursor.endswith('.4'); text.delete(curline, text.index('end')) text.delete(text.index('insert - 1 c')) return "break" def onReturn(self); text = self.text lineStart = text.index('insert linestart') lineEnd = text.index('insert lineend') textStart = text.index(self.startidx+' linestart') #startIdx = self.startidx #cursor = text.index(INSERT) linestr = text.get(lineStart, lineEnd) textstr = text.get(textStart, END).rstrip('\n').rstrip(' ').rstrip('\n') cmd = '\n'.join([line[4;] for line in textstr.splitlines()]) if linestr == '>>> ';#text.get('end - 4 c') in ('>>>', '>>> '); self.v.set('Please type a command at the prompt') text.see('end') elif (linestr.rstrip() == '...') or (cmd.count('\n') == 0 and linestr[-1] != ';'); self.execute(cmd) text.insert('end', '\n>>> ') self.text.mark_set(INSERT, END) self.text.see('end') self.startidx = self.text.index(INSERT) text.edit_reset() else; indent = _getwhite(text.get(text.index(lineStart+' + 4 c'), text.index(lineStart+' lineend'))) if linestr[-1] == ';'; indent = ' '+indent text.insert(INSERT, '\n... '+indent) text.see(INSERT) return "break" if __name__ == '__main__'; root = tk.Tk() cp = Cmd(root, globals(), locals()) root.mainloop()
Here is a sample test run so you can see what this ting looks like!
>>> a=1 >>> b=2 >>> a+b 3 >>> 'string' string >>> for x in range(5); ... print x ... 0 1 2 3 4 >>> for x in range(5); ... print x, ... 0 1 2 3 4 >>> for x in range(2,10,2); ... print x, ... 2 4 6 8 >>> def add(x,y); ... return x+y ... >>> add(1,2) 3 >>> class Selection(list); ... def __init__(self); ... list.__init__(self) ... def add(self, arg); ... self.append(arg) ... def remove(arg); ... list.remove(self, arg) ... def clear(self); ... del self[;] ... >>> sel = Selection() >>> sel [] >>> sel.add(1) None >>> sel [1] >>> sel.add('component') None >>> sel [1, 'component'] >>>
-
You got multiline console in the shape of webdialog already: http://forums.sketchucation.com/viewtopic.php?f=323&t=25800
Personally I don't use it. I use the Console for quick little tests - and write plugins in Notepad++.
-
I have not checked it out yet but fully plan on it. Great Job Jesse on getting something posted. I know you've been working on this for a long time (well, I don't know how long you've spent on this exact project, but I know you have been interested in making something like this for a while anyhow). So Congrats,
Chris
-
@chris fullmer said:
I have not checked it out yet but fully plan on it. Great Job Jesse on getting something posted.
@Chris!
You always make me feel very welcome here and that is the kind of people that really deserve to be mods and admins... Thanks! And yes, i had written this script a very long time ago and completely forgot about it until Alex posted his improved web console a few days back. Like thomthom noted Alex's webconsole has multi line capabilities and looks very nice. But some SketchUp folks may wonder...@unknownuser said:
Jesse, why bother to post your multi-line console when it seems that Alex has cornered the market? Is this really a "microcosm" of SketchUp scripting industrial espionage?
Not exactly Joe, i think Alex and i are on the same page but simultainously we are two sides of the same majestic mountain! The major difference between my goals and Alex's goals is that i believe a multi-line console is so important that i very much intend to get a fully functioning (GUI based) multi-line console built-in to SketchUp herself!
Like our good friend thomthom has pointed out, there are many great scripts and IDEs out in the wild. HOWEVER none are built into Sketchup!
@unknownuser said:
So Jesse what you are saying is that currently a scripter' not only has to download a 3rd party console but that any third party console will never be as useful as a built in console...?
No Joe, thats exactly what i am saying!! You see, i have worked with many applications UI's and scripting inputs and i can tell you one thing -- NOTHING is more important to a noobie or professional than the ability to test code snipit's "interactively" in an "intuitive" environment with a GUI based tool that does not force you to swim in the lake of archaic asinine redundancies.
@unknownuser said:
Ok Jesse you put the hook in me and i'm ready to buy the extended warranty, but where do we go from here?
Well Joe a good start would be to get the word out. Show these SketchUp scripters that there is an better way and they have the power to make this a reality. We have a bunch of really smart folks here but they are just lost at sea. We just need to fire up the old light house and show the way home... We can give them hope, but only they can bring about real change.
-
One thing I noticed about the webdialog - you still needed the Console open to see error messages and puts statements. Would this multi-line console avoid that?
-
@thomthom said:
One thing I noticed about the webdialog - you still needed the Console open to see error messages and puts statements. Would this multi-line console avoid that?
Very True, you bring up a very good point thomthom!! Your insights no doubt qualify you as moderator material. No matter how great any Ruby console "we" make is... it shall always and forever suffer the plague of the blacksheep, the outsider, and the village idiot. It will never integrate fully with the SketchUp application. This is what makes any argument for the current console completely and utterly untenable!
Face it guy's scripting SketchUp is painful because of this self imposed bottleneck. We have to be rodeo level window herders to manage this herd of RubyConsoleWindow, SketchUpWindow, SomeThirdPartyIDEWindow, SomeThirdPartyConsoleWindow, and don't forget to check the RubyConsole for stdout and stderrr or don't forget to issue a "load this.rb" command to reload your scripts after every modification.... what a nightmare for pros and a torture chamber for noobs!
Sadly so many of us have taken the "if you can't beat'em, join'em" approach or simply just cannot stomach a fight to fix this problem. Well people it's time to get off your keysters' and join the march!
Only we can fix this. That "WE" is you and me. Even if that means writing the damn code and emailing it to some SU Dev honcho with a "Copy and paste this or else!" letter attached. I guarantee you if we do a little work our selfs and yell loud enough we can do this!
...are you with me?
-
Would now be a bad time to point out i actually quite enjoy scripting in SU?
-
@unknownuser said:
Would now be a bad time to point out i actually quite enjoy scripting in SU?
Great punch line remus . Your keen sense of comic relief no doubt qualifies you as a mod. I always seem to get a good giggle from your posts.
But seriously folks we have an issue here. Some people will say that tring to improve SketchUp is a lost cause. Well that may be slightly true but we cannot give up already. I think a lot of newer scripters (some may never had experianced with scriping before) are now joining the fray but have found major workflow issues that quite frankly are just impossible to overcome alone...
@unknownuser said:
Wait a minute Jesse!
uhhh, excuse me a moment, Yes Joe!
@unknownuser said:
Jesse, why in the world would some people feel that SketchUp scripting (the way it stands now) is a good system? We all know it sucks eggs!
Thats a good question Joe and one has many complicated and reveiling answers. You ever hear the expression "more than meets the eye" Joe? I think for one people just get so accostomed to something that the "fear of change" simply (in their minds) out-weighs the hassles they currently endure. So they just choose to endure it.
What about the "Helsinki Syndrome", have you ever heard of that one Joe? Well some correlations can be drawn between that state of mind and the current state of SketchUp scripters. If you where to ask them privately you'll find that they are just scared Joe, they feel helpless, and like little children they need guidance to free their minds from this hell-state.
For example, lots of people use Microsoft OS even when far better (and free) choices are available. But what is most entertaining is when you ask them WHY? Why do you choose to use a proprietary OS when a number of free alternatives exists? It's like a deer caught in headlights Joe, don't even waste your time.
But sadly there is also evil afoot in the form of FUD mongers and jolly green greedies. Some people have capital gains to be made from SketchUp scripting and they will do anything to keep their piece of the pie even if that means stepping on the little people (thats you and me Joe) to get to where they are going. Sometimes just by making a system uncomfortable you can keep the noobies out and therefore curtail competition. This system has been successfully employed by many major corporations! But we are not here to talk about M$'s evil world domination practices, we are here for the wonderful SketchUp.
@unknownuser said:
This is all very sad Jesse , i'm beginning to lose faith in my fellow SketchUp brothers and sisters.
Yes i know, but i believe in growth Joe, and i believe in change. i believe we can cultivate these confused chaps into productive scripters that produce SketchUp scripts with much ease. No more shall they lament the torturous trappings of the Ruby Console. No more shall they endure 3rd party patch-ware and windowing multiplicity! Heck, they may even start to enjoy this!
Advertisement