Foreach-Schleifen in LaTeX TikZ

Mit dem listofitems-Paket lassen sich recht einfach „for jeden Eintrag in Liste“ Schleifen in LaTeX umsetzen. Hier ein Beispiel für das Setzen einer 10m Zielscheibe für Luftpistolen mit TikZ.

\documentclass[tikz,border=0.5cm]{standalone}
\usetikzlibrary{positioning} 
\renewcommand\familydefault{\sfdefault}
 
\usepackage{listofitems}
\setsepchar{;}
 
\begin{document}
\begin{tikzpicture}[font=\bfseries,thick]
%\draw[step=0.5cm,lightgray,thin] (0,0) grid (16,16);
 
\coordinate (o) at (8,8);
\draw[black,thick,fill=lightgray] (8,8) circle (29.75mm);
 
\readlist\distances{77.5;69.75;61.75;53.75;45.75;37.75;21.75;13.75;5.75}
\foreachitem\distance\in\distances{
	\draw[black] (8,8) circle (\distance mm);
}
 
\readlist\distances{7.1;6.3;5.5;4.7;3.9;3.1;2.3;1.5;0.7}
\readlist\directions{right;above;left;below}
 
\foreachitem\direction\in\directions{
	\foreachitem\distance\in\distances{
 
\node[\direction=\distance cm of o] {\distancecnt};
        }
}
 
\end{tikzpicture}
\end{document}

PDF

Lupi-10-1

Mit Python pandas CSV nach Excel konvertieren

Hier ein einfacher Code-Schnipsel, um aus CSV-Dateien Excel-Dateien zu machen.

Es empfiehlt sich auch, die Engine explizit zu setzen, mit der die Excel-Datei geschrieben wird. Standardmäßig nutzt pandas openpyxl, xlsxwriter scheint nach meinen Tests aber um ca. 30% schneller zu sein.

import pandas as pd
from openpyxl import Workbook
from openpyxl.utils.dataframe import dataframe_to_rows
 
 
# output control
files = ['']
 
wb = Workbook()
wb.remove(wb['Sheet'])
 
for index, file in enumerate(files,0):
    temp = pd.read_csv(file + '.csv',sep='\t')
    ws1 = wb.create_sheet()
    ws1.title = file
    rows = dataframe_to_rows(temp, index=False, header=True)
 
    for r_idx, row in enumerate(rows, 1):
        for c_idx, value in enumerate(row, 1):
            ws1.cell(row=r_idx, column=c_idx, value=value)
 
wb.save('files45646.xlsx')
wb.close()

Werte in pandas Dataframes ersetzen mit replace()

Aus aktuellem Anlass hier ein einfaches Beispiel, wie man Werte in pandas Dataframes ersetzen kann:

import pandas as pd
 
# initialize data of lists.
data = {'Hersteller': ['VW', 'BMW', 'VW', 'Porsche'],
        'Modell': ['Golf', '1er', 'Polo', '911']}
 
 
df = pd.DataFrame(data)
 
 
print(df,'\n')
 
 
df['Hersteller'] = df['Hersteller'].replace(
    {"VW": "Volkswagen", "Horch": "Audi"})
 
 
print(df)

DuckDB Beispiel-Code für Python

Hier ein Code-Beispiel für die wichtigsten Funktionen von DuckDB.

import duckdb as ddb
import pandas as pd
 
con = ddb.connect(':memory:')
 
con_p = ddb.connect('my_database.db')
 
con_p.execute('CREATE OR REPLACE TABLE telefonnummern(fullname VARCHAR,phone VARCHAR);')
 
 
con_p.execute("INSERT INTO telefonnummern VALUES ('Max Mustermann', '0123-4567890')")
 
print(con_p.sql('SHOW ALL TABLES'))
 
print(con_p.sql('SELECT * FROM telefonnummern;'))
 
 
ddb_object = con_p.sql('SELECT * FROM telefonnummern;')
 
df = ddb_object.to_df()
 
ddb_tuple = ddb_object.fetchall()
 
