Virtuelle Desktops in Windows 10

Nachdem ich mich vor einigen durch das unbeabsichtige Öffnen eines neuen virtuellen Desktops selbst behindert habe, wurde es Zeit diesem Windows Feature mal einen kurzen Artikel zu widmen

Virtuelle Desktops verhalten sich wie normale Oberflächen (Windows Desktops), zwischen denen man mittels Tastenkombination wechseln kann. Was früher unter Windows nur mit Zusatzsoftware erreicht werden konnte — in der UNIX / Linux Welt gibt es diese Funktionalität schon seit Jahrzehnten — ist in Windows 10 eingebaut.

Zur Erstellung einen neuen Virtuellen Desktops gibt es unterschiedliche Möglichkeiten:

Möglichkeit A:

  • Man drückt auf den Button „Aktive Anwendungen“ und erstellt dann links oben über den „+ Neuer Desktop“ einen neuen virtuellen Desktop. Hinweis: Wenn der „Aktive Anwendungen“ Button nicht da ist, rechte Maustaste auf die Taskleiste und „Taskansicht-Schaltfläche anzeigen“ anklicken
  • Alternativ drückt man <Windows><TAB>, um in die Taskansicht zu kommen
  • In dieser Taskansicht kann man auch per Maus zwischen den Desktops umschalten
  • In dieser Taskansicht kann man auch einzelne Anwendungen zwischen den Desktops hin und her schubsen oder an alle Desktops verteilen (via Kontextmenü)

Möglichkeit B:

  • <Windows><CTRL><D> um einen neuen Desktop zu erstellen und zu ihm zu wechseln
  • <Windows><CTRL><F4> um den aktuellen Desktop zu schließen
  • <Windows><CTRL><links/rechts> um zwischen den Desktops hin und her zu wechseln

Warum TeXworks manchmal nicht die PDF Anzeige aktualisiert / When TeXworks does not update the PDF

Vor einiger Zeit fiel mir auf, dass TeXworks die PDF-Anzeige manchmal nicht aktualisiert. Wann genau dies aber geschah und wann nicht, konnte ich aber nicht feststellen. Gestern schließlich brachte eine Google-Suche nach „texworks not refreshing the pdf“ eine TSX-Frage [1] ans Licht, in der der Autor dieses Fehlverhalten untersucht hatte: Das PDF wurde immer dann nicht aktualisiert, wenn die entsprechende TeX-Datei im Wurzelverzeichnis eines Laufwerks lag. Muss man erst einmal drauf kommen…

Ein Bug-Request [2] war schnell geschrieben, in der neuesten Development-Version ist dieser Bug bereits gefixt.

[1] https://tex.stackexchange.com/questions/568368/texworks-pdf-not-refreshing
[2] https://github.com/TeXworks/texworks/issues/908

English Summary

In TeXworks (at least in version 0.6.5) the PDF is not updated after compilation if the TeX file had been placed in a top-level directory. A bug report was raised, it will be fixed in future versions.

Mit WinRAR Dateien einzeln packen und verschlüsseln

Gelegentlich ist es praktisch, Aufgaben auf der Kommandozeile zu erledigen. Das Packen von n Dateien in separate verschlüsselte Archive gehört dazu. Der folgende Code packt jede XLSX Datei in einem Verzeichnis in ein entsprechendes RAR und verschlüsselt es mit dem Passwort „ABCDE“. Verschlüsselte ZIP Archive lassen sich meines Wissens nach nicht damit erstellen!

For /F %%a IN ('DIR /B *.xlsx') DO (
 CALL "C:\winrar\rar.exe" a -pABCDE %%~na.rar %%~na.xlsx
)

Mailman Spammer mit Python blocken – Teil 2

Ausgehend von meinem ersten Artikel zu diesem Thema habe ich jetzt noch eine Erweiterung des Skripts vorgenommen. Als Spammer erkannte E-Mail-Adressen werden jetzt auch automatisch geblockt.

Dazu suche ich alle „Dauerhaft von der Liste verbannen“ Checkboxen — ihre Namen beginnen alle mit „ban-“ — und klicke sie.

