Schlagwort-Archiv: Code

AutoHotKey-Hacks

AutoHotkey (AHK), kennste, kennste? Das ist ein – jedenfalls unter Windows – sehr mächtiges Open Source-Tool, wenn es um das Nutzen von eigenen Tastenkombinationen, das Automatisieren von Vorgängen oder Mausaktionen geht. Eine eierlegende Wollmilchsau.

Allerdings finde ich die Nutzung schon immer etwas holprig. AutoHotkey ist skriptbasiert, das muss man mögen und können. Wobei die Sache mit dem Können in Zeiten mächtiger LLMs schon an Schrecken verloren hat. Wenn man eine Idee hat, die man damit umsetzen will, dann bekommt man das mittlerweile auch gelöst. Zudem ist seit Version 2 alles etwas glatter und konsistenter geworden.

Wenn man eine Idee hat… Daher will ich hier mal kurz zeigen, was ich mit AutoHotkey so mache, vielleicht inspiriert es ja den einen oder die andere. Was ich hier nicht zeige ist den Einstieg in die Nutzung von AutoHotkey, denn Youtube ist voll von solchen Tutorials, da wirst du fündig. Und wild ist das nicht.

Bsp. 1: Der Klassiker: Öffnen eines Programms

Ich verwende als Passwortmanager Keepass XC, und den brauche ich ständig. Ich will also eine sehr einfache Möglichkeit haben, Keepass XC zu öffnen und den Fokus direkt auf das Suchfeld zu legen. Für die Verwendung von Passwörtern im Browser ist das nicht nötig, da ist Keepass XC bestens integriert, aber für alle andere Dinge schon.

Was ich also will: Mit “Strg + Alt + K” (K wie Keepass) soll Keepass XC ggf. gestartet, auf jeden Fall geöffnet und der Fokus auf das Suchfeld gesetzt werden. Gelöst habe ich das so:

; Welches Fenster soll gesucht werden? (Prozessnamen statt Fenstertitel, sonst Probleme, wenn z.B. "KeePassXC" in einem VS Code Tab steht).
targetTitle := "ahk_exe KeePassXC.exe"

; --- Haupt-Hotkey: Strg + Alt + K ---
^!k:: {
    ; Fall 1: Das Fenster ist bereits im Vordergrund -> Minimieren
    if WinActive(targetTitle) {
        WinMinimize
    }
    else {
        ; Fall 2: Das Programm läuft noch gar nicht -> Starten
        if !WinExist(targetTitle) {
            try {
                Run("C:\Program Files\KeePassXC\KeePassXC.exe")
                ; Warte max. 10 Sek., bis das Fenster vom System registriert wird
                if !WinWait(targetTitle, , 10) {
                    MsgBox("KeePassXC startete zu langsam.", "Zeitüberschreitung", "IconHand")
                    return
                }
            } catch {
                MsgBox("Die Datei 'KeePassXC.exe' wurde unter dem Pfad nicht gefunden.", "Fehler", "IconHand")
                return
            }
        }

        ; Fall 3: Programm läuft im Hintergrund -> Aktivieren und Fokus setzen
        WinActivate(targetTitle)

        ; Warte kurz, bis das Fenster wirklich den Fokus hat, bevor wir Tippen
        if WinWaitActive(targetTitle, , 2) {
            Send("^f") ; Sendet Strg+F für die Suche
        }
    }
}

Das ist zwar etwas umfangreicher als nötig, hat dafür aber schon diverses Error-Handling mit drin. Mit diesem Code bin ich seit Jahren mehr als glücklich und verwende den mehrfach täglich.

Bsp. 2: Tastaturlayout (bei Laptops) anpassen

Seit einigen Jahren arbeite mit einem Laptop, bei dem mir das Standard-Tastaturlayout nicht gefällt. Und zwar hat er physische “Page Up”- und “Page Down”-Tasten, die von mir dagegen häufig verwendeten Tasten “Home” / “Pos1” und “End” sind nur über den Umweg der Function-Taste erreichbar. Das nervt, ist aber ein ganz klassisches Anwendungsszenario für AutoHotkey.

Mit diesem Code macht man aus der Eingabe von “Page Up” ein “Pos1” usw.:

; Tasten direkt vertauschen:
PgUp::Home      ; Drücke ich PgUp, wird stattdessen Home gesendet
PgDn::End       ; Drücke ich PgDn, wird stattdessen End gesendet
Home::PgUp      ; Drücke ich Home, wird stattdessen PgUp gesendet
End::PgDn       ; Drücke ich End,  wird stattdessen PgDn gesendet

; Dasselbe für die Kombination mit Shift:
+PgUp::+Home    ; Shift+PgUp verhält sich wie Shift+Home
+PgDn::+End     ; Shift+PgDn verhält sich wie Shift+End
+Home::+PgUp    ; Shift+Home verhält sich wie Shift+PgUp
+End::+PgDn     ; Shift+End  verhält sich wie Shift+PgDn