print(df)
 
print(ddb_tuple)

Python: print() durch ic() ersetzen

Hier ein paar Beispiele, wie man mit icecream print() Ausgaben ersetzen kann.

"""
icecream examples
"""
 
 
from icecream import ic
 
# define some function
def addiere(x, y):
    return x + y
 
# call ice
ic(addiere(1, 2))
 
# Output:
# ic| addiere(1, 2): 3
 
 
d = {'i': 2, 'j': 3, 'k': 4711}
ic(d['k'])
 
 
struct = {
    "hersteller": "VW",
    "modell": "Golf",
    "Farben": ["gelb", "rot"]
}
 
ic(struct)
 
ic.disable()
ic(struct) # no output
ic.enable()
 
 
def logstuff(text):
    # log to output file
    print(text)
 
 
ic.configureOutput(prefix="Hallo| ", outputFunction=logstuff)
 
ic(addiere(7, 7))
 
 
ic.configureOutput(prefix="Welt| ", outputFunction=logstuff)
 
ic(addiere(7, 7))

Iso-Papier mit LaTeX und dem gridpapers Paket

Hier ein Beispiel aus der gridpapers Doku, wie man mit LaTeX isometrische Papiere gestalten kann.

\documentclass{article}
\usepackage[pattern=iso, %tri für senkrecht
patternsize=0.5cm,
textarea,
bgcolor=white,
%majorcolor={green},
minorcolor={lightgray},
geometry={a4paper, margin=1cm}]{gridpapers}
\begin{document}
\thispagestyle{empty}
~
\end{document}

gridpapers-iso

gridpapers-tri

Drum Patterns mit LaTeX

Hier ein erster Entwurf, wie man mit LaTeX Drum Patters setzen kann.

\documentclass[12pt,ngerman]{scrartcl}
\usepackage[margin=1cm]{geometry}
\usepackage[T1]{fontenc}
\usepackage{babel}
\usepackage{graphicx}
\usepackage{xcolor}
\usepackage{tikz}
 
\begin{document}
 
\begin{tikzpicture}
    % Define the pattern as a comma-separated list
    \def\pattern{x,-,-,-,-,x,x,-,-,-,x,x,-,-,-,x}
    \def\cellsizex{1cm}
    \def\cellsizey{0.5cm}
 
    \node[draw, rectangle,minimum width = \cellsizex, align=left,minimum height=\cellsizey] (a) at (\cellsizex/2,\cellsizey/2){\tiny 1}; 
 
    % Loop through each character in the pattern
    \foreach \cell [count=\index from 1] in \pattern {
        % If 'x' fill the cell, otherwise leave it empty
        \if\cell x
            \fill[gray,draw=black] ({\index*\cellsizex}, 0) rectangle ++(\cellsizex, \cellsizey);
        \else
            \draw ({\index*\cellsizex}, 0) rectangle ++(\cellsizex, \cellsizey);
        \fi
    }
\end{tikzpicture}

drumbrute_pattern

VHS Videos digitalisieren, ein Erfahrungsbericht

Ich bin vor einigen Tagen gefragt worden, ob ich nicht das passende Equipment hätte, um eine VHS Kassette zu digitalisieren. Recht blauäugig hab ich sofort „klar“ gesagt, denn einiges habe ich in der Tat:

  • eine AverMedia HDMI Capture Karte für den PC
  • zwei Elgato HDMI-Grabber für USB
  • ein Kaico Edition OSSC Konverter, der aus SCART HDMI zaubert

Theoretisch war also alles vorhanden, doch grau ist alle Theorie!

Versuch Nummer 1

Meine Idee war, einfach den geborgten Video-Recorder an den OSSC anzuschließen, um von dort das Bild mit der Capture-Karte und OBS aufzunehmen. Es tat sich genau nichts, es gab kein Bild. Des Rätsels Lösung war, dass der Video-Recorder ein Composite-Videosignal ausgibt, mit dem der OSSC Konverter leider nichts anfangen kann. Das Ding ist nur für alte Konsolen und Heim-PCs gemacht, aber nicht für VHS.

