#!/usr/bin/env python3 # GladeVcp Widget - calculator input # This widgets allows simple calculations. # The result can be returned for further use. # Originally to be used as numerical input on a touch screen. # # Copyright (c) 2012 Chris Morley # # 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 sys, os import math # localization import locale locale.setlocale( locale.LC_ALL, '' ) datadir = os.path.abspath( os.path.dirname( __file__ ) ) BASE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) LOCALEDIR = os.path.join(BASE, "share", "locale") locale.bindtextdomain("linuxcnc", LOCALEDIR) import gi gi.require_version("Gtk","3.0") from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject from gi.repository import Pango class Calculator( Gtk.VBox ): __gtype_name__ = 'Calculator' __gproperties__ = { 'is_editable' : ( GObject.TYPE_BOOLEAN, 'Is Editable', 'Ability to use a keyboard to enter data', False, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT ), 'font' : ( GObject.TYPE_STRING, 'Pango Font', 'Display font to use', "sans 12", GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT ), } __gproperties = __gproperties__ def __init__( self, *a, **kw ): Gtk.VBox.__init__( self, *a, **kw ) self.preset_value = None self.eval_string = "" self.font = "sans 12" self.is_editable = False self.integer_only = False self.has_num_pad_only = False self.wTree = Gtk.Builder() self.wTree.set_translation_domain("linuxcnc") # for locale translations self.wTree.add_from_file( os.path.join( datadir, "calculator.glade" ) ) dic = { "on_displayText_activate" : self.displayText, "on_displayText_changed" : self.displayText_changed, "on_CLR_clicked" : self.displayClr, "on_Pi_clicked" : self.displayPi, "on_Left_bracket_clicked" : self.displayLeftBracket, "on_Right_bracket_clicked" : self.displayRightBracket, "on_Seven_clicked" : self.displaySeven, "on_Eight_clicked" : self.displayEight, "on_Nine_clicked" : self.displayNine, "on_Divide_clicked" : self.displayDiv, "on_Four_clicked" : self.displayFour, "on_Five_clicked" : self.displayFive, "on_Six_clicked" : self.displaySix, "on_Multiply_clicked" : self.displayMultiply, "on_One_clicked" : self.displayOne, "on_Two_clicked" : self.displayTwo, "on_Three_clicked" : self.displayThree, "on_Minus_clicked" : self.displayMinus, "on_Zero_clicked" : self.displayZero, "on_Dot_clicked" : self.displayDot, "on_Equal_clicked" : self.displayEqual, "on_Add_clicked" : self.displayAdd, "on_Backspace_clicked" : self.displayBackspace, "on_mm_inch_clicked" : self.displayMmInch, "on_inch_mm_clicked" : self.displayInchMm, "on_ok_button_clicked" : self.on_ok_button_clicked, "on_cancel_button_clicked" : self.on_cancel_button_clicked } self.wTree.connect_signals( dic ) self.entry = self.wTree.get_object( "displayText" ) self.entry.modify_font( Pango.FontDescription( self.font ) ) self.calc_box = self.wTree.get_object( "calc_box" ) self.calc_box.set_vexpand(True) self.calc_box.set_hexpand(True) self.table = self.wTree.get_object("table1") self.window = self.calc_box.get_parent() self.window.remove(self.calc_box) self.add(self.calc_box) # Use CSS style for buttons screen = Gdk.Screen.get_default() provider = Gtk.CssProvider() style_context = Gtk.StyleContext() style_context.add_provider_for_screen(screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) css = b"button {padding: 0;}" provider.load_from_data(css) def num_pad_only( self, value ): self.has_num_pad_only = value objects = ["Left_bracket", "Right_bracket", "Pi", "Divide", "Multiply", "Add", "Minus", "Equal", "Inch_mm", "mm_Inch", "Backspace", "CLR", "Inch_mm", "mm_Inch", "cancel_button", "ok_button"] if value: for i in objects: self.table.remove(self.wTree.get_object(i)) if self.integer_only: col_offs = 0 else: col_offs = 1 self.table.resize(5, 3+col_offs) # reorder items to fit size-reduced table self.table.attach(self.wTree.get_object("Backspace"), 1, 2, 0, 1) self.table.attach(self.wTree.get_object("CLR"), 2, 3, 0, 1) self.table.attach(self.wTree.get_object("cancel_button"), 1+col_offs, 2+col_offs, 4, 5) self.table.attach(self.wTree.get_object("ok_button"), 2+col_offs, 3+col_offs, 4, 5) self.show_all() def integer_entry_only( self, value ): if value: self.table.remove(self.wTree.get_object('Dot')) self.integer_only = True else: self.integer_only = False def set_editable( self, value ): self.is_editable = value self.entry.set_editable( value ) def set_font( self, font ): self.font = font self.entry.modify_font( Pango.FontDescription( font ) ) def set_value( self, value ): val = value try: val = str( locale.format_string( "%f", float( val ) ).rstrip( "0" ) ) if val[-1] == locale.localeconv()["decimal_point"]: val = val.rstrip( locale.localeconv()["decimal_point"] ) except: value = "Error" self.delete() self.displayOperand( str( val ) ) self.preset_value = value def get_value( self ): self.compute() try: value = self.wTree.get_object( "displayText" ).get_text() return locale.atof( value ) except: return None # print( "value in get value = ", value ) # print( "converted value in get value = ", locale.atof( value ) ) def get_preset_value( self ): return self.preset_value def compute( self ): qualified = '' # print"string:",self.eval_string temp = self.eval_string.strip( " " ).replace("Pi", "math.pi") # this loop adds only spaces around the mentioned operators for i in( '-', '+', '/', '*', 'math.pi', '(', ')' ): new = " %s " % i temp = temp.replace( i, new ) for i in temp.split(): # print ( "i in compute = ", i ) try: i = str( locale.atof( i ) ) # print ( "converted i in compute = ", i ) except: pass if i.isdigit(): qualified = qualified + str( float( i ) ) else: qualified = qualified + i try : if self.integer_only: b = str( int( eval( qualified ) ) ) else: b = str( eval( qualified ) ) except: b = "Error" print("Calculator widget error, string:", self.eval_string, sys.exc_info()[0]) self.eval_string = '' else : self.eval_string = b # if locale.localeconv()["decimal_point" = comma , # we have to replace the internal dot by a comma, # otherwise it will be interpreted as an thousend separator try: b = locale.format_string( "%f", float( b ) ).rstrip( "0" ) if b[-1] == locale.localeconv()["decimal_point"]: b = b.rstrip( locale.localeconv()["decimal_point"] ) except: b = "Error" self.wTree.get_object( "displayText" ).set_text( b ) self.eval_string = b + " " # add space to indicate that calculation was last def delete( self ): self.eval_string = '' self.wTree.get_object( "displayText" ).set_text( "" ) def displayOperand( self, i ): if self.wTree.get_object( "displayText" ).get_selection_bounds(): self.delete() if "Error" in self.eval_string: self.eval_string = "" # clear text area when entering a number after a finished calculation #if i.isdigit(): if i not in "+-*/" and self.eval_string != "": if self.eval_string[-1] == " ": self.eval_string = "" self.eval_string = self.eval_string + i self.wTree.get_object( "displayText" ).set_text( str( self.eval_string ) ) def displayText_changed( self, widget ): self.eval_string = widget.get_text() def displayText( self, widget ): pass # self.compute() def displayClr( self, widget ): self.delete() def displayBackspace( self, widget ): text = self.wTree.get_object( "displayText" ).get_text() if(text == "Error"): self.delete() else: if text[-2:] == "Pi": self.wTree.get_object( "displayText" ).set_text(text[:-2]) else: self.wTree.get_object( "displayText" ).set_text(text[:-1]) def displayLeftBracket( self, widget ): self.displayOperand( "(" ) def displayRightBracket( self, widget ): self.displayOperand( ")" ) def displaySeven( self, widget ): self.displayOperand( "7" ) def displayEight( self, widget ): self.displayOperand( "8" ) def displayNine( self, widget ): self.displayOperand( "9" ) def displayFour( self, widget ): self.displayOperand( "4" ) def displayFive( self, widget ): self.displayOperand( "5" ) def displaySix( self, widget ): self.displayOperand( "6" ) def displayOne( self, widget ): self.displayOperand( "1" ) def displayTwo( self, widget ): self.displayOperand( "2" ) def displayThree( self, widget ): self.displayOperand( "3" ) def displayZero( self, widget ): self.displayOperand( "0" ) def displayDot( self, widget ): self.displayOperand( locale.localeconv()["decimal_point"] ) def displayPi( self, widget ): self.displayOperand( "Pi" ) def displayDiv( self, widget ): self.displayOperand( "/" ) def displayMultiply( self, widget ): self.displayOperand( "*" ) def displayMinus( self, widget ): self.displayOperand( "-" ) def displayEqual( self, widget ): self.compute() def displayAdd( self, widget ): self.displayOperand( "+" ) def displayMmInch( self, widget ): self.eval_string = "("+ self.eval_string + ") / " + locale.format("%f", float(25.4)) self.compute() def displayInchMm( self, widget ): self.eval_string = "("+ self.eval_string + ") * " + locale.format("%f", float(25.4)) self.compute() def on_ok_button_clicked ( self, widget ): dialog = self.calc_box.get_toplevel() dialog.response(Gtk.ResponseType.ACCEPT) def on_cancel_button_clicked ( self, widget ): dialog = self.calc_box.get_toplevel() dialog.response(Gtk.ResponseType.REJECT) def do_get_property( self, property ): name = property.name.replace( '-', '_' ) if name in list(self.__gproperties.keys()): return getattr( self, name ) else: raise AttributeError( 'unknown property %s' % property.name ) def do_set_property( self, property, value ): try: name = property.name.replace( '-', '_' ) if name == 'is_editable': self.set_editable( value ) if name == 'font': self.set_font( value ) except: pass # for testing without glade editor: def main(): window = Gtk.Dialog( "My dialog", None, modal=True, destroy_with_parent=True ) calc = Calculator() window.vbox.add( calc ) window.connect( "destroy", Gtk.main_quit ) calc.set_value( 2.5 ) calc.set_font( "sans 25" ) calc.set_editable( True ) window.show_all() response = window.run() if response == Gtk.ResponseType.ACCEPT: print(calc.get_value()) else: print(calc.get_preset_value()) if __name__ == "__main__": main()