from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options
from selenium.common.exceptions import NoSuchElementException
 
opts = Options()
browser = Firefox(executable_path=r"C:\geckodriver-v0.27.0\geckodriver.exe",
                  options=opts)
browser.implicitly_wait(3)
 
browser.get('<url>')
search_form = browser.find_element_by_name('adminpw')
search_form.send_keys('<password>')
search_form.submit()
 
try:
    field = browser.find_element_by_name('discardalldefersp')
    field.click()
    browser.implicitly_wait(3)
    submit = browser.find_element_by_name('submit')
    submit.click()
except NoSuchElementException:
    print('No new messages to be discarded')
 
browser.implicitly_wait(3)
 
fields = browser.find_elements_by_xpath("//input[@value='3']")
emails = browser.find_elements_by_xpath('//td[contains(text(),"@")]')
banfields = browser.find_elements_by_xpath('//input[contains(@name,"ban-")]')
 
if len(fields) == 0:
    print('No new requests to be discarded, closing browser')
    browser.close()
else:
    if len(fields) == len(emails) and len(fields) == len(banfields) :
        zipped_list = list(zip(emails, fields, banfields))
 
        for i in zipped_list:
            email, field, banfield = i
            if not email.text.endswith(')'):
                field.click()
                banfield.click()

ESP32 unter Windows programmieren

Ein ESP32 ist ein sehr preiswerter Microcontroller, der aber einiges an Leistung „unter der Haube“ hat:

Wie kann man ihn unter Windows programmieren? Ich mache es wie folgt:

  1. Arduino IDE von https://www.arduino.cc/ installieren
  2. In den Voreinstellungen unter „Zusätzliche Boardverwalter-URLs“ die folgende URL hinzufügen: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  3. Boardverwalter-URL

  4. Unter Boardverwalter nach „ESP“ suchen und das Paket installieren
  5. ESP

  6. Eigenen Code oder Demo-Code schreiben/kompilieren und auf den ESP hochladen. Bei mir hat es mit den folgenden Einstellungen geklappt, zusätzlich musste ich aber beim Upload (nach dem Drücken des Upload-Knopfs) einmal den Boot-Button auf dem ESP drücken.
  7. Upload-Settings

Siemens Nixdorf VFD Displays ansteuern mit Python

Vor einigen Monaten habe ich mir günstig auf Ebay ein Siemens Nixdorf BA 63 USB Display gekauft, das VFD (vacuum fluorescent display) Technologie für die Anzeige nutzt und 2×20 Zeichen bietet.

Als passendste Python-Bibliothek nutze ich https://github.com/stephanemouton/VFD-WCN, die Anleitung zur Einrichtung wird im github sehr gut beschrieben.

Hier der Quellcode, der die Ausgabe im Bild erzeugt:

from vfdpos import *
factory=WincorNixdorfDisplayFactory()
VFDs = factory.get_vfd_pos()
MyVFD = VFDs[0]

MyVFD.clearscreen()

MyVFD.poscur(1, 1)
MyVFD.write_msg("Hallo")
MyVFD.write_msg("Welt")
MyVFD.write_msg("1234567abcd\r")
MyVFD.poscur(2, 1)
MyVFD.write_msg("ABCDEFüäößi")

MyVFD.close()

CTAN-Pakete per REST-API hochladen

Vermutlich ist es nicht so bekannt, dass man LaTeX-Pakete auch per REST-API auf CTAN hochladen kann. Das ist insbesondere dann praktisch, wenn man öfter Pakete aktualisieren muss, wie ich es beispielsweise mit der DTK Bibliografie mehrmals im Jahr mache.