Versuch Nummer 2

Weg mit dem Kaico, her mit einem SCART-auf-HDMI Adapter vom großen A, Kostenpunkt knapp 7 (!) Euro. Audio kam an, aber das Bild war meist kaputt: „No Signal“. Des Rätsels Lösung war, dass das Video-Signal leider zu schlecht war und immer wieder zusammenbrach. Damit kam der günstige Adapter leider nicht klar.

Versuch Nummer 3

Versuch Nummer 3 brachte dann den Erfolg. Zusammen mit dem Video-Recorder hatte ich auch noch einen „Canopus ADVC-300 Advanced Digital Video Converter“ in die Hand gedrückt bekommen, den ich erst komplett ignoriert hatte. Damit lassen sich analoge Videos per FireWire auf den Rechner sichern. FireWire Hardware hab ich zwar vielleicht noch rumliegen, aber nicht mehr aktiv im Einsatz. Jedoch ist FireWire nicht die einzige Schnittstelle, das Ding nimmt analoge Videos nicht nur an sondern kann diese auch wieder analog ausgeben. Dabei wird das Videosignal so weit stabilisiert, dass der Scart-auf-HDMI Adapter nichts zu nörgeln hatte.

Der finale Prozess sah daher so aus:

  • VHS Videorecorder
  • mit drei CINCH-Kabeln dann in den Canopus
  • mit drei CINCH-Kabeln dann vom Canopus in einen 3-Cinch auf SCART-Adapter
  • mit dem SCART-Adapter dann in den sehr preiswerten SCART-auf-HDMI Adapter
  • vom SCART-auf-HDMI Adapter per HDMI in den HDMI Grabber (AverMedia LiveGamer 2)
  • in OBS dann dann von der Grabber-Karte aufnehmen, vorher Leinwand auf 4:3 anpassen
  • als Ausgabeformat wird MKV empfohlen, alle Audioquellen außer die Grabber-Karte wurden aktiv deaktiviert (mute)

Mit Python camt.053 aus MT940 erzeugen

Für Dante e.V. bestand die Notwendigkeit, aus MT940 Dateien moderne CAMT.053 zu erzeugen, dank Python wurde das eine lösbare Aufgabe.

Schritt 1

Die MT940 Datei parsen und die Transaktionen in einen pandas DataFrame überführen.

import mt940
import pprint
import pandas as pd

df = pd.DataFrame()

transactions = mt940.parse('Umsaetze_2310007_22.07.2024.mta')

print('Transactions:')
pprint.pprint(transactions.data)

for transaction in transactions:
    print('Transaction: ', transaction)
    pprint.pprint(transaction.data)
    t = transaction.data
    tt = pd.DataFrame(t, index=[0])

    df = pd.concat([df,tt],ignore_index=True)
    
df.to_excel('AllBookings.xlsx',index=False)

Schritt 2

Aus dem DataFrame das XML befüllen, die für den Kopf der XML-Datei notwendigen Kontostandsinformationen holen ich dazu aus der MT940 Datei.

import pandas as pd # data wrangling
import jinja2 # template engine
import os # for file-related stuff
import mt940
from datetime import datetime


today = datetime.today()
now = today.strftime("%Y-%m-%d")



transactions = mt940.parse('Umsaetze_2310007_22.07.2024.mta')

opening = transactions.data['final_opening_balance']
openingamount = str(opening.amount)[:-4]
openingdate = opening.date

closing = transactions.data['final_closing_balance']
closingamount = str(closing.amount)[:-4]
closingdate = closing.date

 
# create jinja env that can load template from filesystem
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(os.path.abspath('.')))
 
df = pd.read_excel('AllBookings.xlsx', dtype={'date': str,'amount':str})

df['CreditDebit'] = ''
df['CreditDebit'] = df['CreditDebit'].where(df['amount'].str.get(0).isin(['-']), 'CRDT')
df['CreditDebit'] = df['CreditDebit'].where(~df['amount'].str.get(0).isin(['-']), 'DBIT')

