Calculator-Eingabefeld für Windows

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Pollux
Beiträge: 23
Registriert: 23.09.2010 18:46
Computerausstattung: Eigenbau: CPU Intel i7-960 Windows 7 64-Bit 12 GB RAM
Toshiba Notebook Windows XP 32-Bit
weitere
Wohnort: Gilching bei München

Calculator-Eingabefeld für Windows

Beitrag von Pollux »

In Excel kann man in ein Feld, in das man eigentlich eine Zahl eingeben soll, auch einen Rechenausdruck eingeben, der die Zahl dann liefert. Beispiel: Gesamtkosten-Feld: "374*4+7*(83+99)-3*128". Das erspart einem die Suche nach dem Taschenrechner. Will man so ein Eingabefeld in seinem eigenen PureBasic-Programm realisieren, müßte man die Zeichenfolge analysieren, feststellen welche Zahlen und Operatoren vorkommen und dann in der richtigen Reihenfolge die Rechenoperationen ausführen (Punktrechnung vor Strichrechnung). Das dürfte ziemlich auswendig werden. In dem folgenden Code-Schnipsel möchte ich einen Vorschlag machen, wie man das einfacher machen kann.

Das Verfahren beruht auf der Fähigkeit der neuen PowerShell, direkt im Eingabeprompt zu rechnen. Es wird also mit RunProgram() einfach der Eingabe-String an die PowerShell übergeben. Die PowerShell ist in Windows 7 eingebaut (sogar in Verson 1 und 2). Ab XP kann sie kostenlos nachgerüstet werden (Version 1 ?). Von Administratoren wird die PowerShell aber gerne für den Normal-Nutzer eingeschränkt. Aber auf dem Rechner zuhause sollte es gehen.

Das Programm beeped zweimal. Das erste mal, wenn die PowerShell aufgerufen wird, und das zweite mal, wenn es von der PowerShell wieder zurückkommt. Bei mir dauert es etwa 1 Sekunde, bis das Ergebnis da ist.

Zunächst das Include-File 'CalculatorFeld.pbi':

Code: Alles auswählen

;***********************************************************************************************************************
;* Dieses Modul enthält den eigentlichen Kalkulations-Aufruf.                                                          *
;* ------------------------------------------------------------------------------------------------------------------- *
;* Entwickelt für PureBasic 4.5 von: Jürgen Priess (09.2010)                                                           *
;***********************************************************************************************************************
EnableExplicit

Procedure.s DoCalculation(Ausdruck.s="")
;***********************************************************************************************************************
;* Der Rechen-Ausdruck wird berechnet, indem er der Windows PowerShell übergeben wird. Diese ist ab Windows 7 bereits  *
;* im Betriebssystem integriert. Nutzer von XP und Vista können die PowerShell (Version 1 ?) kostenlos nachrüsten. In  *
;* Windows 7 sind beide Versionen vorhanden - hier wird aber nur die Version 1 genutzt.                                *
;* ------------------------------------------------------------------------------------------------------------------- *
;* Den String, der die Program-Variable initialisiert, wurde so aus dem Eigenschaftsfenster der PowerShell übernommen. *
;* Gegebenenfalls muß 'powershell.exe' gesucht werden und der vollständige Pfad angepaßt werden.                       *
;***********************************************************************************************************************
Protected ProgramId.l  = 0
Protected Program.s    = "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" ;gegebenenfalls anpassen
Protected SystemRoot.s = GetEnvironmentVariable("SystemRoot")
Protected Result.s     = ""
Protected Flags.l      = #PB_Program_Open | #PB_Program_Read | #PB_Program_Hide
Protected I.l          = 0
Protected Zeichen.s    = ""

Ausdruck = Trim(Ausdruck) ;führende und nachfolgende Leerzeichen entfernen

;Ausdruck auf ungültige Zeichen hin untersuchen (es soll nicht 'dir *.*' eingegeben werden können)
For I = 1 To Len(Ausdruck)
  Zeichen = Mid(Ausdruck,I,1)
  If 0 = FindString(" ()*+-./0123456789Ee",Zeichen,1) 
    MessageRequester("Problem!","Zeichen '" + Zeichen + "' ist nicht erlaubt!")
    ProcedureReturn Result 
  EndIf
Next I

