openmv/usr/openmv-ide.py
2014-09-25 18:35:08 +02:00

399 lines
14 KiB
Python
Executable File

#!/usr/bin/env python
import sys, os, os.path
import gtk
import gtksourceview2 as gtksourceview
import gio
import pango
import gobject
import vte
import serial
from time import sleep
import usb.core
import usb.util
import numpy as np
import openmv
from os.path import expanduser
UI_PATH =os.path.dirname(os.path.realpath(__file__))+"/openmv-ide.glade"
CONFIG_PATH = expanduser("~")+"/.openmvide.config"
EXAMPLE_PATH = os.path.dirname(os.path.realpath(__file__))+"/examples"
SCALE =1
class OMVGtk:
def __init__(self):
#Set the Glade file
self.builder = gtk.Builder()
self.builder.add_from_file(UI_PATH)
# get top window
self.window = self.builder.get_object("top_window")
# status bar stuff
self.statusbar = self.builder.get_object("statusbar")
self.statusbar_ctx = self.statusbar.get_context_id("default")
#save toolbutton
self.save_button = self.builder.get_object('save_file_toolbutton')
self.save_button.set_sensitive(False)
#configure gtksourceview
sourceview = self.builder.get_object('gtksourceview')
self.buffer = gtksourceview.Buffer()
mgr = gtksourceview.style_scheme_manager_get_default()
style_scheme = mgr.get_scheme('classic')
if style_scheme:
self.buffer.set_style_scheme(style_scheme)
lang_manager = gtksourceview.language_manager_get_default()
self.buffer.set_highlight_syntax(True)
self.buffer.set_language(lang_manager.get_language("python"))
self.buffer.connect("changed", self.text_changed)
sourceview.set_buffer(self.buffer)
#configure the terminal
self.fd = -1
self.terminal = self.builder.get_object('terminal')
self.terminal.set_size(80,24)
# get drawingarea
self.pixbuf = None
self.drawingarea = self.builder.get_object("drawingarea")
self.da_menu = self.builder.get_object("da_menu")
# selection coords
self.x1 =0
self.y1 =0
self.x2 =0
self.y2 =0
self.selection_started=False
self.sel_ended=False
# set control scales attributes
self.builder.get_object("contrast_adjust").attr= openmv.ATTR_CONTRAST
self.builder.get_object("brightness_adjust").attr= openmv.ATTR_BRIGHTNESS
self.builder.get_object("saturation_adjust").attr= openmv.ATTR_SATURATION
self.builder.get_object("gainceiling_adjust").attr= openmv.ATTR_GAINCEILING
#connect signals
signals = {
"on_top_window_destroy" : self.quit,
"on_connect_clicked" : self.connect_clicked,
"on_reset_clicked" : self.reset_clicked,
"on_execute_clicked" : self.execute_clicked,
"on_stop_clicked" : self.stop_clicked,
"on_motion_notify" : self.motion_notify,
"on_button_press" : self.button_pressed,
"on_button_release" : self.button_released,
"on_open_file" : self.open_file,
"on_save_file" : self.save_file,
"on_save_file_as" : self.save_file_as,
"on_save_template_activate" : self.save_template,
"on_save_descriptor_activate" : self.save_descriptor,
"on_ctrl_scale_value_changed" : self.on_ctrl_scale_value_changed,
"on_zoomin_clicked" : self.zoomin_clicked,
"on_zoomout_clicked" : self.zoomout_clicked,
"on_bestfit_clicked" : self.bestfit_clicked,
"on_updatefb_clicked" : self.updatefb_clicked,
}
self.builder.connect_signals(signals)
self.connected = False
self.builder.get_object('connect_button').set_sensitive(True)
self.builder.get_object('reset_button').set_sensitive(False)
self.builder.get_object('exec_button').set_sensitive(False)
self.builder.get_object('stop_button').set_sensitive(False)
self.builder.get_object('zoomin_button').set_sensitive(False)
self.builder.get_object('zoomout_button').set_sensitive(False)
self.builder.get_object('bestfit_button').set_sensitive(False)
self.builder.get_object('refresh_button').set_sensitive(False)
# open last opened file
if os.path.isfile(CONFIG_PATH):
with open(CONFIG_PATH, "r") as file:
self.file_path = file.read()
if os.path.isfile(self.file_path):
with open(self.file_path, "r") as file:
self.buffer.set_text(file.read())
self.window.set_title(os.path.basename(self.file_path))
else:
self.file_path = None
# build examples menu
if os.path.isdir(EXAMPLE_PATH):
exmenu = gtk.Menu()
example_menu = self.builder.get_object('example_menu')
for f in os.listdir(EXAMPLE_PATH):
if f.endswith(".py"):
label = os.path.basename(f)
mitem =gtk.MenuItem(label)
mitem.connect("activate", self.open_example)
exmenu.append(mitem)
example_menu.set_submenu(exmenu)
def show_message_dialog(self, msg_type, msg):
message = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT,
type=msg_type, buttons=gtk.BUTTONS_OK, message_format=msg)
message.run()
message.destroy()
def connect(self):
self.terminal = self.builder.get_object('terminal')
try:
# open VCP and configure the terminal
self.fd = os.open("/dev/openmvcam", os.O_RDWR)
self.terminal.reset(True, True)
self.terminal.set_size(80,24)
self.terminal.set_pty(self.fd)
except Exception, e:
self.show_message_dialog(gtk.MESSAGE_ERROR, "Faild to connect to OpenMV\n%s"%e)
return
try:
# init openmv
openmv.init()
# interrupt any running code
openmv.stop_script()
sleep(0.1)
except Exception, e:
self.show_message_dialog(gtk.MESSAGE_ERROR, "Faild to connect to OpenMV\n%s"%e)
return
self.connected = True
self.builder.get_object('connect_button').set_sensitive(False)
self.builder.get_object('reset_button').set_sensitive(True)
self.builder.get_object('exec_button').set_sensitive(True)
self.builder.get_object('stop_button').set_sensitive(True)
self.builder.get_object('zoomin_button').set_sensitive(True)
self.builder.get_object('zoomout_button').set_sensitive(True)
self.builder.get_object('bestfit_button').set_sensitive(True)
self.builder.get_object('refresh_button').set_sensitive(True)
def disconnect(self):
try:
# close VCP
os.close(self.fd)
except OSError:
pass
#reset terminal
self.terminal.set_pty(-1)
self.terminal.reset(True, True)
try:
# stop running code
openmv.stop_script();
except:
pass
# release OpenMV
openmv.release()
self.connected = False
self.builder.get_object('connect_button').set_sensitive(True)
self.builder.get_object('reset_button').set_sensitive(False)
self.builder.get_object('exec_button').set_sensitive(False)
self.builder.get_object('stop_button').set_sensitive(False)
self.builder.get_object('zoomin_button').set_sensitive(False)
self.builder.get_object('zoomout_button').set_sensitive(False)
self.builder.get_object('bestfit_button').set_sensitive(False)
self.builder.get_object('refresh_button').set_sensitive(False)
def connect_clicked(self, widget):
self.connect()
def reset_clicked(self, widget):
if (self.connected):
openmv.reset()
def execute_clicked(self, widget):
buf = self.buffer.get_text(self.buffer.get_start_iter(), self.buffer.get_end_iter())
# interrupt any running code
openmv.stop_script()
sleep(0.1)
# exec script
openmv.exec_script(buf)
def stop_clicked(self, widget):
openmv.stop_script();
def zoomin_clicked(self, widget):
global SCALE
SCALE+=1
def zoomout_clicked(self, widget):
global SCALE
if SCALE>1:
SCALE-=1
def bestfit_clicked(self, widget):
global SCALE
SCALE=1
def updatefb_clicked(self, widget):
openmv.fb_update()
def button_pressed(self, widget, event):
self.x1 = int(event.x)
self.y1 = int(event.y)
self.x2 = int(event.x)
self.y2 = int(event.y)
self.selection_started = True
def button_released(self, widget, event):
self.x2 = int(event.x)
self.y2 = int(event.y)
self.selection_started = False
self.da_menu.popup(None, None, None, event.button, event.time, None)
self.da_menu.show_all()
def motion_notify(self, widget, event):
x = int(event.x)
y = int(event.y)
self.x2 = int(event.x)
self.y2 = int(event.y)
if self.pixbuf and x < self.pixbuf.get_width() and y < self.pixbuf.get_height():
pixel = self.pixbuf.get_pixels_array()[y][x]
rgb = "(%d, %d, %d)" %(pixel[0], pixel[1], pixel[2])
self.statusbar.pop(self.statusbar_ctx)
self.statusbar.push(self.statusbar_ctx, rgb)
def update_drawing(self):
if (not self.connected):
return True
try:
# read drawingarea
fb = openmv.fb_dump()
except Exception, e:
self.disconnect()
self.show_message_dialog(gtk.MESSAGE_ERROR, "Faild to update FB\n%s"%e)
return True
if fb:
# convert to RGB888 and blit
self.pixbuf = gtk.gdk.pixbuf_new_from_array(fb[2].reshape((fb[1], fb[0], 3)), gtk.gdk.COLORSPACE_RGB, 8)
self.pixbuf = self.pixbuf.scale_simple(fb[0]*SCALE, fb[1]*SCALE, gtk.gdk.INTERP_BILINEAR)
self.drawingarea.realize();
cm = self.drawingarea.window.get_colormap()
gc = self.drawingarea.window.new_gc(foreground=cm.alloc_color('#FFFFFF',True,False))
self.drawingarea.set_size_request(fb[0]*SCALE, fb[1]*SCALE)
self.drawingarea.window.draw_pixbuf(gc, self.pixbuf, 0, 0, 0, 0)
if self.selection_started or self.da_menu.flags() & gtk.MAPPED:
self.drawingarea.window.draw_rectangle(gc, False, self.x1, self.y1, self.x2-self.x1, self.y2-self.y1)
return True
def on_ctrl_scale_value_changed(self, adjust):
openmv.set_attr(adjust.attr, int(adjust.value))
def open_file(self, widget):
dialog = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
dialog.set_default_response(gtk.RESPONSE_OK)
dialog.set_current_folder("./examples/")
ff = gtk.FileFilter()
ff.set_name("python")
ff.add_pattern("*.py")
dialog.add_filter(ff)
if dialog.run() == gtk.RESPONSE_OK:
with open(dialog.get_filename(), "r") as file:
self.buffer.set_text(file.read())
self.file_path=dialog.get_filename()
self.save_button.set_sensitive(False)
self.window.set_title(os.path.basename(self.file_path))
dialog.destroy()
def open_example(self, widget):
self.file_path = os.path.join(EXAMPLE_PATH, widget.get_label())
with open(self.file_path, "r") as file:
self.buffer.set_text(file.read())
self.window.set_title(os.path.basename(self.file_path))
def save_template(self, widget):
self.da_menu.hide()
x = self.x1
y = self.y1
w = self.x2-self.x1
h = self.y2-self.y1
entry = self.builder.get_object("template_entry")
image = self.builder.get_object("template_image")
image.set_from_pixbuf(self.pixbuf.subpixbuf(x, y, w, h))
dialog = self.builder.get_object("save_template_dialog")
dialog.set_transient_for(self.window);
#dialog.set_default_response(gtk.RESPONSE_OK)
if dialog.run() == gtk.RESPONSE_OK:
openmv.save_template(x/SCALE, y/SCALE, w/SCALE, h/SCALE, entry.get_text()) #Use Scale
dialog.hide()
def save_descriptor(self, widget):
self.da_menu.hide()
x = self.x1
y = self.y1
w = self.x2-self.x1
h = self.y2-self.y1
entry = self.builder.get_object("desc_entry")
image = self.builder.get_object("desc_image")
image.set_from_pixbuf(self.pixbuf.subpixbuf(x, y, w, h))
dialog = self.builder.get_object("save_descriptor_dialog")
dialog.set_transient_for(self.window);
#dialog.set_default_response(gtk.RESPONSE_OK)
if dialog.run() == gtk.RESPONSE_OK:
#if not entry.get_text():
openmv.save_descriptor(x/SCALE, y/SCALE, w/SCALE, h/SCALE, entry.get_text()) #Use Scale
dialog.hide()
def save_file(self, widget):
self.save_button.set_sensitive(False)
with open(self.file_path, "w") as file:
file.write(self.buffer.get_text(self.buffer.get_start_iter(), self.buffer.get_end_iter()))
def save_file_as(self, widget):
dialog = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
dialog.set_default_response(gtk.RESPONSE_OK)
dialog.set_current_folder("./examples/")
ff = gtk.FileFilter()
ff.set_name("python")
ff.add_pattern("*.py")
dialog.add_filter(ff)
if dialog.run() == gtk.RESPONSE_OK:
with open(dialog.get_filename(), "w") as file:
file.write(self.buffer.get_text(self.buffer.get_start_iter(), self.buffer.get_end_iter()))
dialog.destroy()
def text_changed(self, widget):
self.save_button.set_sensitive(True)
def quit(self, widget):
# disconnect
self.disconnect()
# write last opened file
with open(CONFIG_PATH, "w") as file:
file.write(self.file_path)
# exit
sys.exit(0)
if __name__ == "__main__":
omvgtk = OMVGtk()
omvgtk.window.show_all()
gobject.gobject.idle_add(omvgtk.update_drawing);
gtk.main()