Manfred Lotz vom CTAN-Team hat dazu ein Python-Skript geschrieben (https://gitlab.com/Lotz/pkgcheck/blob/master/ctan_upload.py), das diese API befüttert.

Ich habe sein Skript noch ein wenig angepasst (ich erstelle auch die ZIP-Datei damit und kopiere die richtigen Dateien an ihren Platz), mein Skript findet ihr im Github unter https://github.com/dante-ev/dtk-bibliography/blob/master/pack_and_upload_to_ctan.py. Für den Upload selbst benötigt man noch eine TOML-Datei, in der die Upload-Informationen als Key-Value-Paare stehen. Ein Beispiel für eine solche TOML-Datei findet ihr bei Manfred unter https://gitlab.com/Lotz/pkgcheck/-/blob/master/pkgcheck.toml.

Fortnite teilweise blockieren – Erweiterung um Status

This entry is part 2 of 2 in the series Pi-Hole

Im ersten Artikel zu diesem Thema hatte ich schon beschrieben, wie man mit Pi-Hole DNS Lookups so unterbinden kann, dass man Fortnite oder andere Zeitfresser temporär blockieren kann. In diesem Teil erweitern wir das Skript um eine Statusabfrage. Dazu fügen wir einfach eine Route hinzu, die die URL mit dem Parameter /status abfragt. Dann wird der Inhalt der epicstatus.txt abgefragt, die von den Routen /on und /off mit dem Zeitstempel versehen wurde.

import os
from datetime import datetime
from flask import Flask
 
app = Flask(__name__)
 
@app.route('/')
def index():
    return('<h1>Use /off and /on to enable/disable blocking, /status to get epic.log</h1>')
 
@app.route('/<param>')
def setter(param):
    if param=='status':
        with open('/home/pi/epicstatus.txt') as f:
            content = f.read()
            return '<h2>' + content + '</h2>'
    if param=='off':
        os.system("/usr/local/bin/pihole regex '.*\.epicgames.com' > /home/pi/epic.log")
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        with open('/home/pi/epicstatus.txt','w') as ausgabe:
            ausgabe.write(now + ' off')
        return '<h1>Turning off Fortnite</h1>'
    elif param=='on':
        os.system("/usr/local/bin/pihole regex -d '.*\.epicgames.com' > /home/pi/epic.log")
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        with open('/home/pi/epicstatus.txt','w') as ausgabe:
            ausgabe.write(now+ ' on')
        return '<h1>Turning on Fortnite</h1>'

Liste meiner genutzten VSC-Plugins

Neben UltraEdit, Notepad++, Emacs, etc. nutze ich auch Visual Studio Code. Hier die Liste der wichtigsten von mir genutzten Plugins:

  • German Language Pack for Visual Studio Code für deutsche Menüs
  • Render Line Endings, um zwischen LF und CRLF umzuschalten
  • Python für die Python-Unterstützung
  • LaTeX Workshop
  • Graphviz (dot) language support for Visual Studio Code für gv
  • Graphviz Preview für ebendas
  • Encryptor for VS Code, Text mit AES 256 zu verschlüsseln
  • Bracket Pair Colorizer um zueinandergehörige Klammern zu markieren

Dateien kopieren, zippen und löschen mit Python

Hier ein kurzes Beispiel, wie man mit Python-Modulen Dateien kopieren, zippen und löschen kann.

import zipfile
from shutil import copyfile
from os import unlink
 
# copy file
copyfile('dtk-authoryear.bbx'  , './dtk-bibliography/dtk-authoryear.bbx')
copyfile('dtk-authoryear.dbx'  , './dtk-bibliography/dtk-authoryear.dbx')
copyfile('dtk-bibliography.pdf', './dtk-bibliography/dtk-bibliography.pdf')
copyfile('dtk-bibliography.tex', './dtk-bibliography/dtk-bibliography.tex')
 
# create the zip file
with zipfile.ZipFile('dtk-bibliography.zip', 'w', zipfile.ZIP_DEFLATED) as z:
    z.write('./dtk-bibliography/README.md')
    z.write('./dtk-bibliography/dtk-authoryear.bbx')
    z.write('./dtk-bibliography/dtk-authoryear.dbx')
    z.write('./dtk-bibliography/dtk-bibliography.pdf')
    z.write('./dtk-bibliography/dtk-bibliography.tex')
 
# delete copied files
unlink('./dtk-bibliography/dtk-authoryear.bbx')
unlink('./dtk-bibliography/dtk-authoryear.dbx')
unlink('./dtk-bibliography/dtk-bibliography.pdf')
unlink('./dtk-bibliography/dtk-bibliography.tex')