#!/usr/bin/python # -*- coding: utf-8 -*- # Quickcal - fast and easy to use calculator with support for filing # Copyright (C) 2017 Nathan SR <cquickcal@gmail.com> # License: # quickcal 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 3 of the License, or # (at your option) any later version. # # quickcal 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. # # You should have received a copy of the GNU General Public License # along with quickcal. If not, see <http://www.gnu.org/licenses/>. # Necessary Imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk from gi.repository import Pango import subprocess import re import os, errno import datetime import time import sys # Following checks for specific characters in inputs, that affects calculations later def check_existence(text): return bool(re.search(r'[A-Za-z\`\~\!\@\#\%\^\&\*\(\)\-\_\=\+\\\|\]\}\[\{\'\"\;\:\/\?\>\<]', text)) def check_existenceii(text): return bool(re.search(r'[\+\-\*\/\%\^\(\)]', text)) def check_existenceiii(text): return bool(re.search(r'[\`\~\!\@\#\&\_\=\\\|\]\}\[\{\'\"\;\:\?\>\<]', text)) # Following executes os commands after replacing any currency symbols in input def execute_command(dc): start = textbuffer.get_start_iter() end = textbuffer.get_end_iter() if check_existence(textbuffer.get_text(start,end,"false")): result.set_text("Input Contains Non Numerical Characters") else: static_command = "echo " + textbuffer.get_text(start,end,"false").replace('\n', ' ').replace('\r', ' ').replace('$', '').replace(',', '').replace('؋', '').replace('.د.ب', '').replace('¢', '').replace('£', '').replace('¥', '').replace('৳', '').replace('฿', '').replace('៛', '').replace('₡', '').replace('₥', '').replace('₦', '').replace('₩', '').replace('₪', '').replace('₫', '').replace('€', '').replace('₭', '').replace('₮', '').replace('₱', '').replace('₲', '').replace('₴', '').replace('₹', '').replace('ƒ', '').replace('د.إ', '').replace('د.ت', '').replace('د.ع', '').replace('د.ك', '').replace('د.م.', '').replace('دج', '').replace('ر.س', '').replace('ر.ع.', '').replace('ر.ق', '').replace('ر.ي', '').replace('ل.د', '') full_command = static_command + dc proc = subprocess.Popen(full_command,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True,universal_newlines=True) (out, err) = proc.communicate() # outwithoutreturn = out.rstrip('\n') if err: result.set_text("Input Contains Non Numerical Characters") else: result.set_text(out.rstrip('\n')) def execute_commandii(): start = textbuffer.get_start_iter() end = textbuffer.get_end_iter() if not check_existenceii(textbuffer.get_text(start,end,"false")): result.set_text("BM needs any of +,-,*,/,%,^,() operators") elif check_existenceiii(textbuffer.get_text(start,end,"false")): result.set_text("Input Contains Invalid Characters") else: full_command = "awk \"BEGIN {printf \\\"%." + str(spinbutton.get_value_as_int()) + "f\\\", " + textbuffer.get_text(start,end,"false").replace('\n', ' ').replace('\r', ' ').replace('$', '').replace(',', ',').replace('؋', '').replace('.د.ب', '').replace('¢', '').replace('£', '').replace('¥', '').replace('৳', '').replace('฿', '').replace('៛', '').replace('₡', '').replace('₥', '').replace('₦', '').replace('₩', '').replace('₪', '').replace('₫', '').replace('€', '').replace('₭', '').replace('₮', '').replace('₱', '').replace('₲', '').replace('₴', '').replace('₹', '').replace('ƒ', '').replace('د.إ', '').replace('د.ت', '').replace('د.ع', '').replace('د.ك', '').replace('د.م.', '').replace('دج', '').replace('ر.س', '').replace('ر.ع.', '').replace('ر.ق', '').replace('ر.ي', '').replace('ل.د', '') + "}\"" proc = subprocess.Popen(full_command,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True,universal_newlines=True) (out, err) = proc.communicate() # outwithoutreturn = out.rstrip('\n') if err: result.set_text("Input Contains Invalid Characters") else: result.set_text(out) # Following functions creates folder, does filing and viewing of files def create_directory(myfolder): try: os.makedirs(myfolder) # print "Successfully Created : " + myfolder except OSError as e: if e.errno != errno.EEXIST: result.set_text("Failed Creating quickcal Home Folder") def fileit(): from datetime import datetime datestring = datetime.strftime(datetime.now(), '%Y-%m-%d') try: f = open(path + '/' + 'Inputs_' + datestring + '.txt', 'a+') start = textbuffer.get_start_iter() end = textbuffer.get_end_iter() f.write ('\n' + '\n' + textbuffer.get_text(start,end,"false")) f.close() g = open(path + '/' + 'Results_' + datestring + '.txt', 'a+') g.write ('\n' + '\n' + result.get_text()) g.close() result.set_text("Filed Successfully") except IOError: result.set_text("Failed Writing to Inputs / Results file") def viewit(file): from datetime import datetime datestring = datetime.strftime(datetime.now(), '%Y-%m-%d') subprocess.call(["xdg-open", path + '/' + file + datestring + '.txt']) # if sys.platform == 'linux2': # subprocess.call(["xdg-open", path + '/' + file + datestring + '.txt']) # else: # os.startfile(path + '/' + file + datestring + '.txt') class Handler: # Calculator buttons and their respective clicked actions. Calls the functions above with their respective parameters. def basicbutton(self, button): execute_commandii() def sumbutton(self, button): dynamic_command = " | awk '{ for(i=1; i<=NF;i++) j+=$i; printf \"%." + str(spinbutton.get_value_as_int()) + "f\", j; j=0 }'" execute_command(dynamic_command) def averagebutton(self, button): dynamic_command = " | awk '{ for(i=1; i<=NF;i++) j+=$i; printf \"%." + str(spinbutton.get_value_as_int()) + "f\", j/NF; j=0 }'" execute_command(dynamic_command) def countbutton(self, button): dynamic_command = " | awk '{ printf NF }'" execute_command(dynamic_command) def minimumbutton(self, button): dynamic_command = " | awk '{m=$1;for(i=1;i<=NF;i++)if($i<m)m=$i;printf m}'" execute_command(dynamic_command) def maximumbutton(self, button): dynamic_command = " | awk '{m=$1;for(i=1;i<=NF;i++)if($i>m)m=$i;printf m}'" execute_command(dynamic_command) def rangebutton(self, button): dynamic_command = " | awk '{{min=max=$i};for(i=1;i<=NF;i++){if($i>max) {max=$i} if($i<min) {min=$i}} } END {printf \"%." + str(spinbutton.get_value_as_int()) + "f\", max-min}'" execute_command(dynamic_command) def stddevbutton(self, button): dynamic_command = " | awk '{ A=0; V=0; for(N=1; N<=NF; N++) A+=$N ; A/=NF ; for(N=1; N<=NF; N++) V+=(($N-A)*($N-A))/(NF-1); printf \"%." + str(spinbutton.get_value_as_int()) + "f\", sqrt(V) }'" execute_command(dynamic_command) def comboboxtext1_changed(self, comboboxtext): dynamic_command = " | num " + ourcomboboxtext.get_active_text() + " OFMT=\"%." + str(spinbutton.get_value_as_int()) + "f\"" execute_command(dynamic_command) def clearbutton(self, button): textbuffer.set_text("") result.set_text("") textview.grab_focus() def detailsbutton(self, button): ouraboutwindow.set_transient_for(window) ouraboutwindow.run() ouraboutwindow.hide() # ouraboutwindow.destroy() def fileitbutton(self, button): fileit() def view_todays_inputs_button(self, button): viewit("Inputs_") def view_todays_results_button(self, button): viewit("Results_") builder = Gtk.Builder() builder.add_from_file("/usr/lib/quickcal/quickcal.glade") builder.connect_signals(Handler()) window = builder.get_object("window1") textview = builder.get_object("textview1") textbuffer = textview.get_buffer() textview.modify_font(Pango.FontDescription('Sans-serif 14')) ourcomboboxtext = builder.get_object("comboboxtext1") # Following specifies the combo box text values and populates the same default_text = ["Absolute-Value", "Coefficient-Of-Variance", "Count", "First", "Fourth-Moment-About-The-Mean", "Frequency-Maximum", "Frequency-Minimum", "Increment", "Interquartile-Range", "Is-Ascending", "Is-Descending", "Is-Non-Ascending", "Is-Non-Descending", "Is-Strictly-Ascending", "Is-Strictly-Descending", "Is-Unique", "Kurtosis", "Last", "Maximum", "Mean", "Mean-Absolute-Deviation", "Meanest", "Median", "Median-High", "Median-Low", "Minimum", "Mode-High", "Mode-Min", "Modes", "Normalize", "Population-Coefficient-Of-Variance", "Population-Kurtosis", "Population-Skewness", "Population-Standard-Deviation", "Population-Variance", "Product", "Quartile-0", "Quartile-1", "Quartile-2", "Quartile-3", "Quartile-4", "Range", "Round", "Round-Down", "Round-Off", "Round-Up", "Sample-Coefficient-Of-Variance", "Sample-Kurtosis", "Sample-Skewness", "Sample-Standard-Deviation", "Sample-Variance", "Second-Moment-About-The-Mean", "Sign", "Skewness", "Sort", "Sort-Ascending", "Sort-Descending", "Standard-Deviation", "Sum", "Sum-Of-Cubes", "Sum-Of-Quads", "Sum-Of-Squares", "Third-Moment-About-The-Mean", "Trimean", "Variance"] for x in default_text: ourcomboboxtext.append_text(x) spinbutton = builder.get_object("spinbutton1") result = builder.get_object("result1") result.modify_font(Pango.FontDescription('Sans-serif 14')) ouraboutwindow = builder.get_object("aboutdialog1") # Following checks / creates the quickcal home folder home=os.environ["HOME"] path=os.path.join(home,"quickcal") # print path create_directory(path) window.connect("delete-event", Gtk.main_quit) window.show_all() Gtk.main()