; In AutoHotkey bedeutet:
;   + = Shift, ^ = Strg, ! = Alt, # = Win

Das löst das Problem mit dem “falschen” Tastenlayout problemlos.

Bulk Calendar – Mehrere Termine auf einmal bei Google Calendar eintragen

Ihr kennt das: Man will weder einen einzelnen Termin in seinen Google Calendar eintragen, noch einen Serientermin erstellen. Man hat beispielsweise eine Reihe an Seminaren mit verschiedenen Themen, irgendwelche Meetings an wechselnden Orten oder die Fussballtermine der Kinder gegen verschiedene Gegener. Man kann jetzt entweder auf die Details verzichten und doch einen Serientermine anlegen, oder aber man hat viel Handarbeit vor sich.

Genau diese Handarbeit kann man sich deutlich erleichtern, indem man die anzulegenden Termine in eine simple Google Tabelle einträgt und anschließend dort ein Skript startet, welches diese tabellarisch erfassten Daten in einen Kalender einträgt. Es ist schon etwas länger her, dass ich mich auf die Suche nach einem solchen Skript gemacht habe. Daher fehlt mir jetzt leider die Referenz auf das Original an dem ich anschließend noch ziemlich weitergebastelt habe.

Ich habe hier eine Kopie meiner Tabelle erstellt und freigegeben. Sieht in etwas so aus:

Bulk Calendar - Tabelle

Wenn du die Tabelle und das Skript verwenden willst erstellst du dir am besten erst mal eine Kopie der Tabelle (das Skript ist darin eingebettet):

Bulk Calendar - Kopie erstellen

Zuerst muss man logischerweise die gewünschten Daten in die Tabelle eintragen (Tabelle s.o.). Die grau markierten Zellen (Start- und Endzeitpunkt) dürfen nicht editiert werden. Wichtig ist, dass man die Formel in den grauen Spalten nur soweit nach unten kopiert, wie auch Daten in die Zeilen eingetragen sind:

Bulk Calendar - Formeln

Anschließend muss das Skript ausgeführt werden:

Bulk Calendar - Tools - Skriptmanager
Bulk Calendar - Skriptmanager Ausführen

Beim Ausführen des Skript gibt es diverse Kontroll-Dialoge, die man einfach im Skript nach Belieben auskommentieren kann.

Der Vollständigkeit halber hier noch der Code des Skripts. Wie immer quick’n’dirty, tut es aber für mich. Falls jemand damit weiter bastelt bin ich offen für Anregungen.

// Custom Menu:
// see https://developers.google.com/apps-script/guides/bound#custom_menus_dialogs_and_sidebars
function onOpen() {

//  var cals = CalendarApp.getAllOwnedCalendars();
//  Browser.msgBox('Es gibt genau %s Kalender zu Auswahl:', CalendarApp.getAllOwnedCalendars().length);
//  for (var c=0; c < cals.length; c++) {
//    ui.createMenu('Termine eintragen')
//      .addItem(cals[c].getName(), 'TermineInCalendarEintragen')
//      .addToUi();
//  }  

  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Termine eintragen')
      .addItem('Go!', 'TermineInCalendarEintragen')
      .addToUi();

}

function TermineInCalendarEintragen() {

  // Default-Kalender auswählen:
  //var cal = CalendarApp.getDefaultCalendar();

  // Besser: Auswahl an Kalendern anbieten.
  // Ansatz: Alle möglichen Kalender auflisten, per Eingabefeld den gewünschten eingeben lassen:
  var kalenderauswahl = 'In welchen Kalender sollen die Termine eingetragen werden?\\n\\n';
  // Alle eigenen Kalender abrufen:
  var cals = CalendarApp.getAllOwnedCalendars();

  Logger.log('Es gibt genau %s Kalender zu Auswahl:', cals.length);

  // Kalender durchgehen und Ausgabenachricht zusammenbasteln:
  //for (c in cals) {
  for (var c=0; c < cals.length; c++) {
    Logger.log("ID: " + cals[c].getName());
    kalenderauswahl = kalenderauswahl + '- ' + cals[c].getName() + '\\n';
  }
  kalenderauswahl = kalenderauswahl + '\\nBitte Kalendername EXAKT eingeben:';
  var kalendername = Browser.inputBox(kalenderauswahl)
  Logger.log("Kalender: " + kalendername);

  // Jetzt den Kalender mit dem übergebenen Namen auswählen:
  cal = CalendarApp.getCalendarsByName(kalendername);

  // Falls ein (brauchbarer) Kalendername eingegeben wurde:
  if (kalendername && cal.length > 0) {
    Browser.msgBox('Kalender "' + cal[0].getName()  + '" gewählt.')

    // Jetzt werden die Daten aus der Tabelle gezogen:
    var sheet = SpreadsheetApp.getActiveSheet();
    var startRow = 2;
    var numRows = SpreadsheetApp.getActiveSheet().getLastRow()-1;  // -1, weil die Kopfzeile keine Daten enthält
    Browser.msgBox('Es sollen ' + numRows + ' Termine eingetragen werden.')
    // Datenbereich auswählen:
    var dataRange = sheet.getRange(startRow, 1, numRows, 8);
    // Daraus Daten in Variable schreiben:
    var data = dataRange.getValues();
    // Jetzt jede Zeile durchgehen:
    for (d in data) {
      // Daten aus dieser Zeile wählen:
      var row = data[d];

      // Jetzt einzelne Felder davon:
      var title  = row[0];
      var tstart = row[4];
      var tstop  = row[5];
      var loc    = row[6];
      var desc   = row[7];

      // Jetzt wird der Termin eingetragen:
      // https://developers.google.com/apps-script/class_calendarapp?hl=de#createEvent
      cal[0].createEvent(title, tstart, tstop, {description:desc,location:loc});

      // ... und noch eine Meldung ausgegeben. Denn manchmal steigt das Skript aus, und so weiß man, wie weit es war.
      Browser.msgBox( parseInt(d)+1 + '. Termin in Kalender "' + kalendername + '" eingetragen:\\n\\n' + title + '\\n\\n' +  tstart + '\\n\\n' + desc )
    }
    // Am Ende nochmal Meldung, dann ist klar, dass alles durchgelaufen ist:
    Browser.msgBox('Fertig!');
  }
  else {
    Browser.msgBox('Kein Kalender ausgewählt oder Kalender falsch angegeben...')
  }
}