If 0 < Len(Ausdruck) ;nur wenn noch was drinsteht wird gerechnet 
  
  Beep_(880,200)    
  
  Program = ReplaceString(Program,"%SystemRoot%",SystemRoot,#PB_String_NoCase) ;ersetzen des SystemRoot-Verzeichnisses
  
  ProgramId = RunProgram(Program,Ausdruck,"",Flags) ;Aufruf der PowerShell
  
  If ProgramId  
    
    While ProgramRunning(ProgramId)
      If AvailableProgramOutput(ProgramId)
        Result + ReadProgramString(ProgramId)
      EndIf
    Wend
    
    If 0 < ProgramExitCode(ProgramId)
      MessageRequester("Problem!",Result)
      Result = ""
    EndIf
    
    CloseProgram(ProgramId)
    
    Result = ReplaceString(Result,",",".") ;Dezimalzahlen werden mit kommer ausgegeben, aber nur mit Punkt akzeptiert
    
  Else
    MessageRequester("Problem!","Die PowerShell wurde nicht gefunden.")
  EndIf
  
  Beep_(1760,200)
  
EndIf

ProcedureReturn Result

EndProcedure
Die vom Designer generierten Fenster-Definitionen 'Designer.pb':

Code: Alles auswählen

;***********************************************************************************************************************
;* Dieses Modul enthält den im wesentlichen die vom PureBasic-Designer erzeugten Gadget-Definitionen.                  *
;* ------------------------------------------------------------------------------------------------------------------- *
;* PureBasic Visual Designer v3.95 build 1485 (PB4Code)                                                                *
;***********************************************************************************************************************

;- Window Constants
Enumeration
  #CalculatorFeld
EndEnumeration

;- Gadget Constants
Enumeration
  #Ausdruck
  #Rechne
EndEnumeration

Procedure Open_CalculatorFeld()
  If OpenWindow(#CalculatorFeld, 20, 20, 600, 20, "CalculatorFeld",  #PB_Window_SystemMenu | #PB_Window_TitleBar )
    StringGadget(#Ausdruck, 0, 0, 530, 20, "")
    ButtonGadget(#Rechne, 530, 0, 70, 20, "&Rechne")
  EndIf
EndProcedure
Der Event-Loop 'CalculatorFeld.pb':

Code: Alles auswählen

;***********************************************************************************************************************
;* Dieses Modul enthält im wesentlichen die vom PureBasic-Designer erzeugte Event-Schleife.                            *
;* ------------------------------------------------------------------------------------------------------------------- *
;* PureBasic Visual Designer v3.95 build 1485 (PB4Code)                                                                *
;***********************************************************************************************************************
EnableExplicit

IncludeFile "Designer.pb"
IncludeFile "CalculatorFeld.pbi"

Global Event.l     = 0
Global WindowID.l  = 0
Global GadgetID.l  = 0
Global EventType.l = 0

Open_CalculatorFeld()
AddKeyboardShortcut(#CalculatorFeld,#PB_Shortcut_Return,#VK_RETURN)
AddKeyboardShortcut(#CalculatorFeld,#PB_Shortcut_Escape,#VK_ESCAPE)
SetActiveGadget(#Ausdruck)

Repeat ; Start of the event loop
  
  Event = WaitWindowEvent() ; This line waits until an event is received from Windows
  
  WindowID = EventWindow() ; The Window where the event is generated, can be used in the gadget procedures
  
  GadgetID = EventGadget() ; Is it a gadget event?
  
  EventType = EventType() ; The event type
  
  ;You can place code here, and use the result as parameters for the procedures
  
  If Event = #PB_Event_Gadget
    If GadgetID = #Rechne
      SetGadgetText(#Ausdruck,DoCalculation(GetGadgetText(#Ausdruck)))
      SetActiveGadget(#Ausdruck)
    EndIf  
  EndIf
  
  Select EventMenu()
    Case #VK_RETURN
      SetGadgetText(#Ausdruck,DoCalculation(GetGadgetText(#Ausdruck)))
      SetActiveGadget(#Ausdruck)
    Case #VK_ESCAPE
      Break
  EndSelect
  
Until Event = #PB_Event_CloseWindow ; End of the event loop

RemoveKeyboardShortcut(#CalculatorFeld,#PB_Shortcut_All)

End
Dies soll keine vollständige Anwendung sein, sondern nur die Funktionsweise so eines Calculatur-Feldes in einem Dialog demonstrieren. Auch erhebe ich keinen Anspruch darauf, das der Code von herausragender Eleganz ist. Der größte Teil ist generiert oder aus der Hilfe abgeschrieben. Es ist die Idee, die zählt ...

PS: Sollte jemand in das Calculator-Feld "1 + 2 * 3" eingeben, so wird als Ergebnis 7 ausgewiesen, weil hier mathematisch korrekt Punktrechnung vor Strichrechnung berücksichtigt wird. Den Windows-Taschenrechner kann man seit je her in einer kaufmännischen (Standard) und einer technisch-wissenschaftlichen Darstellung anzeigen. Der kaufmännische Taschenrechner bekommt immer schon bei obiger Eingabe 9 heraus, weil er erst "1 + 2" berechnet und dann das Ergebnis mit 3 multipliziert. Der technisch-wissenschaftliche Taschenrechner liefert das Ergebnis 7 (verrückt was?). Das gilt auch für real existierende Taschenrechner. Die billigen liefern bei der Rechnung meist 9, die teuren 7. Da kann man sich furchtbar vertun ...

PS: Das Calculator-Eingabefeld bekommt sogar bei Rechnungen wie "(1/3)*3" wieder 1 heraus. Eine Situation, wo einige Taschenrechner nur "0.99999999999" herausbekommen, weil die Genauigkeit der Zahlendarstellung intern begrenzt ist (bei allen Rechnern). 'Bessere' Rechner können aber damit umgehen.
Pollux
Beiträge: 23
Registriert: 23.09.2010 18:46
Computerausstattung: Eigenbau: CPU Intel i7-960 Windows 7 64-Bit 12 GB RAM
Toshiba Notebook Windows XP 32-Bit
weitere
Wohnort: Gilching bei München

Re: Calculator-Eingabefeld für Windows

Beitrag von Pollux »

... und gleich noch - nach dem gleichen Verfahren wie oben - eine Möglichkeit sich von der PowerShell Informationen zu holen; etwa eine Process-Liste. Da man die Liste als String im Speicher hat und man weiß, wo die Information steht, kann man sie in Programmen auch auswerten. Das folgende Programm ist nur eine Demo. Nur zu Anzeige kann man sich sowas besser vom Programm-Manager holen.

Das Programm beeped zweimal. Das erste mal, wenn die PowerShell aufgerufen wird, und das zweite mal, wenn es von der PowerShell wieder zurückkommt. Bei mir dauert es etwa 1 Sekunde, bis das Ergebnis da ist.

Code: Alles auswählen

;***********************************************************************************************************************
;* Dieses Programm holt sich eine nach der CPU-Auslastung absteigend sortierte Process-Liste von der PowerShell.       *
;* ------------------------------------------------------------------------------------------------------------------- *
;* Zum Compilieren muß das Executable-Format 'Console' eingestellt werden.                                             *
;* Die PowerShell ist ab Windows 7 integriert und befindet sich unter 'Zubehör'. Sie kann ab Windows XP nachgerüstet   *
;* werden. Die Benutzung der PowerShell wird oft von Administratoren eingeschrägt (hab ich gelesen).                   *
;* ------------------------------------------------------------------------------------------------------------------- *
;* Entwickelt für PowerBasic 4.50 von: Jürgen Priess                                                                   *
;***********************************************************************************************************************
EnableExplicit

Global Ausdruck.s   = "get-process |sort-object cpu -descending" ;Cmdlet für sortierte Prozess-Liste
Global ProgramId.l  = 0
Global Program.s    = "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" ;PowerShell (gegebenenfalls anpassen)
Global SystemRoot.s = GetEnvironmentVariable("SystemRoot") ;SystemRoot holen
Global Result.s     = ""
Global Flags.l      = #PB_Program_Open | #PB_Program_Read | #PB_Program_Hide

If OpenConsole()
  
  Program = ReplaceString(Program,"%SystemRoot%",SystemRoot,#PB_String_NoCase) ;ersetzen des SystemRoot-Verzeichnisses
  
  ProgramId = RunProgram(Program,Ausdruck,"",Flags) ;Aufruf der PowerShell mit dem Cmdlet
  
  If ProgramId  
  
    Beep_(880,200)    
    While ProgramRunning(ProgramId)
      If AvailableProgramOutput(ProgramId)
        Result + ReadProgramString(ProgramId)
      EndIf
    Wend
    Beep_(1760,200)
    
    If 0 < ProgramExitCode(ProgramId)
      PrintN(Result)
    Else
      
      CloseProgram(ProgramId)
      
      ;Die Liste im String könnte natürlich in einem Programm weiter ausgewertet werden
      ;Auf gleiche Weise könnte man sich auch z.B. Services- und Registry-Listen holen
      
      Repeat ;Zeilenumbruch für Ausgabe
        PrintN(Left(Result,79))
        Result = Mid(Result,80)
      Until Len(Result) = 0
      
    EndIf
    
  Else
    PrintN("Die PowerShell wurde nicht gefunden.")
  EndIf
      
  PrintN("")
  PrintN("===> Zum Beenden ENTER-Taste druecken")
  Input()

  CloseConsole()

EndIf
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Calculator-Eingabefeld für Windows

Beitrag von ts-soft »

Nur mal als Hinweis:

Code: Alles auswählen

Protected Program.s = "powershell.exe"
genügt, Pfad kann weggelassen werden! Spart schon mal ein paar Zeilen Deines Codes :)
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Calculator-Eingabefeld für Windows

Beitrag von STARGÅTE »

Nett, aber auch ich wurde damals (als ich eben solch ein Parser selbst geschrieben habe) eines besseren belehrt:
Eine Datenbank dafür zu nutzen (missbrauchen):

Code: Alles auswählen

Enumeration : #Database : EndEnumeration

Procedure.s Rechne(String$)
  If DatabaseQuery(#Database, "SELECT ("+String$+")")
    If NextDatabaseRow(#Database)
      ProcedureReturn GetDatabaseString(#Database, 0)
    EndIf
    FinishDatabaseQuery(#Database)
  EndIf
EndProcedure  
      
UseSQLiteDatabase()
OpenDatabase(#Database, ":memory:", "", "", #PB_Database_SQLite)

Debug Rechne("1 + 2 * 3")
Debug Rechne("(1.0/3)*3")
Debug Rechne("374*4+7*(83+99)-3*128")

CloseDatabase(#Database)
Was mich interessieren würde ist, ob diese powershell.exe wirklich nur standard-rechnungen machen kann, weil du ja scheibst:
FindString(" ()*+-./0123456789Ee",Zeichen,1)

Daraus schließe ich das es keine Potenzen, Wurzeln, Funktionen gibt ?
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Kiffi
Beiträge: 10621
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Re: Calculator-Eingabefeld für Windows

Beitrag von Kiffi »

STARGÅTE hat geschrieben:Nett, aber auch ich wurde damals (als ich eben solch ein Parser selbst geschrieben habe) eines besseren belehrt:
<)
STARGÅTE hat geschrieben:Daraus schließe ich das es keine Potenzen, Wurzeln, Funktionen gibt ?
Die Powershell ist schon ein recht mächtiges Werkzeug. Grundlage ist
das .Net-Framework. Alles, was hier implementiert ist, kann man IMO
auch in der Powershell nutzen:

http://msdn.microsoft.com/en-us/library ... .math.aspx

Code: Alles auswählen

PS C:\> [system.math]::tan(45) 
1.61977519054386
Grüße ... Kiffi
Hygge
Pollux
Beiträge: 23
Registriert: 23.09.2010 18:46
Computerausstattung: Eigenbau: CPU Intel i7-960 Windows 7 64-Bit 12 GB RAM
Toshiba Notebook Windows XP 32-Bit
weitere
Wohnort: Gilching bei München

Re: Calculator-Eingabefeld für Windows

Beitrag von Pollux »

Die von STARGATE vorgestellet Lösung über einen Aufruf der Datenbank SQLite ist sogar viel schneller. Außerdem ist das Datenbank-Verfahren nicht auf Systeme mit der PowerShell beschränkt. Es muß Nichts zusätzlich installiert werden.

Wie KIFFI zeigt, kann man in der PowerShell durch Vorranstellen der Math-Bibliothek auch den Tangens ermitteln. Ich bin mir sicher, das geht auch im SQL (ob in SQLite weiß ich nicht). Jedenfalls muß man sich da mit der Schreibweise auskennen. Auch in PureBasic muß man ja für diese Dinge die Math-Bibliothek bemühen.

Ich habe auf der Suche nach Programmen, die Rechenausdrücke ausrechnen können und das Ergebnis auf stdout ausgeben zuerst an den Windows Scripting Host gedacht. Es gibt aber sicherlich noch weitere Programme für solche Aufgaben. Für die Berechnung von einfachen als String vorliegenden Ausdrücken scheint mir die von STARGATE gezeigte Lösung bisher die beßte zu sein.

Danke für die Beiträge
Benutzeravatar
HeX0R
Beiträge: 2960
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win10 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2
Kontaktdaten:

Re: Calculator-Eingabefeld für Windows

Beitrag von HeX0R »

Pollux hat geschrieben:[...]
Für die Berechnung von einfachen als String vorliegenden Ausdrücken scheint mir die von STARGATE gezeigte Lösung bisher die beßte zu sein.
Aua Aua,
wir haben uns ja zwangsweise an die vielen Schreibfehler in deutschen Boards gewöhnen müssen, aber das tut wirklich weh beim lesen...
;)
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Calculator-Eingabefeld für Windows

Beitrag von STARGÅTE »

Ich habs aber auch nur von Kiffi geklaut ^^ der hier oft Lösungen mit Datenbanken anbietet für Probleme die man sonst u.u. sehr viel komplizierter zu Fuß lösen müsste.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Pollux
Beiträge: 23
Registriert: 23.09.2010 18:46
Computerausstattung: Eigenbau: CPU Intel i7-960 Windows 7 64-Bit 12 GB RAM
Toshiba Notebook Windows XP 32-Bit
weitere
Wohnort: Gilching bei München

Re: Calculator-Eingabefeld für Windows

Beitrag von Pollux »

Hallo HeXOR

Ich korrigiere: S Beschte wos je hets gits.
Antworten