Audioguide QR-Codes können automatisch erzeugt werden

This commit is contained in:
2025-08-03 14:00:55 +02:00
parent 90f5229829
commit dd7f5ae34f
7 changed files with 422 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

+28
View File
@@ -96,6 +96,8 @@
tb_IconSize = new TrackBar(); tb_IconSize = new TrackBar();
cb_drawQuietZones = new CheckBox(); cb_drawQuietZones = new CheckBox();
lb_IconSIze = new Label(); lb_IconSIze = new Label();
tabPage5 = new TabPage();
bt_QR4AudioGuide = new Button();
tabControl1.SuspendLayout(); tabControl1.SuspendLayout();
tabPage1.SuspendLayout(); tabPage1.SuspendLayout();
tabPage2.SuspendLayout(); tabPage2.SuspendLayout();
@@ -104,6 +106,7 @@
((System.ComponentModel.ISupportInitialize)pb_QRC).BeginInit(); ((System.ComponentModel.ISupportInitialize)pb_QRC).BeginInit();
((System.ComponentModel.ISupportInitialize)pb_icon).BeginInit(); ((System.ComponentModel.ISupportInitialize)pb_icon).BeginInit();
((System.ComponentModel.ISupportInitialize)tb_IconSize).BeginInit(); ((System.ComponentModel.ISupportInitialize)tb_IconSize).BeginInit();
tabPage5.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// tabControl1 // tabControl1
@@ -112,6 +115,7 @@
tabControl1.Controls.Add(tabPage2); tabControl1.Controls.Add(tabPage2);
tabControl1.Controls.Add(tabPage3); tabControl1.Controls.Add(tabPage3);
tabControl1.Controls.Add(tabPage4); tabControl1.Controls.Add(tabPage4);
tabControl1.Controls.Add(tabPage5);
tabControl1.Location = new Point(12, 12); tabControl1.Location = new Point(12, 12);
tabControl1.Name = "tabControl1"; tabControl1.Name = "tabControl1";
tabControl1.SelectedIndex = 0; tabControl1.SelectedIndex = 0;
@@ -751,6 +755,27 @@
lb_IconSIze.TabIndex = 12; lb_IconSIze.TabIndex = 12;
lb_IconSIze.Text = "label17"; lb_IconSIze.Text = "label17";
// //
// tabPage5
//
tabPage5.Controls.Add(bt_QR4AudioGuide);
tabPage5.Location = new Point(4, 24);
tabPage5.Name = "tabPage5";
tabPage5.Padding = new Padding(3);
tabPage5.Size = new Size(412, 632);
tabPage5.TabIndex = 4;
tabPage5.Text = "QR-Codes für den Audioguide";
tabPage5.UseVisualStyleBackColor = true;
//
// bt_QR4AudioGuide
//
bt_QR4AudioGuide.Location = new Point(77, 36);
bt_QR4AudioGuide.Name = "bt_QR4AudioGuide";
bt_QR4AudioGuide.Size = new Size(261, 134);
bt_QR4AudioGuide.TabIndex = 0;
bt_QR4AudioGuide.Text = "Öffnen";
bt_QR4AudioGuide.UseVisualStyleBackColor = true;
bt_QR4AudioGuide.Click += bt_QR4AudioGuide_Click;
//
// Main // Main
// //
AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleDimensions = new SizeF(7F, 15F);
@@ -786,6 +811,7 @@
((System.ComponentModel.ISupportInitialize)pb_QRC).EndInit(); ((System.ComponentModel.ISupportInitialize)pb_QRC).EndInit();
((System.ComponentModel.ISupportInitialize)pb_icon).EndInit(); ((System.ComponentModel.ISupportInitialize)pb_icon).EndInit();
((System.ComponentModel.ISupportInitialize)tb_IconSize).EndInit(); ((System.ComponentModel.ISupportInitialize)tb_IconSize).EndInit();
tabPage5.ResumeLayout(false);
ResumeLayout(false); ResumeLayout(false);
PerformLayout(); PerformLayout();
} }
@@ -860,5 +886,7 @@
private TrackBar tb_IconSize; private TrackBar tb_IconSize;
private CheckBox cb_drawQuietZones; private CheckBox cb_drawQuietZones;
private Label lb_IconSIze; private Label lb_IconSIze;
private TabPage tabPage5;
private Button bt_QR4AudioGuide;
} }
} }
+6
View File
@@ -322,5 +322,11 @@ namespace QR_Code_Generator
tb_IconSize.Value = iconSizePercent; tb_IconSize.Value = iconSizePercent;
lb_IconSIze.Text = $"{iconSizePercent}%"; lb_IconSIze.Text = $"{iconSizePercent}%";
} }
private void bt_QR4AudioGuide_Click(object sender, EventArgs e)
{
QR4AudioGuide qR4AudioGuide = new QR4AudioGuide();
qR4AudioGuide.Show();
}
} }
} }
@@ -10,8 +10,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentFTP" Version="53.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="QRCoder" Version="1.6.0" /> <PackageReference Include="QRCoder" Version="1.6.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="Icon_Audioguid.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>
+88
View File
@@ -0,0 +1,88 @@
namespace QR_Code_Generator
{
partial class QR4AudioGuide
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
bt_connect = new Button();
dataGridView1 = new DataGridView();
bt_Save = new Button();
((System.ComponentModel.ISupportInitialize)dataGridView1).BeginInit();
SuspendLayout();
//
// bt_connect
//
bt_connect.Location = new Point(12, 12);
bt_connect.Name = "bt_connect";
bt_connect.Size = new Size(163, 23);
bt_connect.TabIndex = 0;
bt_connect.Text = "Verbinden und listen";
bt_connect.UseVisualStyleBackColor = true;
bt_connect.Click += bt_connect_Click;
//
// dataGridView1
//
dataGridView1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView1.Location = new Point(12, 41);
dataGridView1.Name = "dataGridView1";
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.Size = new Size(776, 473);
dataGridView1.TabIndex = 3;
dataGridView1.CellContentClick += dataGridView1_CellContentClick;
//
// bt_Save
//
bt_Save.Location = new Point(181, 12);
bt_Save.Name = "bt_Save";
bt_Save.Size = new Size(234, 23);
bt_Save.TabIndex = 4;
bt_Save.Text = "QR-Codes erzeugen und speichern";
bt_Save.UseVisualStyleBackColor = true;
bt_Save.Click += bt_Save_Click;
//
// QR4AudioGuide
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 520);
Controls.Add(bt_Save);
Controls.Add(dataGridView1);
Controls.Add(bt_connect);
Name = "QR4AudioGuide";
Text = "QR4AudioGuide";
((System.ComponentModel.ISupportInitialize)dataGridView1).EndInit();
ResumeLayout(false);
}
#endregion
private Button bt_connect;
private DataGridView dataGridView1;
private Button bt_Save;
}
}
+173
View File
@@ -0,0 +1,173 @@
using FluentFTP;
using QRCoder;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static QRCoder.QRCodeGenerator;
namespace QR_Code_Generator
{
public partial class QR4AudioGuide : Form
{
private bool drawQuietZones = true;
private bool forceUtf8 = true;
private int pixlesPerModul = 20;
private int iconSizePercent = 29;
private int iconBorderWidth = 5;
private QRCodeGenerator.ECCLevel eCCLevel = QRCodeGenerator.ECCLevel.Q;
List<Bitmap> qrCodeImages = new List<Bitmap>();
AsyncFtpClient client = new AsyncFtpClient("astronomiemuseum.de", "w01d9fc3", "7sFv4vQJEQ3RTm3a");
public QR4AudioGuide()
{
InitializeComponent();
}
private void bt_connect_Click(object sender, EventArgs e)
{
list();
}
private async void list()
{
var dirs = await ListAllDirectoriesAsync("astronomiemuseum.de", "w01d9fc3", "7sFv4vQJEQ3RTm3a", "/audioguide.astromuseum.de");
dataGridView1.Columns.Clear();
dataGridView1.Columns.Add("Verzeichnis", "Verzeichnis");
var linkColumn = new DataGridViewLinkColumn();
linkColumn.HeaderText = "Player-URL";
linkColumn.Name = "URL";
linkColumn.LinkBehavior = LinkBehavior.HoverUnderline;
linkColumn.UseColumnTextForLinkValue = false;
dataGridView1.Columns.Add(linkColumn);
string baseUrl = "https://audioguide.astromuseum.de/player/index.php?dir=";
foreach (var fullPath in dirs)
{
// 🛑 Skip alles unter /bootstrap
if (fullPath.Contains("/bootstrap") || fullPath.Contains("/player")) continue;
string dir = fullPath.Split('/').Last();
string url = baseUrl + Uri.EscapeDataString(dir);
dataGridView1.Rows.Add(dir, url);
}
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
}
public async Task<List<string>> ListAllDirectoriesAsync(string host, string user, string pass, string rootPath = "/")
{
var dirs = new List<string>();
var token = new CancellationToken();
using var client = new AsyncFtpClient(host, user, pass);
await client.Connect(token);
await ScanDirsRecursive(client, rootPath, dirs, token);
await client.Disconnect(token);
return dirs;
}
private async Task ScanDirsRecursive(AsyncFtpClient client, string path, List<string> result, CancellationToken token)
{
var items = await client.GetListing(path, FtpListOption.ForceList, token);
foreach (var item in items)
{
if (item.Type == FtpObjectType.Directory)
{
result.Add(item.FullName);
await ScanDirsRecursive(client, item.FullName, result, token);
}
}
}
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && dataGridView1.Columns[e.ColumnIndex] is DataGridViewLinkColumn)
{
string url = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value?.ToString();
if (!string.IsNullOrWhiteSpace(url))
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = url,
UseShellExecute = true
});
}
}
}
public void GenerateQRCodesFromSelectedRows(DataGridView dgv)
{
qrCodeImages.Clear();
foreach (DataGridViewRow row in dgv.SelectedRows)
{
if (row.Cells["URL"].Value is string url && !string.IsNullOrWhiteSpace(url))
{
string urlPayload = new PayloadGenerator.Url(url).ToString();
Bitmap qrCode = GenerateQRCode(urlPayload, Color.Black, Color.White, eCCLevel);
}
}
}
public void SaveSelectedQRCodes(DataGridView dgv, ECCLevel eCCLevel)
{
using (var folderDialog = new FolderBrowserDialog())
{
if (folderDialog.ShowDialog() != DialogResult.OK)
return;
string selectedPath = folderDialog.SelectedPath;
foreach (DataGridViewRow row in dgv.SelectedRows)
{
if (row.Cells["URL"].Value is string url && !string.IsNullOrWhiteSpace(url))
{
string dateiname = row.Cells[0].Value?.ToString() ?? "QR";
string safeName = string.Join("_", dateiname.Split(Path.GetInvalidFileNameChars())); // Dateinamen absichern
string urlPayload = new PayloadGenerator.Url(url).ToString();
Bitmap qrCode = GenerateQRCode(urlPayload, Color.Black, Color.White, eCCLevel);
string filePath = Path.Combine(selectedPath, safeName + ".png");
qrCode.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);
}
}
MessageBox.Show("QR-Codes gespeichert!", "Fertig", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private Bitmap GenerateQRCode(string payload, Color dark, Color light, QRCodeGenerator.ECCLevel eCC)
{
using QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrGenerator.CreateQrCode(payload, eCC, forceUtf8);
using QRCode qrCode = new QRCode(qrCodeData);
// Prüfen, ob ein Icon vorhanden ist
Bitmap? icon = Image.FromFile("Icon_Audioguid.png") as Bitmap;
return qrCode.GetGraphic(
pixlesPerModul,
dark,
light, icon, iconSizePercent, iconBorderWidth, drawQuietZones, null);
}
private void bt_Save_Click(object sender, EventArgs e)
{
SaveSelectedQRCodes(dataGridView1, eCCLevel);
}
}
}
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>