df['date'] = df['date'].str[:-9]


# Währung weg
df['amount'] = df['amount'].str[:-4]
# Vorzeichen weg
df['amount'] = df['amount'].str.replace('-','')

#df['amount'].replace('-','',inplace=True)
#df["amount"] = df["amount"].apply(lambda x: x.str.replace("-", ""))

template = jinja_env.get_template('Ntry.xml')
 
with open('FertigesXML.xml','w') as output:
    output.write(template.render(data=df,
                                 openingamount=openingamount,
                                 openingdate=openingdate,
                                 closingamount=closingamount,
                                 closingdate=closingdate
                                 ))

Jinja2 XML-Template

Das XML-Template für Jinja2 findet ihr hier:

Ntry_blog

ASNs (Archive Serial Numbers) in LaTeX erzeugen

Ich experimentiere aktuell mit paperless-ngx, in der c’t war eine passende Anleitung für Docker auf Synology NAS, der ich gefolgt bin.

Um Bögen mit Archive Serial Numbers zu erstellen, will ich natürlich LaTeX nutzen, ticket.sty heißt hier das Zauberwort. (Es gibt auch web-basierte Generatoren, siehe https://tobiasmaier.info/asn-qr-code-label-generator/)

PDF Beispiel

Anbei der Quellcode für LaTeX, für den Druck auf andere Bögen als Avery Zweckform 4736 muss man das Format der Labels anpassen. Dann ist die LaTeX-Datei zweimal zu übersetzen.

\documentclass[a4paper,12pt]{scrartcl}
\usepackage[total={210mm,297mm},top=0mm,left=0mm,bottom=0mm,includefoot]{geometry}
\usepackage[ASN]{ticket} %boxed,cutmark during development
\usepackage{qrcode} 
\usepackage{forloop}
\usepackage[T1]{fontenc}
 
% https://tex.stackexchange.com/questions/716116/generate-sequential-padded-barcodes-with-qrcode?noredirect=1#comment1780003_716116
\makeatletter
\newcommand{\padnum}[2]{%
  \ifnum#1>1 \ifnum#2<10 0\fi
  \ifnum#1>2 \ifnum#2<100 0\fi
  \ifnum#1>3 \ifnum#2<1000 0\fi
  \ifnum#1>4 \ifnum#2<10000 0\fi
  \ifnum#1>5 \ifnum#2<100000 0\fi
  \ifnum#1>6 \ifnum#2<1000000 0\fi
  \ifnum#1>7 \ifnum#2<10000000 0\fi
  \ifnum#1>8 \ifnum#2<100000000 0\fi
  \ifnum#1>9 \ifnum#2<1000000000 0\fi
  \fi\fi\fi\fi\fi\fi\fi\fi\fi
  \expandafter\@firstofone\expandafter{\number#2}%
}
\makeatother
 
\begin{filecontents*}[overwrite]{ASN.tdf}
\unitlength=1mm
\hoffset=-16mm
\voffset=-8mm
\ticketNumbers{4}{12}
\ticketSize{45.7}{21.2} % Breite und Höhe der Labels in mm
\ticketDistance{2.5}{0} % Abstand der Labels
\end{filecontents*}
 
%reset background 
\renewcommand{\ticketdefault}{}%
 
 
\newcounter{asn}
% start value of the labels
\setcounter{asn}{1}
 
\newcommand{\mylabel}{
\ticket{%
\hspace*{4mm}\raisebox{9mm}[4mm][2mm]{%
\qrcode[height=1.4cm]{ASN\padnum{5}{\value{asn}}}%
~\texttt{\large ASN\padnum{5}{\value{asn}}}
}
\stepcounter{asn}
}
}
 
\begin{document}
 
% just for the loop
\newcounter{x}
% create 48 labels
\forloop{x}{1}{\value{x} < 49}{
\mylabel%
}
 
%print on Avery  Zweckform 4736
% in original size, not scaled to fit page
\end{document}