djvar94 / gtk-simple-calculator-python3

Just a simple gtk multifactor and multi operator calculator written in Python 3
GNU General Public License v3.0
1 stars 1 forks source link

Convert x, ÷, % symbols to Python operators #7

Open djvar94 opened 4 years ago

djvar94 commented 4 years ago

@Wk-GiHu So I have the operator module imported in my program. I have seen somewhere online someone who suggested to use a dictionary assigning the symbols to the operator functions. So it would be operators = {"+": operator.add, "-": operator.sub, "x": operator.mul, "÷": operator.truediv, "%": operator.mod} So I'm trying to figure how to then convert the symbols from the given user expression to operators that Python can actually understand.

djvar94 commented 4 years ago

@WK-GiHu Now I'm getting this warning in the console result = buffer.set_buffer(calculation) AttributeError: 'TextBuffer' object has no attribute 'set_buffer'

djvar94 commented 4 years ago

@WK-GiHu Hang on, obviously I'm thinking after the expression has been evaluated and therefore done the result will have to be converted from a number (preferably long type) to a string so I'm thinking calculation = str(eval(expression))

WK-GiHu commented 4 years ago

@djvar94

operators = {"+": operator.add, "-": operator.sub, "x": operator.mul, "÷": operator.truediv, "%": operator.mod}

  1. '+' needs not to changed
  2. '-' needs not to changed
  3. 'x' have to be '' do: `expression = expression.replace('x', '')`
  4. '÷' have to be '/' do: expression = expression.replace('÷', '/')
  5. "%" needs not to changed if used as modulo but didn't you want it as percent?

AttributeError: 'TextBuffer' object has no attribute 'set_buffer'

Why don't you use your allready used:

buffer.insert_at_cursor(...

To clear the TextBuffer do:

buffer.delete(start, end)

have to be converted from a number (preferably long type) to a string so I'm thinking calculation = str(eval(expression))

Yes, looks OK.

djvar94 commented 4 years ago

@WK-GiHu Oh yeah. Lol I don't know why I haven't thought of that before about the +, - and insert_at_cursor 😄. Thank you very much nevertheless for your help, I'll give that a try perhaps tomorrow..

djvar94 commented 4 years ago

@WK-GiHu By the way yes, I am trying to do % as (num1 ± num2%)

WK-GiHu commented 4 years ago

@WK-GiHu

yes, I am trying to do % as (num1 ± num2%)

Don't get the meaning of ±? But either way, you have to replace num% with the base Python mat, e.g

100%10 => 100*0.10 
.replace('%10', '*0.10')

or

100%10 => 100/100*10
.replace('%10', /100*10')

This requires to slice the relevant part, %10 from the expression string, compute and then replace it.

Read up on: module-re

Another approach is, to split the whole expression string into single parts and compute this single parts one by one in a chain. E.g.:

100+200/3%10 => [100, '+', 200, '/', 3, '%', 10]

v = 0.0
op = None
for x in [100, '+', 200, '/', 3, '%', 10]:
   if x in  '+-/':
       op = <operater_function x>
   else:
       if x == '%':
           op = <operater_function %>
       else:
           v = float(x)
   if op is not None:
       v = op(v, x)
       op = None

This becomes more an more complex if you have to obey math orders or groups by (). I can imagin, there is already a Python module available which parse a expression string an give the result back.

djvar94 commented 4 years ago

@WK-GiHu By ± I mean + or - say for example 100 - 25% = 75 or if the user puts a + instead of a minus then Python would have to add 25% rather than subtracting it of course, so it would be 100 + 25% = 125.

Seems a bit complicated all that other example you've given, I'm sure too there's probably a module already but will try and give some experimentation nevertheless.

Anyway, changing topic for a little bit, I was just thinking about a project I was working on some months ago that then I gave up because it was far too complicated for me to understand. It was a fun project of a gtk text spammer on Linux but I don't seem to understand how I could have grabbed the focus of any other Linux program that can accept text input say like Firefox, Chrome/Chromium or even gedit, etc etc. Do you know by any chance if that is possible to grab another program's focus from click even in gtk/Python programming? I know this is off topic, but just wanted to know if it's possible or not, tried to ask on stacked change too but got no answer...

djvar94 commented 4 years ago

@WK-GiHu Ok, so I have fixed every operator except for %. That one seems to be a bit tricky for me as it would require a special function dedicated to an operation like num1 - (or +) num2%. Only thing though is that for Python that is not a valid syntax so I've got to use some other way to let the Python interpreter know I want to do that operation somehow. I have updated the code by the way, I'm also thinking of adding ( and ) buttons to the UI for the expression and a back like <-- button to delete one character at a time from the expression field.

Anyway, I have tried to copy and paste that code

100%10 => 1000.10 .replace('%10', '0.10')

and

100+200/3%10 => [100, '+', 200, '/', 3, '%', 10]

v = 0.0
for x in [100, '+', 200, '/', 3, '%', 10]:
   if x in  '+-/':
       op = <operator_function x>
   else:
       if x == '%':
           op = <operator_function %>
       else:
           v = float(x)
   if op is not None:
       v = op(v, x)
       op = None

but it didn't work, I got so many red lines and stuff...:(

WK-GiHu commented 4 years ago

@djvar94

Ok, so I have fixed every operator except for %.

Looks OK

That one seems to be a bit tricky

Yes, you have top split before and after 'xx%', eval the left part, eval the 'xx%' part and than eval both subparts.


By ± I mean + or - say for example 100 - 25% = 75

Ah, yes, makes sense. Therefore you have to adapt the chain logic accordinly.

but it didn't work, I got so many red lines and stuff...:(

Yes, it's not a working example. It's only PSEYDOCODE to show the approach in general.

djvar94 commented 4 years ago

@WK-GiHu right ok.

Yes, you have top split before and after 'xx%', eval the left part, eval the 'xx%' part and than eval both subparts.

The only thing though is how do I split the factors on a variable expression given by the user, is there some sort of loop (like a for loop) I can implement to go through the expression and make the separation automatically, eval them separately and then put them together and eval them again all together? I don't know how to split the expression :/ Usually when it comes to percentage I do (factor /100 *(percentageNumber)) and I get the actual percentage but in this example I don't think it works.

djvar94 commented 4 years ago

@WK-GiHu what about SymPy? Do you think it would be any use? SymPy

WK-GiHu commented 4 years ago

@djvar94

what about SymPy? Do you think it would be any use?

Didn't find a percent usage example. Also didn't find a simple 100 + 200 example. As i understand the use of variables like x is required. I'm unsure to tell about it ...


The only thing though is how do I split the factors on a variable expression given by the user

Post a example string expression, i would like to extend my PSEUDOCODE to a working example.

djvar94 commented 4 years ago

@WK-GiHu Ok, will try that.

djvar94 commented 4 years ago

@WK-GiHu OMG I have just found an answer to another guy's questions on how to use % in Python on Stack Exchange and I quote this guy

def percent(expression):
    if "%" in expression:
        expression = expression.replace("%","/100")
    return eval(expression)

>>> percent("1500*20%")
300.0

I think this could really work. Will try it maybe tomorrow on my code..

WK-GiHu commented 4 years ago

@djvar94

expression.replace("%","/100")

Yes, very simple solution. Give it a try.

djvar94 commented 4 years ago

@WK-GiHu I picked up MATE Calculator to test out the formula first and while that works for multiplication and division, it doesn't work for addition and subtraction, so it's ok for things like 1500x20% or 1500÷20% but not for 1500+20% or 1500-20%. + or - don't return the same result as if you did it on the calculator normally..

WK-GiHu commented 4 years ago

@djvar94

1500+20% or 1500-20%. + or - don't return the same result as if you did it on the calculator normally

1500÷20% should also fail

Expand it to: 1500+20/100, this evaluated to:

  1. 1500 + 20 = 1520
  2. 1520 /100 = 15.20

1500x20% => 1500x20/100

  1. 1500 * 20 = 30000
  2. 30000 / 100 = 300

Should work if you write: 1500-(20%), but it's errorprone to obey such rules.

djvar94 commented 4 years ago

@WK-GiHu well. Again with MATE Calculator even putting parenthesis in doesn't change anything obvious as in maths multiplication and division get executed before additions and subtractions. So it would execute the calculation inside the parenthesis first and then take that result and do the subtraction. The results with + and - that you report are exactly what I get too. It doesn't add 20% it only adds 0.2. is there perhaps another formula to replace to the % that would work both for x, ÷ and +, -?

djvar94 commented 4 years ago

@WK-GiHu

Should work if you write: 1500-(20%), but it's errorprone to obey such rules.

Just tried on my phone it gives the same result as if you did 1500-20/100. It instead gives the correct result of you write it 1500-20% without parenthesis.

WK-GiHu commented 4 years ago

@djvar94

another formula to replace to the % that would work


Should work if you write: 1500-(20%), but it's errorprone to obey such rules. Just tried ...

If you meant: Subtract 20% of 1500 from 1500, then it will fail. But, 1500-(20/100) should compute OK. Therefore man have to clarify the meaning of: x - y%

djvar94 commented 4 years ago

@WK-GiHu Ok, I've updated the code with the expression = expression.replace('%', '/100') and now it does return the correct result ONLY WHEN DOING THE OPERATION IN PARENTHESIS (eg. 1500÷(20%), if I do it without the parenthesis it's completely wrong...NOT THE SAME for multiplication though. And + and - are a no no. So maybe what I'm thinking to do is to say to Python, if there is a * operator before the percentage then behave in a way, otherwise do something else, but it's kind of tricky....why couldn't programmers just assign a damn operator for percentages already. Why have they got to make this simple thing such a hell?

djvar94 commented 4 years ago

@WK-GiHu I think I could add the slicing operator, have a look on this page and scroll down until you see the slicing in the table. operator --- Standard Operators as functions

djvar94 commented 4 years ago

@WK-GiHu ooops. Let's try again operator

WK-GiHu commented 4 years ago

@djvar94

Have a look at

djvar94 commented 4 years ago

@WK-GiHu

Have a look at Order of operations Common_operator_notation Reverse_Polish_notation Percentage List_of_algorithms#Parsing Operator-precedence_parser

I'm afraid this doesn't help me at all as I already knew most of it. I just thought maybe a way to split the expression string into (float) numbers and their respective operators so maybe if we can treat each number individually I can apply the percentage formula better.

found this in the meantime anyway, might help

WK-GiHu commented 4 years ago

@djvar94

a way to split the expression string into (float) numbers and their respective operators

This is described in Reverse_Polish_notation, List_of_algorithms#Parsing and Operator-precedence_parser. Also my proposal here: https://github.com/djvar94/gtk-simple-calculator-python3/issues/7#issuecomment-595824791


found this in the meantime anyway, might help

It's the Python build-in split(), this can handle only split by one single chr.

More powerfull, but also more complicated usage, are: module-re. The re.split(... allows a string expression, e.g. 5x5 + 2 / 3÷2+ 10 % - 1, to be splitted to:

[('', '5', ''), ('*', '5', ''), ('+', '2', ''), ('/', '3', ''), ('÷', '2', ''), ('+', '10', ' %'), ('-', '1', '')]
djvar94 commented 4 years ago

@WK-GiHu oh ok, yes, forgot about your example, will have to give that a try. I see the use of re.split(), thank you for that.

djvar94 commented 4 years ago

@WK-GiHu Ok, so I tried to test the code that you gave me and I get this. Screenshot at 2020-03-13 19-28-25 The red underline says: "[pyflakes] invalid syntax" Screenshot at 2020-03-13 19-30-30

WK-GiHu commented 4 years ago

@djvar94

invalid syntax

It's PSEYDOCODE, see comment

See my Pull Request file SimpleCalculator2.py

djvar94 commented 4 years ago

@WK-GiHu Ok. Some bits were kind of complicated, that pseudocode was not too easy to understand, I sort of get what it does but I certainly wouldn't have been able to get through it on my own, at least not with the knowledge of Python that I have at the moment. I saw your pull request and think I get what it's doing. Will have to get inspiration and try to see if I can make it in my code too. My thought was to define the eval function manually so that it can work with the percentage like you did in your bit. Thanks anyway :) will keep you informed.

djvar94 commented 4 years ago

@WK-GiHu Ok so I followed your pull request and I have updated my code. Now all the calculations are weird. For example if I do 100-20 it should return 80 but I get -20 instead. For addition if I do 100+20 I get 20, if I do 100x20 I get 0.0, if I do 100/20 I get 0.0 and if I do 100-20% I get -0.2. Don't really know why..

WK-GiHu commented 4 years ago

@djvar94

Now all the calculations are weird.

Yes, can reproduce this. The regex fail if the expession starts with more than one digit. Can't explain why, because the used (\d+) should grab as many digits up to the next none digit. But it results with ('1', '00', '') which gives 0 as start value instead of 100.

OK, i have changed the regex to start always with digits. See my updated Pull Request, you have to exchange the whole def eval(...

djvar94 commented 4 years ago

@WK-GiHu Thanks mate, will give it a try

djvar94 commented 4 years ago

@WK-GiHu Ok so I have tried the code and now everything works correctly except for the famous %. If I do 1500-20% it returns instead of the correct value that should be 1200 it now returns 14.8. (I have updated the code based on your pull request commit number 2)

Also if I do 1500-%20 instead of returning the error popup for malformed expression like it should it gives a result (0.1497005988023952)

WK-GiHu commented 4 years ago

@djvar94

everything works correctly except for the famous %.

As i mentiniod earlier, first you have to define the rule. Example: 100 - 10% Do you want: left value minus percent == 100 - 0.1 or do you want: left value minus (left value percent) == 100 - (1000.1)

djvar94 commented 4 years ago

@WK-GiHu Well of course I want as the expression states: left value minus (or plus or whatever) the right value. So yeah, definitively the second example scenario 100 - (100*0.1)

WK-GiHu commented 4 years ago

@djvar94

So yeah, definitively the second example scenario 100 - (100*0.1)

Try, didn't verify it:

r = op(r, mul(r, (float(v[2]) / 100)))
djvar94 commented 4 years ago

@WK-GiHu Nope, still doesn't work. For 100-20% it returns 0.8 instead of 80.

WK-GiHu commented 4 years ago

@djvar94

Hmm, i get 80.0

    e:[('100', '', '', ''), ('-20%', '-', '20', '%')]
    r:100.0
    r:80.0
    buffer:('100-20%', '80.0')

Have you replaced the one in the % branche?

djvar94 commented 4 years ago

Have you replaced the one in the % branche?

@WK-GiHu Yes, I did. That's the only one I replaced.

djvar94 commented 4 years ago

@WK-GiHu uh that is interesting. Whilst it does good with few numbers, I have tried to make a calculation with many factors and operators (but not %) and I get a totally wrong number. In fact the result should even be a negative number, instead I get a really high positive one. You can try it yourself on any good calculator, : 100+30-40x16÷3+45+54-13x28 the result should be −348.333333333 I instead get 15848.0 it seems like it gets confused when you use different operators in the expression

WK-GiHu commented 4 years ago

@djvar94

I get a totally wrong number

Different calculators follow different orders of operations. Read Calculators

This simple calculator is working left to right without any priority given to different operators. Therefore you get 15848.0

Your good calculator uses: 100+30-(40x16)÷3+45+54-(13x28) Results in: −348.333333333

djvar94 commented 4 years ago

@WK-GiHu I see. Although when I was using normal eval (without defining eval function manually) it was doing that alright with correct operator prevalence, only the % wasn't working. That's what I've been trying to fix :(

WK-GiHu commented 4 years ago

@djvar94

You have to implement: orders of operations.

Have a look at GitHub - plusminus, there is a ready to use example BusinessParser with % operator

djvar94 commented 4 years ago

Removed manual eval definition as not working correctly as a calculator should, reverted back to normal eval function