Android-Tablet als digitaler Bilderrahmen

Eltern und Großeltern freuen sich ja immer über Bilder ihrer Kinder und Enkel (gut, etwas pauschalisiert). Aus diesem Grund haben wir unseren Eltern vor einigen Jahren einen digitalen Bilderrahmen geschenkt, der mit WLAN im heimischen Netz hängt und über einen Dienst per Mail mit neuen Bildern gefüttert werden kann. Super Sache. Es war zwar nur ein kleiner Rahmen (ein Kodak W730 Pulse mit 7 Zoll), aber ordentlich zu bedienen und eben mit gewünschter und zuverlässiger Email-Funktionalität, die auch (mal mehr, mal weniger) rege genutzt wurde.

Weiterlesen

Kontakteverwaltung: Perfektionistisch bis zwanghaft

Ich gebe zu, bei meiner Kontaktverwaltung bin ich durchaus perfektionistisch bis zwanghaft. Jahre lang habe ich Outlook dafür herangezogen und war auch mit der Synchronisation mit meinen ersten mobilen Endgeräten sehr zufrieden. Mit dem Umstieg auf Android bin ich dann schweren Herzens auch mit meinen Kontakten zu Google Contacts umgezogen. Mit welchem Programm ich das gemacht habe weiß ich leider nicht mehr, die Suche hat damals etwas gedauert, weil es mir wichtig war, alle Kontaktbilder (und das waren viele!) beim Umzug nicht zu verlieren. Hat dann auch alles soweit geklappt, einzig die Darstellung der Namen war etwas unschön. Dazu muss man wissen, dass es in Kontaktverwaltungen verschiedenste Namensfelder gibt, einfach Vor- und Nachname wäre da zu simpel. Bei meinem Umzug wurden die Felder für Vor- und Nachnamen korrekt belegt, zusätzlich wurde aber der angezeigte Name auf “Nachname, Vorname” gesetzt. Das ist eigentlich erst mal egal, kann aber unschön werden, wenn man dann andere Tools mit seinen Google-Kontakten synchronisiert. Ich nutze bspw. den automatischen Datenabgleich mit meiner Fritzbox, der dafür sorgt, dass bei allen Nummern, die ich in meinen Kontakten habe, auf dem Festnetztelefon der Name des Anrufers angezeigt wird, auch wenn dieser gar nicht im Festnetztelefon gespeichert ist. Und ich nutze das Thunderbird-Add-On “Google Contacts”, um im Mailclient meines Vertrauens immer die Kontakte aktuell zu haben. Und dort ergibt sich dann z.B. das “Problem”, dass eine Mail dann nicht an ‘Vorname Nachname <mail@dres.se>’ gesendet wird, sondern an ‘”Nachname, Vorname” <mail@dres.se>’. Ist jetzt nicht weiter schlimm, erzeugt aber beim Kontaktperfektionisten ein gewisses Unbehagen.

In Outlook hatte ich mir mal ein kleines VBA-Schnipsel erstellt, um alle Kontakte durchzugehen und Felder nach meinen Änderungswünschen zu modifizieren. Sowas wollte ich jetzt auch für meine Google-Kontakte haben und habe mich mal hingesetzt, und eine kleine PHP-Lösung zusammengestrickt. Der Großteil des Codes ist von irgendwo zusammengeklaut und eben für meine Bedürfnisse angepasst, benutzt wird außerdem das Zend Framework. Ich habe damit jetzt einfach mal alle “Nachname, Vorname” in “Vorname Nachname” ändern lassen, es lässt sich aber auch leicht an andere “Problemfälle” anpassen. Viel Spaß.

Weiterlesen