diff --git a/README.md b/README.md index a57339b..8605cce 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Vzdělávací aplikace k přípravě na maturitu (repo `Graduately`). / An educa Rozhraní je v češtině a angličtině. Otevřené předměty jsou v seznamu níže; ostatní jsou zatím zamčené. Aplikace se po instalaci aktualizuje sama. -| Úvod | Předměty | Německá cesta | -| ---- | -------- | ------------- | -| ![Úvod](assets/welcomescreen.png) | ![Předměty](assets/subjectsscreen.png) | ![Německá cesta](assets/germanlection.png) | +| Úvod | Němčina | Neue Freunde | +| ---- | -------- | ------------ | +| ![Úvod](assets/home.png) | ![Němčina](assets/deutsch.png) | ![Neue Freunde](assets/neue-freunde.png) | ### Instalace @@ -71,6 +71,15 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses - **Technické vybavení** — 4 ročníky: 1. ročník má 29 lekcí (architektura, data, skříň, deska, karty, disky a vstup), ročníky 2–4 po 8 lekcích (monitory, sestavení, RAID, servery, údržba) +- **Technická grafika** — 4 ročníky AutoCADu, v každém 8 lekcí: + výkres a souřadnice, úpravy a kóty, pohledy a tisk, 3D a maturitní zadání +- **Prezentační grafika** — 4 ročníky, v každém 8 lekcí: Photoshop od pixelu + po plakát, Premiere Pro a ve čtvrtém ročníku Illustrator, After Effects + a InDesign +- **Programové vybavení** — 4 ročníky, v každém 8 lekcí: Windows a licence, + Word a Excel, Access a MySQL, pak WordPress, Power BI, dávkové soubory a zásady +- **Programování** — 4 ročníky C# po vzoru W3Schools. U každé lekce se kód + píše a spouští vedle výkladu, od výpisu po třídy, LINQ a async - **Český jazyk a literatura** — literatura (4 ročníky, 32 lekcí podle osnov středních škol), mluvnice (20 cvičení) a maturitní četba (82 knih: zápisky, kvíz, sestavení děje) @@ -90,10 +99,10 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses ### Použití -1. **Pokračuj** otevře mapu předmětů. -2. Otevřený předmět (vlajka, Wi‑Fi, čip, česká vlajka, váhy, funkce, atom, baňka) vede na učební cestu. -3. Uzly cvičení / lekcí otevřou obsah; zamčené nic nedělají. -4. **Hledat** (lupa), **Statistiky** (graf) a **Nastavení** (ozubené kolo) jsou vpravo nahoře. +1. **Úvod** ukáže postup a mřížku předmětů. Každý předmět spustí svou cestu. +2. U němčiny je učebnice (Neue Freunde, Aus aller Welt, Bei uns zu Hause) a čtyři ročníky. Ostatní otevřené předměty mají ročníky, lekce nebo knihy. +3. Z cesty se otevře výklad, poslech, čtení, psaní nebo kvíz. +4. **Statistiky**, **Nastavení** a **Hledat** jsou v postranním panelu na Macu. Na telefonu a v desktopové aplikaci jsou na záložkách, hledání navíc ikonou lupy. Zkratka je `Cmd/Ctrl+K`. ### Sestavení ze zdroje @@ -198,9 +207,9 @@ Licence: [GPL-3.0](LICENSE). The UI is Czech and English. Open subjects are listed below; the rest are still locked. After install the app updates itself. -| Welcome | Subjects | German path | -| ------- | -------- | ----------- | -| ![Welcome](assets/welcomescreen.png) | ![Subjects](assets/subjectsscreen.png) | ![German path](assets/germanlection.png) | +| Welcome | German | Neue Freunde | +| ------- | ------ | ----------- | +| ![Welcome](assets/home.png) | ![German](assets/deutsch.png) | ![Neue Freunde](assets/neue-freunde.png) | ### Install @@ -259,6 +268,10 @@ Intel Mac, another architecture, or hacking on the code → - **Computer hardware** — 4 years: year 1 has 29 lessons (architecture, data, case, board, cards, drives and input), years 2–4 have 8 lessons each (displays, assembly, RAID, servers, maintenance) +- **Technical graphics** — 4 years of AutoCAD, 8 lessons each: + the sheet and coordinates, editing and dimensions, views and plotting, 3D and the exam drawing +- **Programming** — 4 years of C# in the style of W3Schools. Each lesson has + code you edit and run beside the explanation, from output to classes, LINQ and async - **Czech language** — literature (4 years, 32 lessons from the secondary-school syllabus), grammar (20 exercises) and required reading (82 books: notes, quiz, plot ordering) @@ -279,10 +292,10 @@ Intel Mac, another architecture, or hacking on the code → ### Usage -1. **Continue** opens the subject map. -2. An open subject (flag, Wi‑Fi, chip, Czech flag, scales, function, atom, flask) opens its path. -3. Exercise / lesson nodes open content; locked nodes do nothing. -4. **Search** (magnifier), **Statistics** (chart) and **Settings** (gear) sit in the top-right. +1. **Welcome** shows progress and the subject grid. An open subject starts its path. Locked tiles do nothing yet: presentation graphics and software. +2. German has the textbook (Neue Freunde, Aus aller Welt, Bei uns zu Hause) and four years. The other open subjects have years, lessons or books. +3. A path opens an overview, listening, reading, writing or a quiz. +4. **Statistics**, **Settings** and **Search** sit in the sidebar on Mac. On the phone and the desktop app they are tabs, with search also on the magnifier. The shortcut is `Cmd/Ctrl+K`. ### Build from source diff --git a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/CSharpLab.kt b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/CSharpLab.kt new file mode 100644 index 0000000..ad7f5f1 --- /dev/null +++ b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/CSharpLab.kt @@ -0,0 +1,128 @@ +package org.maturita.maturita.desktop + +import org.maturita.maturita.platform.CSharpRun +import java.io.ByteArrayOutputStream +import java.io.File +import java.nio.charset.StandardCharsets +import java.util.concurrent.TimeUnit + +/** One cached console project. The student's program replaces Program.cs and `dotnet run` executes it. */ +object CSharpLab { + private val gate = Object() + + fun run(source: String, stdin: String): CSharpRun { + if (source.length > 100_000) return CSharpRun("fail", "Program je příliš dlouhý.") + val dotnet = findDotnet() ?: return CSharpRun("unavailable", "") + synchronized(gate) { + return try { + execute(dotnet, source, stdin) + } catch (e: Exception) { + CSharpRun("fail", e.message ?: "Spuštění selhalo.") + } + } + } + + private fun execute(dotnet: String, source: String, stdin: String): CSharpRun { + val dir = File(System.getProperty("user.home"), ".graduately/csharp-lab") + dir.mkdirs() + val csproj = File(dir, "Lab.csproj") + if (!csproj.isFile) { + csproj.writeText(projectFile(targetFramework(dotnet)), StandardCharsets.UTF_8) + val restored = command(dotnet, listOf("restore", dir.absolutePath), dir, "", 90) + if (restored.status != "ok") return restored + } + File(dir, "Program.cs").writeText(source, StandardCharsets.UTF_8) + val args = mutableListOf("run", "--project", dir.absolutePath, "--verbosity", "quiet") + if (File(dir, "obj").isDirectory) args += "--no-restore" + return command(dotnet, args, dir, stdin, 20) + } + + private fun command(dotnet: String, args: List, dir: File, stdin: String, seconds: Long): CSharpRun { + val process = ProcessBuilder(listOf(dotnet) + args) + .directory(dir) + .redirectErrorStream(true) + .apply { + environment().clear() + environment().putAll(environmentFor(dotnet)) + } + .start() + process.outputStream.use { it.write(stdin.toByteArray(StandardCharsets.UTF_8)) } + val output = ByteArrayOutputStream() + val reader = Thread { + process.inputStream.use { it.copyTo(output) } + } + reader.isDaemon = true + reader.start() + val finished = process.waitFor(seconds, TimeUnit.SECONDS) + if (!finished) { + process.descendants().forEach { it.destroyForcibly() } + process.destroyForcibly() + process.waitFor(2, TimeUnit.SECONDS) + reader.join(1000) + return CSharpRun("timeout", clip(output.toString(StandardCharsets.UTF_8))) + } + reader.join(2000) + val text = clip(output.toString(StandardCharsets.UTF_8)).trim() + return if (process.exitValue() == 0) CSharpRun("ok", text) else CSharpRun("fail", text) + } + + private fun clip(text: String): String = + if (text.length <= 16_000) text else text.take(16_000) + "\n…" + + private fun environmentFor(dotnet: String): Map { + val home = System.getProperty("user.home") + val root = File(dotnet).parent + val path = listOf(root, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin") + .joinToString(File.pathSeparator) + val env = mutableMapOf( + "PATH" to path, + "HOME" to home, + "USERPROFILE" to home, + "DOTNET_ROOT" to root, + "DOTNET_CLI_TELEMETRY_OPTOUT" to "1", + "DOTNET_NOLOGO" to "1", + "DOTNET_SKIP_FIRST_TIME_EXPERIENCE" to "1", + "TEMP" to System.getProperty("java.io.tmpdir"), + "TMP" to System.getProperty("java.io.tmpdir"), + ) + System.getenv("SystemRoot")?.let { env["SystemRoot"] = it } + System.getenv("ComSpec")?.let { env["ComSpec"] = it } + return env + } + + private fun targetFramework(dotnet: String): String { + val version = command(dotnet, listOf("--version"), File(System.getProperty("java.io.tmpdir")), "", 20) + val major = version.output.trim().substringBefore('.').filter { it.isDigit() } + return if (major.isEmpty()) "net8.0" else "net$major.0" + } + + private fun projectFile(framework: String) = """ + + + Exe + $framework + enable + disable + latest + + + """.trimIndent() + + private fun findDotnet(): String? { + val home = System.getProperty("user.home") + val programFiles = System.getenv("ProgramFiles") ?: "C:\\Program Files" + val names = listOf( + "/usr/local/share/dotnet/dotnet", + "/usr/share/dotnet/dotnet", + "/opt/homebrew/share/dotnet/dotnet", + "$home/.dotnet/dotnet", + "$programFiles\\dotnet\\dotnet.exe", + ) + names.firstOrNull { File(it).canExecute() }?.let { return it } + val path = System.getenv("PATH") ?: return null + return path.split(File.pathSeparator) + .map { File(it, if (System.getProperty("os.name").startsWith("Windows")) "dotnet.exe" else "dotnet") } + .firstOrNull { it.canExecute() } + ?.absolutePath + } +} diff --git a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt index ced42c6..c61e57c 100644 --- a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt +++ b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/DesktopPlatform.kt @@ -1,8 +1,10 @@ package org.maturita.maturita.desktop +import org.maturita.maturita.platform.CSharpRun import org.maturita.maturita.platform.Platform import org.maturita.maturita.platform.Prefs import org.maturita.maturita.platform.Speaker +import javax.swing.SwingUtilities class DesktopPlatform( override val commit: String, @@ -14,4 +16,11 @@ class DesktopPlatform( override val updater = DesktopUpdater(commit, install) override fun speaker(): Speaker? = DesktopSpeaker.create() + + override fun runCSharp(source: String, stdin: String, cb: (CSharpRun) -> Unit) { + Thread { + val result = CSharpLab.run(source, stdin) + SwingUtilities.invokeLater { cb(result) } + }.apply { isDaemon = true }.start() + } } diff --git a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt index 98d77e9..aa9a704 100644 --- a/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt +++ b/android/desktop/src/main/kotlin/org/maturita/maturita/desktop/Main.kt @@ -40,6 +40,8 @@ fun main() { val info = Properties().apply { resourceText("build-info.properties")?.let { load(it.reader()) } } val platform = DesktopPlatform(info.getProperty("commit") ?: "dev", prefs, install) val vm = AppViewModel(Content.parse(resourceText("content.json")), platform) + // `GRADUATELY_PAGE=u1e3 make run` opens a page, for screenshots. + System.getenv("GRADUATELY_PAGE")?.takeIf { it.isNotBlank() }?.let { vm.openPage(it) } val icon = Thread.currentThread().contextClassLoader.getResourceAsStream("icon.png") ?.use { ImageIO.read(it) }?.let { BitmapPainter(it.toComposeImageBitmap()) } diff --git a/android/shared/src/org/maturita/maturita/App.kt b/android/shared/src/org/maturita/maturita/App.kt index 7f796e1..27eef9d 100644 --- a/android/shared/src/org/maturita/maturita/App.kt +++ b/android/shared/src/org/maturita/maturita/App.kt @@ -23,6 +23,8 @@ import org.maturita.maturita.data.ColorMode import org.maturita.maturita.data.Palette import org.maturita.maturita.ui.BookListScreen import org.maturita.maturita.ui.BookScreen +import org.maturita.maturita.ui.CodeLessonScreen +import org.maturita.maturita.ui.CsYearsScreen import org.maturita.maturita.ui.CzechMapScreen import org.maturita.maturita.ui.GermanExercise import org.maturita.maturita.ui.HomeScreen @@ -30,6 +32,12 @@ import org.maturita.maturita.ui.HwQuizScreen import org.maturita.maturita.ui.HwYearsScreen import org.maturita.maturita.ui.LessonListScreen import org.maturita.maturita.ui.FyzQuizScreen +import org.maturita.maturita.ui.PgQuizScreen +import org.maturita.maturita.ui.PgYearsScreen +import org.maturita.maturita.ui.SwQuizScreen +import org.maturita.maturita.ui.SwYearsScreen +import org.maturita.maturita.ui.TgQuizScreen +import org.maturita.maturita.ui.TgYearsScreen import org.maturita.maturita.ui.MathPracticeScreen import org.maturita.maturita.ui.MatYearsScreen import org.maturita.maturita.ui.FyzYearsScreen @@ -525,6 +533,222 @@ private fun MaturitaAppBody(vm: AppViewModel) { } } is Route.Fyz4Ex -> FyzQuizScreen(vm, 4, r.id) + Route.TgYears -> TgYearsScreen(vm) + Route.TgMap -> LessonListScreen( + vm, vm.tr("tg_year1"), vm.tr("tg_sub"), + vm.content.tg.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tgDone(id), Route.TgLesson(id)) + }, + ) + is Route.TgLesson -> { + val l = vm.content.tgLesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.TgEx(r.id)) + } + } + is Route.TgEx -> TgQuizScreen(vm, 1, r.id) + Route.Tg2Map -> LessonListScreen( + vm, vm.tr("tg_year2"), vm.tr("tg2_sub"), + vm.content.tg2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg2Done(id), Route.Tg2Lesson(id)) + }, + ) + is Route.Tg2Lesson -> { + val l = vm.content.tg2Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Tg2Ex(r.id)) + } + } + is Route.Tg2Ex -> TgQuizScreen(vm, 2, r.id) + Route.Tg3Map -> LessonListScreen( + vm, vm.tr("tg_year3"), vm.tr("tg3_sub"), + vm.content.tg3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg3Done(id), Route.Tg3Lesson(id)) + }, + ) + is Route.Tg3Lesson -> { + val l = vm.content.tg3Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Tg3Ex(r.id)) + } + } + is Route.Tg3Ex -> TgQuizScreen(vm, 3, r.id) + Route.Tg4Map -> LessonListScreen( + vm, vm.tr("tg_year4"), vm.tr("tg4_sub"), + vm.content.tg4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.tg4Done(id), Route.Tg4Lesson(id)) + }, + ) + is Route.Tg4Lesson -> { + val l = vm.content.tg4Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Tg4Ex(r.id)) + } + } + is Route.Tg4Ex -> TgQuizScreen(vm, 4, r.id) + Route.PgYears -> PgYearsScreen(vm) + Route.PgMap -> LessonListScreen( + vm, vm.tr("pg_year1"), vm.tr("pg_sub"), + vm.content.pg.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pgDone(id), Route.PgLesson(id)) + }, + ) + is Route.PgLesson -> { + val l = vm.content.pgLesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.PgEx(r.id)) + } + } + is Route.PgEx -> PgQuizScreen(vm, 1, r.id) + Route.Pg2Map -> LessonListScreen( + vm, vm.tr("pg_year2"), vm.tr("pg2_sub"), + vm.content.pg2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pg2Done(id), Route.Pg2Lesson(id)) + }, + ) + is Route.Pg2Lesson -> { + val l = vm.content.pg2Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Pg2Ex(r.id)) + } + } + is Route.Pg2Ex -> PgQuizScreen(vm, 2, r.id) + Route.Pg3Map -> LessonListScreen( + vm, vm.tr("pg_year3"), vm.tr("pg3_sub"), + vm.content.pg3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pg3Done(id), Route.Pg3Lesson(id)) + }, + ) + is Route.Pg3Lesson -> { + val l = vm.content.pg3Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Pg3Ex(r.id)) + } + } + is Route.Pg3Ex -> PgQuizScreen(vm, 3, r.id) + Route.Pg4Map -> LessonListScreen( + vm, vm.tr("pg_year4"), vm.tr("pg4_sub"), + vm.content.pg4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.pg4Done(id), Route.Pg4Lesson(id)) + }, + ) + is Route.Pg4Lesson -> { + val l = vm.content.pg4Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Pg4Ex(r.id)) + } + } + is Route.Pg4Ex -> PgQuizScreen(vm, 4, r.id) + Route.SwYears -> SwYearsScreen(vm) + Route.SwMap -> LessonListScreen( + vm, vm.tr("sw_year1"), vm.tr("sw_sub"), + vm.content.sw.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.swDone(id), Route.SwLesson(id)) + }, + ) + is Route.SwLesson -> { + val l = vm.content.swLesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.SwEx(r.id)) + } + } + is Route.SwEx -> SwQuizScreen(vm, 1, r.id) + Route.Sw2Map -> LessonListScreen( + vm, vm.tr("sw_year2"), vm.tr("sw2_sub"), + vm.content.sw2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.sw2Done(id), Route.Sw2Lesson(id)) + }, + ) + is Route.Sw2Lesson -> { + val l = vm.content.sw2Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Sw2Ex(r.id)) + } + } + is Route.Sw2Ex -> SwQuizScreen(vm, 2, r.id) + Route.Sw3Map -> LessonListScreen( + vm, vm.tr("sw_year3"), vm.tr("sw3_sub"), + vm.content.sw3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.sw3Done(id), Route.Sw3Lesson(id)) + }, + ) + is Route.Sw3Lesson -> { + val l = vm.content.sw3Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Sw3Ex(r.id)) + } + } + is Route.Sw3Ex -> SwQuizScreen(vm, 3, r.id) + Route.Sw4Map -> LessonListScreen( + vm, vm.tr("sw_year4"), vm.tr("sw4_sub"), + vm.content.sw4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.sw4Done(id), Route.Sw4Lesson(id)) + }, + ) + is Route.Sw4Lesson -> { + val l = vm.content.sw4Lesson(r.id) + if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) { + vm.replace(Route.Sw4Ex(r.id)) + } + } + is Route.Sw4Ex -> SwQuizScreen(vm, 4, r.id) + Route.CsYears -> CsYearsScreen(vm) + Route.CsMap -> LessonListScreen( + vm, vm.tr("cs_year1"), vm.tr("cs_sub"), + vm.content.cs.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.csDone(id), Route.CsLesson(id)) + }, + ) + is Route.CsLesson -> { + val l = vm.content.csLesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs(r.id) } + } + Route.Cs2Map -> LessonListScreen( + vm, vm.tr("cs_year2"), vm.tr("cs2_sub"), + vm.content.cs2.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs2Done(id), Route.Cs2Lesson(id)) + }, + ) + is Route.Cs2Lesson -> { + val l = vm.content.cs2Lesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs2(r.id) } + } + Route.Cs3Map -> LessonListScreen( + vm, vm.tr("cs_year3"), vm.tr("cs3_sub"), + vm.content.cs3.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs3Done(id), Route.Cs3Lesson(id)) + }, + ) + is Route.Cs3Lesson -> { + val l = vm.content.cs3Lesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs3(r.id) } + } + Route.Cs4Map -> LessonListScreen( + vm, vm.tr("cs_year4"), vm.tr("cs4_sub"), + vm.content.cs4.map { l -> + val id = l.int("id") + LessonRow(vm.tr(l.str("titleKey")), vm.progress.cs4Done(id), Route.Cs4Lesson(id)) + }, + ) + is Route.Cs4Lesson -> { + val l = vm.content.cs4Lesson(r.id) + if (l != null) CodeLessonScreen(vm, l) { vm.markCs4(r.id) } + } Route.SciMap -> SciMapScreen(vm) Route.ChemMap -> LessonListScreen( vm, vm.tr("Chemie"), vm.tr("chem_sub"), diff --git a/android/shared/src/org/maturita/maturita/AppViewModel.kt b/android/shared/src/org/maturita/maturita/AppViewModel.kt index 03fd3ea..4be6a80 100644 --- a/android/shared/src/org/maturita/maturita/AppViewModel.kt +++ b/android/shared/src/org/maturita/maturita/AppViewModel.kt @@ -242,6 +242,86 @@ class AppViewModel(val content: Content, val platform: Platform) { refresh() } + fun markTg(id: Int) { + progress.markTg(id) + refresh() + } + + fun markTg2(id: Int) { + progress.markTg2(id) + refresh() + } + + fun markTg3(id: Int) { + progress.markTg3(id) + refresh() + } + + fun markTg4(id: Int) { + progress.markTg4(id) + refresh() + } + + fun markPg(id: Int) { + progress.markPg(id) + refresh() + } + + fun markPg2(id: Int) { + progress.markPg2(id) + refresh() + } + + fun markPg3(id: Int) { + progress.markPg3(id) + refresh() + } + + fun markPg4(id: Int) { + progress.markPg4(id) + refresh() + } + + fun markSw(id: Int) { + progress.markSw(id) + refresh() + } + + fun markSw2(id: Int) { + progress.markSw2(id) + refresh() + } + + fun markSw3(id: Int) { + progress.markSw3(id) + refresh() + } + + fun markSw4(id: Int) { + progress.markSw4(id) + refresh() + } + + fun markCs(id: Int) { + progress.markCs(id) + refresh() + } + + fun markCs2(id: Int) { + progress.markCs2(id) + refresh() + } + + fun markCs3(id: Int) { + progress.markCs3(id) + refresh() + } + + fun markCs4(id: Int) { + progress.markCs4(id) + refresh() + } + fun markMluv(n: Int) { progress.markMluv(n) refresh() diff --git a/android/shared/src/org/maturita/maturita/Route.kt b/android/shared/src/org/maturita/maturita/Route.kt index c90685e..af79af8 100644 --- a/android/shared/src/org/maturita/maturita/Route.kt +++ b/android/shared/src/org/maturita/maturita/Route.kt @@ -105,6 +105,54 @@ sealed class Route(val page: String) { data object Fyz4Map : Route("fyz4map") data class Fyz4Lesson(val id: Int) : Route("fyz4unit$id") data class Fyz4Ex(val id: Int) : Route("fyz4ex$id") + data object TgYears : Route("tgyears") + data object TgMap : Route("tgmap") + data class TgLesson(val id: Int) : Route("tgunit$id") + data class TgEx(val id: Int) : Route("tgex$id") + data object Tg2Map : Route("tg2map") + data class Tg2Lesson(val id: Int) : Route("tg2unit$id") + data class Tg2Ex(val id: Int) : Route("tg2ex$id") + data object Tg3Map : Route("tg3map") + data class Tg3Lesson(val id: Int) : Route("tg3unit$id") + data class Tg3Ex(val id: Int) : Route("tg3ex$id") + data object Tg4Map : Route("tg4map") + data class Tg4Lesson(val id: Int) : Route("tg4unit$id") + data class Tg4Ex(val id: Int) : Route("tg4ex$id") + data object PgYears : Route("pgyears") + data object PgMap : Route("pgmap") + data class PgLesson(val id: Int) : Route("pgunit$id") + data class PgEx(val id: Int) : Route("pgex$id") + data object Pg2Map : Route("pg2map") + data class Pg2Lesson(val id: Int) : Route("pg2unit$id") + data class Pg2Ex(val id: Int) : Route("pg2ex$id") + data object Pg3Map : Route("pg3map") + data class Pg3Lesson(val id: Int) : Route("pg3unit$id") + data class Pg3Ex(val id: Int) : Route("pg3ex$id") + data object Pg4Map : Route("pg4map") + data class Pg4Lesson(val id: Int) : Route("pg4unit$id") + data class Pg4Ex(val id: Int) : Route("pg4ex$id") + data object SwYears : Route("swyears") + data object SwMap : Route("swmap") + data class SwLesson(val id: Int) : Route("swunit$id") + data class SwEx(val id: Int) : Route("swex$id") + data object Sw2Map : Route("sw2map") + data class Sw2Lesson(val id: Int) : Route("sw2unit$id") + data class Sw2Ex(val id: Int) : Route("sw2ex$id") + data object Sw3Map : Route("sw3map") + data class Sw3Lesson(val id: Int) : Route("sw3unit$id") + data class Sw3Ex(val id: Int) : Route("sw3ex$id") + data object Sw4Map : Route("sw4map") + data class Sw4Lesson(val id: Int) : Route("sw4unit$id") + data class Sw4Ex(val id: Int) : Route("sw4ex$id") + data object CsYears : Route("csyears") + data object CsMap : Route("csmap") + data class CsLesson(val id: Int) : Route("csunit$id") + data object Cs2Map : Route("cs2map") + data class Cs2Lesson(val id: Int) : Route("cs2unit$id") + data object Cs3Map : Route("cs3map") + data class Cs3Lesson(val id: Int) : Route("cs3unit$id") + data object Cs4Map : Route("cs4map") + data class Cs4Lesson(val id: Int) : Route("cs4unit$id") data object CzechMap : Route("czechmap") data object Mluvnice : Route("mluvnice") data class MluvEx(val n: Int) : Route("mluve$n") @@ -237,6 +285,54 @@ sealed class Route(val page: String) { Fyz4Map -> listOf(Subjects, FyzYears, Fyz4Map) is Fyz4Lesson -> listOf(Subjects, FyzYears, Fyz4Map, this) is Fyz4Ex -> listOf(Subjects, FyzYears, Fyz4Map, Fyz4Lesson(id), this) + TgYears -> listOf(Subjects, TgYears) + TgMap -> listOf(Subjects, TgYears, TgMap) + is TgLesson -> listOf(Subjects, TgYears, TgMap, this) + is TgEx -> listOf(Subjects, TgYears, TgMap, TgLesson(id), this) + Tg2Map -> listOf(Subjects, TgYears, Tg2Map) + is Tg2Lesson -> listOf(Subjects, TgYears, Tg2Map, this) + is Tg2Ex -> listOf(Subjects, TgYears, Tg2Map, Tg2Lesson(id), this) + Tg3Map -> listOf(Subjects, TgYears, Tg3Map) + is Tg3Lesson -> listOf(Subjects, TgYears, Tg3Map, this) + is Tg3Ex -> listOf(Subjects, TgYears, Tg3Map, Tg3Lesson(id), this) + Tg4Map -> listOf(Subjects, TgYears, Tg4Map) + is Tg4Lesson -> listOf(Subjects, TgYears, Tg4Map, this) + is Tg4Ex -> listOf(Subjects, TgYears, Tg4Map, Tg4Lesson(id), this) + PgYears -> listOf(Subjects, PgYears) + PgMap -> listOf(Subjects, PgYears, PgMap) + is PgLesson -> listOf(Subjects, PgYears, PgMap, this) + is PgEx -> listOf(Subjects, PgYears, PgMap, PgLesson(id), this) + Pg2Map -> listOf(Subjects, PgYears, Pg2Map) + is Pg2Lesson -> listOf(Subjects, PgYears, Pg2Map, this) + is Pg2Ex -> listOf(Subjects, PgYears, Pg2Map, Pg2Lesson(id), this) + Pg3Map -> listOf(Subjects, PgYears, Pg3Map) + is Pg3Lesson -> listOf(Subjects, PgYears, Pg3Map, this) + is Pg3Ex -> listOf(Subjects, PgYears, Pg3Map, Pg3Lesson(id), this) + Pg4Map -> listOf(Subjects, PgYears, Pg4Map) + is Pg4Lesson -> listOf(Subjects, PgYears, Pg4Map, this) + is Pg4Ex -> listOf(Subjects, PgYears, Pg4Map, Pg4Lesson(id), this) + SwYears -> listOf(Subjects, SwYears) + SwMap -> listOf(Subjects, SwYears, SwMap) + is SwLesson -> listOf(Subjects, SwYears, SwMap, this) + is SwEx -> listOf(Subjects, SwYears, SwMap, SwLesson(id), this) + Sw2Map -> listOf(Subjects, SwYears, Sw2Map) + is Sw2Lesson -> listOf(Subjects, SwYears, Sw2Map, this) + is Sw2Ex -> listOf(Subjects, SwYears, Sw2Map, Sw2Lesson(id), this) + Sw3Map -> listOf(Subjects, SwYears, Sw3Map) + is Sw3Lesson -> listOf(Subjects, SwYears, Sw3Map, this) + is Sw3Ex -> listOf(Subjects, SwYears, Sw3Map, Sw3Lesson(id), this) + Sw4Map -> listOf(Subjects, SwYears, Sw4Map) + is Sw4Lesson -> listOf(Subjects, SwYears, Sw4Map, this) + is Sw4Ex -> listOf(Subjects, SwYears, Sw4Map, Sw4Lesson(id), this) + CsYears -> listOf(Subjects, CsYears) + CsMap -> listOf(Subjects, CsYears, CsMap) + is CsLesson -> listOf(Subjects, CsYears, CsMap, this) + Cs2Map -> listOf(Subjects, CsYears, Cs2Map) + is Cs2Lesson -> listOf(Subjects, CsYears, Cs2Map, this) + Cs3Map -> listOf(Subjects, CsYears, Cs3Map) + is Cs3Lesson -> listOf(Subjects, CsYears, Cs3Map, this) + Cs4Map -> listOf(Subjects, CsYears, Cs4Map) + is Cs4Lesson -> listOf(Subjects, CsYears, Cs4Map, this) CzechMap -> listOf(Subjects, CzechMap) Mluvnice -> listOf(Subjects, CzechMap, Mluvnice) is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this) @@ -289,6 +385,26 @@ fun routeFromPage(page: String): Route? = when (page) { "fyz2map" -> Route.Fyz2Map "fyz3map" -> Route.Fyz3Map "fyz4map" -> Route.Fyz4Map + "tgyears" -> Route.TgYears + "tgmap" -> Route.TgMap + "tg2map" -> Route.Tg2Map + "tg3map" -> Route.Tg3Map + "tg4map" -> Route.Tg4Map + "pgyears" -> Route.PgYears + "pgmap" -> Route.PgMap + "pg2map" -> Route.Pg2Map + "pg3map" -> Route.Pg3Map + "pg4map" -> Route.Pg4Map + "swyears" -> Route.SwYears + "swmap" -> Route.SwMap + "sw2map" -> Route.Sw2Map + "sw3map" -> Route.Sw3Map + "sw4map" -> Route.Sw4Map + "csyears" -> Route.CsYears + "csmap" -> Route.CsMap + "cs2map" -> Route.Cs2Map + "cs3map" -> Route.Cs3Map + "cs4map" -> Route.Cs4Map "czechmap" -> Route.CzechMap "mluvnice" -> Route.Mluvnice "readinglist" -> Route.ReadingList @@ -386,6 +502,90 @@ fun routeFromPage(page: String): Route? = when (page) { Regex("""fyzex(\d+)""").matchEntire(page)?.let { return Route.FyzEx(it.groupValues[1].toInt()) } + Regex("""tg4unit(\d+)""").matchEntire(page)?.let { + return Route.Tg4Lesson(it.groupValues[1].toInt()) + } + Regex("""tg4ex(\d+)""").matchEntire(page)?.let { + return Route.Tg4Ex(it.groupValues[1].toInt()) + } + Regex("""tg3unit(\d+)""").matchEntire(page)?.let { + return Route.Tg3Lesson(it.groupValues[1].toInt()) + } + Regex("""tg3ex(\d+)""").matchEntire(page)?.let { + return Route.Tg3Ex(it.groupValues[1].toInt()) + } + Regex("""tg2unit(\d+)""").matchEntire(page)?.let { + return Route.Tg2Lesson(it.groupValues[1].toInt()) + } + Regex("""tg2ex(\d+)""").matchEntire(page)?.let { + return Route.Tg2Ex(it.groupValues[1].toInt()) + } + Regex("""tgunit(\d+)""").matchEntire(page)?.let { + return Route.TgLesson(it.groupValues[1].toInt()) + } + Regex("""tgex(\d+)""").matchEntire(page)?.let { + return Route.TgEx(it.groupValues[1].toInt()) + } + Regex("""pg4unit(\d+)""").matchEntire(page)?.let { + return Route.Pg4Lesson(it.groupValues[1].toInt()) + } + Regex("""pg4ex(\d+)""").matchEntire(page)?.let { + return Route.Pg4Ex(it.groupValues[1].toInt()) + } + Regex("""pg3unit(\d+)""").matchEntire(page)?.let { + return Route.Pg3Lesson(it.groupValues[1].toInt()) + } + Regex("""pg3ex(\d+)""").matchEntire(page)?.let { + return Route.Pg3Ex(it.groupValues[1].toInt()) + } + Regex("""pg2unit(\d+)""").matchEntire(page)?.let { + return Route.Pg2Lesson(it.groupValues[1].toInt()) + } + Regex("""pg2ex(\d+)""").matchEntire(page)?.let { + return Route.Pg2Ex(it.groupValues[1].toInt()) + } + Regex("""pgunit(\d+)""").matchEntire(page)?.let { + return Route.PgLesson(it.groupValues[1].toInt()) + } + Regex("""pgex(\d+)""").matchEntire(page)?.let { + return Route.PgEx(it.groupValues[1].toInt()) + } + Regex("""sw4unit(\d+)""").matchEntire(page)?.let { + return Route.Sw4Lesson(it.groupValues[1].toInt()) + } + Regex("""sw4ex(\d+)""").matchEntire(page)?.let { + return Route.Sw4Ex(it.groupValues[1].toInt()) + } + Regex("""sw3unit(\d+)""").matchEntire(page)?.let { + return Route.Sw3Lesson(it.groupValues[1].toInt()) + } + Regex("""sw3ex(\d+)""").matchEntire(page)?.let { + return Route.Sw3Ex(it.groupValues[1].toInt()) + } + Regex("""sw2unit(\d+)""").matchEntire(page)?.let { + return Route.Sw2Lesson(it.groupValues[1].toInt()) + } + Regex("""sw2ex(\d+)""").matchEntire(page)?.let { + return Route.Sw2Ex(it.groupValues[1].toInt()) + } + Regex("""swunit(\d+)""").matchEntire(page)?.let { + return Route.SwLesson(it.groupValues[1].toInt()) + } + Regex("""swex(\d+)""").matchEntire(page)?.let { + return Route.SwEx(it.groupValues[1].toInt()) + } + Regex("""cs4unit(\d+)""").matchEntire(page)?.let { + return Route.Cs4Lesson(it.groupValues[1].toInt()) + } + Regex("""cs3unit(\d+)""").matchEntire(page)?.let { + return Route.Cs3Lesson(it.groupValues[1].toInt()) + } + Regex("""cs2unit(\d+)""").matchEntire(page)?.let { + return Route.Cs2Lesson(it.groupValues[1].toInt()) + } + Regex("""csunit(\d+)""").matchEntire(page)?.let { + return Route.CsLesson(it.groupValues[1].toInt()) + } Regex("""lit4unit(\d+)""").matchEntire(page)?.let { return Route.Lit4Lesson(it.groupValues[1].toInt()) } diff --git a/android/shared/src/org/maturita/maturita/data/Content.kt b/android/shared/src/org/maturita/maturita/data/Content.kt index 44176ee..ecb0c93 100644 --- a/android/shared/src/org/maturita/maturita/data/Content.kt +++ b/android/shared/src/org/maturita/maturita/data/Content.kt @@ -56,6 +56,22 @@ class Content(root: JSONObject) { val fyz2 = raw.arr("fyz2") val fyz3 = raw.arr("fyz3") val fyz4 = raw.arr("fyz4") + val tg = raw.arr("tg") + val tg2 = raw.arr("tg2") + val tg3 = raw.arr("tg3") + val tg4 = raw.arr("tg4") + val pg = raw.arr("pg") + val pg2 = raw.arr("pg2") + val pg3 = raw.arr("pg3") + val pg4 = raw.arr("pg4") + val sw = raw.arr("sw") + val sw2 = raw.arr("sw2") + val sw3 = raw.arr("sw3") + val sw4 = raw.arr("sw4") + val cs = raw.arr("cs") + val cs2 = raw.arr("cs2") + val cs3 = raw.arr("cs3") + val cs4 = raw.arr("cs4") val mat0 = raw.arr("mat0") val mat = raw.arr("mat") val mat2 = raw.arr("mat2") @@ -108,6 +124,22 @@ class Content(root: JSONObject) { fun fyz2Lesson(id: Int) = fyz2.firstOrNull { it.int("id") == id } fun fyz3Lesson(id: Int) = fyz3.firstOrNull { it.int("id") == id } fun fyz4Lesson(id: Int) = fyz4.firstOrNull { it.int("id") == id } + fun tgLesson(id: Int) = tg.firstOrNull { it.int("id") == id } + fun tg2Lesson(id: Int) = tg2.firstOrNull { it.int("id") == id } + fun tg3Lesson(id: Int) = tg3.firstOrNull { it.int("id") == id } + fun tg4Lesson(id: Int) = tg4.firstOrNull { it.int("id") == id } + fun pgLesson(id: Int) = pg.firstOrNull { it.int("id") == id } + fun pg2Lesson(id: Int) = pg2.firstOrNull { it.int("id") == id } + fun pg3Lesson(id: Int) = pg3.firstOrNull { it.int("id") == id } + fun pg4Lesson(id: Int) = pg4.firstOrNull { it.int("id") == id } + fun swLesson(id: Int) = sw.firstOrNull { it.int("id") == id } + fun sw2Lesson(id: Int) = sw2.firstOrNull { it.int("id") == id } + fun sw3Lesson(id: Int) = sw3.firstOrNull { it.int("id") == id } + fun sw4Lesson(id: Int) = sw4.firstOrNull { it.int("id") == id } + fun csLesson(id: Int) = cs.firstOrNull { it.int("id") == id } + fun cs2Lesson(id: Int) = cs2.firstOrNull { it.int("id") == id } + fun cs3Lesson(id: Int) = cs3.firstOrNull { it.int("id") == id } + fun cs4Lesson(id: Int) = cs4.firstOrNull { it.int("id") == id } fun mat0Lesson(id: Int) = mat0.firstOrNull { it.int("id") == id } fun matLesson(id: Int) = mat.firstOrNull { it.int("id") == id } fun mat2Lesson(id: Int) = mat2.firstOrNull { it.int("id") == id } diff --git a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt index 67ad66b..f4e5607 100644 --- a/android/shared/src/org/maturita/maturita/data/ProgressStore.kt +++ b/android/shared/src/org/maturita/maturita/data/ProgressStore.kt @@ -91,6 +91,38 @@ class ProgressStore(private val prefs: Prefs, content: Content) { fun markFyz3(id: Int) { mark("fyz3.$id") } fun fyz4Done(id: Int) = done("fyz4.$id") fun markFyz4(id: Int) { mark("fyz4.$id") } + fun tgDone(id: Int) = done("tg.$id") + fun markTg(id: Int) { mark("tg.$id") } + fun tg2Done(id: Int) = done("tg2.$id") + fun markTg2(id: Int) { mark("tg2.$id") } + fun tg3Done(id: Int) = done("tg3.$id") + fun markTg3(id: Int) { mark("tg3.$id") } + fun tg4Done(id: Int) = done("tg4.$id") + fun markTg4(id: Int) { mark("tg4.$id") } + fun pgDone(id: Int) = done("pg.$id") + fun markPg(id: Int) { mark("pg.$id") } + fun pg2Done(id: Int) = done("pg2.$id") + fun markPg2(id: Int) { mark("pg2.$id") } + fun pg3Done(id: Int) = done("pg3.$id") + fun markPg3(id: Int) { mark("pg3.$id") } + fun pg4Done(id: Int) = done("pg4.$id") + fun markPg4(id: Int) { mark("pg4.$id") } + fun swDone(id: Int) = done("sw.$id") + fun markSw(id: Int) { mark("sw.$id") } + fun sw2Done(id: Int) = done("sw2.$id") + fun markSw2(id: Int) { mark("sw2.$id") } + fun sw3Done(id: Int) = done("sw3.$id") + fun markSw3(id: Int) { mark("sw3.$id") } + fun sw4Done(id: Int) = done("sw4.$id") + fun markSw4(id: Int) { mark("sw4.$id") } + fun csDone(id: Int) = done("cs.$id") + fun markCs(id: Int) { mark("cs.$id") } + fun cs2Done(id: Int) = done("cs2.$id") + fun markCs2(id: Int) { mark("cs2.$id") } + fun cs3Done(id: Int) = done("cs3.$id") + fun markCs3(id: Int) { mark("cs3.$id") } + fun cs4Done(id: Int) = done("cs4.$id") + fun markCs4(id: Int) { mark("cs4.$id") } fun mat0Done(id: Int) = done("mat0.$id") fun markMat0(id: Int) { mark("mat0.$id") } fun matDone(id: Int) = done("mat.$id") @@ -280,6 +312,134 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum { sum.doneUnits += 1 } } + content.tg.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tgDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.tg2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tg2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.tg3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tg3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.tg4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.tg4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.pg.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pgDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.pg2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pg2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.pg3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pg3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.pg4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.pg4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.sw.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.swDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.sw2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.sw2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.sw3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.sw3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.sw4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.sw4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.cs.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.csDone(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.cs2.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.cs2Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.cs3.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.cs3Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + content.cs4.forEach { l -> + sum.totalEx += 1 + sum.openUnits += 1 + if (p.cs4Done(l.int("id"))) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } content.mat0.forEach { l -> sum.totalEx += 1 sum.openUnits += 1 diff --git a/android/shared/src/org/maturita/maturita/platform/Platform.kt b/android/shared/src/org/maturita/maturita/platform/Platform.kt index afd88fa..e275434 100644 --- a/android/shared/src/org/maturita/maturita/platform/Platform.kt +++ b/android/shared/src/org/maturita/maturita/platform/Platform.kt @@ -18,8 +18,18 @@ interface Platform { /** A speech engine for listening exercises, or null when there is none. */ fun speaker(): Speaker? + + /** + * Compiles and runs a C# program. [cb] runs on the UI thread. + * Status is ok, fail, timeout or unavailable. + */ + fun runCSharp(source: String, stdin: String, cb: (CSharpRun) -> Unit) { + cb(CSharpRun("unavailable", "")) + } } +data class CSharpRun(val status: String, val output: String) + /** The subset of Android's SharedPreferences the app uses. */ interface Prefs { fun getBoolean(key: String, fallback: Boolean): Boolean diff --git a/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt b/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt new file mode 100644 index 0000000..3e8640e --- /dev/null +++ b/android/shared/src/org/maturita/maturita/ui/CodeLesson.kt @@ -0,0 +1,303 @@ +package org.maturita.maturita.ui + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Cancel +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Code +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material.icons.filled.Terminal +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.input.OffsetMapping +import androidx.compose.ui.text.input.TransformedText +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.max +import androidx.compose.ui.unit.min +import androidx.compose.ui.unit.sp +import org.maturita.maturita.AppViewModel +import org.maturita.maturita.data.J + +/** Explanation on one side, the program and its output on the other. */ +@Composable +fun CodeLessonScreen(vm: AppViewModel, lesson: J, onSuccess: () -> Unit) { + val key = lesson.str("key") + val starter = lesson.str("code") + var code by remember(key) { mutableStateOf(starter) } + var stdin by remember(key) { mutableStateOf("") } + var output by remember(key) { mutableStateOf("") } + var status by remember(key) { mutableStateOf("") } + val running = status == "running" + val needsInput = "ReadLine" in code + + fun run() { + if (running) return + status = "running" + output = vm.tr("cs_running") + vm.platform.runCSharp(code, if (needsInput) stdin else "") { result -> + status = result.status + output = when (result.status) { + "unavailable" -> vm.tr("cs_unavailable") + "timeout" -> (result.output + "\n\n" + vm.tr("cs_timeout")).trim() + else -> result.output.ifEmpty { vm.tr("cs_empty") } + } + if (result.status == "ok") onSuccess() + } + } + + fun reset() { + code = starter + output = "" + status = "" + } + + DetailScaffold(vm, vm.tr(lesson.str("titleKey")), vm.tr(lesson.str("subKey")), maxContentWidth = 1640.dp) { inner -> + BoxWithConstraints(Modifier.fillMaxSize().padding(inner)) { + val width = maxWidth + val height = maxHeight + if (width >= 900.dp) { + Row( + Modifier.fillMaxSize().padding(horizontal = 24.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + Column(Modifier.width(min(560.dp, width * 0.4f)).fillMaxHeight().verticalScroll(rememberScrollState())) { + Explanation(lesson) + } + Column(Modifier.weight(1f).fillMaxHeight(), verticalArrangement = Arrangement.spacedBy(16.dp)) { + EditorCard(vm, code, { code = it }, running, ::run, ::reset, compact = false, Modifier.weight(1f)) + if (needsInput) InputField(vm, stdin) { stdin = it } + OutputCard(vm, output, status, Modifier.height(max(180.dp, height * 0.32f))) + } + } + } else { + Column( + Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Explanation(lesson) + EditorCard(vm, code, { code = it }, running, ::run, ::reset, compact = true, Modifier.height(320.dp)) + if (needsInput) InputField(vm, stdin) { stdin = it } + OutputCard(vm, output, status, Modifier.height(220.dp)) + } + } + } + } +} + +@Composable +private fun Explanation(lesson: J) { + Column(verticalArrangement = Arrangement.spacedBy(28.dp)) { + lesson.arr("slides").forEach { SlideContent(it) } + } +} + +@Composable +private fun EditorCard( + vm: AppViewModel, + code: String, + onCode: (String) -> Unit, + running: Boolean, + onRun: () -> Unit, + onReset: () -> Unit, + compact: Boolean, + modifier: Modifier, +) { + val dark = MaterialTheme.colorScheme.background.luminance() < 0.5f + val highlight = remember(dark) { CSharpHighlight(dark) } + Surface(modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surfaceContainerHigh) { + Column { + Row( + Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + Icon(Icons.Filled.Code, null, tint = MaterialTheme.colorScheme.primary) + Text("Program.cs", style = MaterialTheme.typography.titleSmall.copy(fontFamily = FontFamily.Monospace)) + Spacer(Modifier.weight(1f)) + if (compact) { + IconButton(onClick = onReset, enabled = !running) { + Icon(Icons.Filled.Refresh, contentDescription = vm.tr("cs_reset")) + } + } else { + OutlinedButton(onClick = onReset, enabled = !running) { + Icon(Icons.Filled.Refresh, null, Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text(vm.tr("cs_reset")) + } + } + Button(onClick = onRun, enabled = !running) { + Icon(Icons.Filled.PlayArrow, null, Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text(vm.tr("cs_run")) + } + } + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) + BasicTextField( + value = code, + onValueChange = onCode, + textStyle = codeStyle().copy(color = MaterialTheme.colorScheme.onSurface), + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + visualTransformation = highlight, + modifier = Modifier.fillMaxSize().padding(14.dp), + ) + } + } +} + +@Composable +private fun InputField(vm: AppViewModel, stdin: String, onStdin: (String) -> Unit) { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + Text(vm.tr("cs_stdin"), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onSurfaceVariant) + OutlinedTextField( + value = stdin, + onValueChange = onStdin, + textStyle = codeStyle(), + minLines = 1, + maxLines = 4, + modifier = Modifier.fillMaxWidth(), + ) + } +} + +@Composable +private fun OutputCard(vm: AppViewModel, output: String, status: String, modifier: Modifier) { + val muted = output.isEmpty() || status == "running" + Surface(modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surfaceContainerHigh) { + Column { + Row( + Modifier.fillMaxWidth().height(48.dp).padding(horizontal = 14.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(Icons.Filled.Terminal, null, tint = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(8.dp)) + Text(vm.tr("cs_output"), style = MaterialTheme.typography.titleSmall) + Spacer(Modifier.weight(1f)) + StatusBadge(vm, status) + } + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) + Text( + output.ifEmpty { vm.tr("cs_empty") }, + style = codeStyle(), + color = if (muted) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.onSurface, + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(14.dp), + ) + } + } +} + +@Composable +private fun StatusBadge(vm: AppViewModel, status: String) { + when (status) { + "running" -> CircularProgressIndicator(Modifier.size(18.dp), strokeWidth = 2.dp) + "ok" -> Badge(vm.tr("cs_done"), Icons.Filled.CheckCircle, MaterialTheme.colorScheme.primary) + "fail", "timeout" -> Badge(vm.tr("cs_failed"), Icons.Filled.Cancel, MaterialTheme.colorScheme.error) + } +} + +@Composable +private fun Badge(text: String, icon: androidx.compose.ui.graphics.vector.ImageVector, color: Color) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon(icon, null, tint = color, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text(text, style = MaterialTheme.typography.labelLarge, color = color) + } +} + +private fun codeStyle() = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 15.sp, lineHeight = 22.sp) + +/** Colours C# keywords, types, numbers, strings and comments without changing the text. */ +private class CSharpHighlight(dark: Boolean) : VisualTransformation { + private val palette = listOf( + Color(if (dark) 0xFFFF7AB2 else 0xFF9B2393), + Color(if (dark) 0xFF5DD8FF else 0xFF0B4F79), + Color(if (dark) 0xFFD9C97C else 0xFF1C00CF), + Color(if (dark) 0xFFFC6A5D else 0xFFC41A16), + Color(if (dark) 0xFF7F8C98 else 0xFF5D6C79), + ) + + override fun filter(text: AnnotatedString): TransformedText = + TransformedText(highlight(text.text), OffsetMapping.Identity) + + private fun highlight(code: String): AnnotatedString { + val kind = IntArray(code.length) { -1 } + RULES.forEachIndexed { index, rule -> + rule.findAll(code).forEach { m -> for (i in m.range) kind[i] = index } + } + return buildAnnotatedString { + var start = 0 + while (start < code.length) { + var end = start + while (end < code.length && kind[end] == kind[start]) end++ + val k = kind[start] + if (k >= 0) { + pushStyle(SpanStyle(color = palette[k])) + append(code, start, end) + pop() + } else { + append(code, start, end) + } + start = end + } + } + } + + companion object { + private val KEYWORDS = listOf( + "abstract", "as", "async", "await", "base", "bool", "break", "case", "catch", "char", "class", + "const", "continue", "decimal", "default", "do", "double", "else", "enum", "false", "finally", + "float", "for", "foreach", "get", "if", "in", "int", "interface", "internal", "is", "long", + "namespace", "new", "null", "object", "out", "override", "private", "protected", "public", + "readonly", "record", "ref", "return", "sealed", "set", "static", "string", "struct", "switch", + "this", "throw", "true", "try", "using", "var", "virtual", "void", "while", + ) + + /** Later rules win, so strings and comments override keywords inside them. */ + private val RULES = listOf( + Regex("\\b(?:" + KEYWORDS.joinToString("|") + ")\\b"), + Regex("\\b(?:Console|Math|List|Dictionary|File|Task|Exception|Func|Action|String|DateTime|Random)\\b"), + Regex("\\b\\d+(?:\\.\\d+)?\\b"), + Regex("\\$?@?\"(?:\\\\.|[^\"\\\\\\n])*\"|'(?:\\\\.|[^'\\\\\\n])'"), + Regex("//[^\\n]*|/\\*[\\s\\S]*?\\*/"), + ) + } +} diff --git a/android/shared/src/org/maturita/maturita/ui/Components.kt b/android/shared/src/org/maturita/maturita/ui/Components.kt index 6798891..6bdde68 100644 --- a/android/shared/src/org/maturita/maturita/ui/Components.kt +++ b/android/shared/src/org/maturita/maturita/ui/Components.kt @@ -8,15 +8,22 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn @@ -24,7 +31,17 @@ import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.unit.Dp import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.text.KeyboardOptions @@ -44,7 +61,6 @@ import androidx.compose.material3.FilterChip import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme @@ -150,7 +166,19 @@ fun AppNavBar(vm: AppViewModel) { private fun navIconColor(selected: Boolean): Color = if (selected) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onSurfaceVariant -/** Tab root: large collapsing title, search action, bottom navigation. */ +/** Lists and exercises stop growing here so they keep their shape in wide desktop windows. */ +val ContentMaxWidth = 880.dp +val ActionMaxWidth = 560.dp + +/** Caps the width of [content] and centres it; phones are narrower than the cap anyway. */ +@Composable +fun CenteredColumn(maxWidth: Dp = ContentMaxWidth, content: @Composable () -> Unit) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) { + Box(Modifier.widthIn(max = maxWidth).fillMaxSize()) { content() } + } +} + +/** Tab root: large collapsing title on phones, a compact one in wide windows. */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun RootScaffold( @@ -159,30 +187,48 @@ fun RootScaffold( showSearch: Boolean = true, content: LazyListScope.() -> Unit, ) { - val scroll = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) - Scaffold( - modifier = Modifier.nestedScroll(scroll.nestedScrollConnection), - topBar = { - LargeTopAppBar( - title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, - actions = { - if (showSearch) { - IconButton(onClick = { vm.searchOpen = true }) { - Icon(Icons.Filled.Search, contentDescription = vm.tr("search_placeholder")) - } - } - }, - scrollBehavior = scroll, - ) - }, - bottomBar = { AppNavBar(vm) }, - ) { inner -> - LazyColumn( - contentPadding = PaddingValues( - top = inner.calculateTopPadding(), - bottom = inner.calculateBottomPadding() + 16.dp, - ), - ) { content() } + BoxWithConstraints { + val wide = maxWidth >= 600.dp + val scroll = if (wide) { + TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + } else { + TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) + } + val actions: @Composable RowScope.() -> Unit = { + if (showSearch) { + IconButton(onClick = { vm.searchOpen = true }) { + Icon(Icons.Filled.Search, contentDescription = vm.tr("search_placeholder")) + } + } + } + Scaffold( + modifier = Modifier.nestedScroll(scroll.nestedScrollConnection), + topBar = { + if (wide) { + TopAppBar( + title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.headlineSmall) }, + actions = actions, + scrollBehavior = scroll, + ) + } else { + LargeTopAppBar( + title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, + actions = actions, + scrollBehavior = scroll, + ) + } + }, + bottomBar = { AppNavBar(vm) }, + ) { inner -> + CenteredColumn { + LazyColumn( + contentPadding = PaddingValues( + top = inner.calculateTopPadding(), + bottom = inner.calculateBottomPadding() + 16.dp, + ), + ) { content() } + } + } } } @@ -194,6 +240,7 @@ fun DetailScaffold( title: String, subtitle: String? = null, bottomBar: (@Composable () -> Unit)? = null, + maxContentWidth: Dp = ContentMaxWidth, content: @Composable (PaddingValues) -> Unit, ) { val scroll = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) @@ -224,8 +271,9 @@ fun DetailScaffold( ) }, bottomBar = { bottomBar?.invoke() }, - content = content, - ) + ) { inner -> + CenteredColumn(maxContentWidth) { content(inner) } + } } /** Feedback line plus the primary action, anchored at the bottom of an exercise. */ @@ -242,6 +290,8 @@ fun ActionBar( Column( Modifier .fillMaxWidth() + .wrapContentWidth(Alignment.CenterHorizontally) + .widthIn(max = ActionMaxWidth) .windowInsetsPadding(WindowInsets.navigationBars) .padding(horizontal = 16.dp, vertical = 12.dp), ) { @@ -276,6 +326,27 @@ fun SectionHeader(text: String) { ) } +/** Number or short label in a tinted square, so rows line up whatever the label's width. */ +@Composable +fun LeadingBadge(text: String) { + Box( + Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f)), + contentAlignment = Alignment.Center, + ) { + Text(text, style = MaterialTheme.typography.titleSmall, color = MaterialTheme.colorScheme.primary) + } +} + +@Composable +fun LeadingIcon(icon: ImageVector) { + Box( + Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f)), + contentAlignment = Alignment.Center, + ) { + Icon(icon, null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(20.dp)) + } +} + /** One navigable row: done/pending/locked state on the left, chevron on the right. */ @Composable fun NavRow( @@ -284,15 +355,33 @@ fun NavRow( done: Boolean = false, locked: Boolean = false, leading: (@Composable () -> Unit)? = null, + progress: Pair? = null, onClick: (() -> Unit)?, ) { val enabled = !locked && onClick != null val textColor = if (enabled) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant val rowModifier = if (enabled) Modifier.clickable(onClick = onClick!!) else Modifier + val shownSubtitle = subtitle?.takeIf { it.isNotBlank() } + val shownProgress = progress?.takeIf { it.second > 0 } ListItem( headlineContent = { Text(title, color = textColor) }, - supportingContent = subtitle?.takeIf { it.isNotBlank() }?.let { - { Text(it, color = MaterialTheme.colorScheme.onSurfaceVariant) } + supportingContent = if (shownSubtitle == null && shownProgress == null) null else { + { + Column { + shownSubtitle?.let { Text(it, color = MaterialTheme.colorScheme.onSurfaceVariant) } + shownProgress?.let { (doneCount, total) -> + Row(Modifier.padding(top = 8.dp), verticalAlignment = Alignment.CenterVertically) { + SmoothProgress(doneCount / total.toFloat(), Modifier.weight(1f).height(6.dp)) + Spacer(Modifier.width(10.dp)) + Text( + "$doneCount / $total", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } + } }, leadingContent = { when { @@ -328,19 +417,38 @@ fun SmoothProgress( progress: Float, modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.primary, - trackColor: Color = MaterialTheme.colorScheme.surfaceVariant, + trackColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f), ) { val animated by animateFloatAsState( targetValue = progress.coerceIn(0f, 1f), animationSpec = tween(durationMillis = 380), label = "progress", ) - LinearProgressIndicator( - progress = { animated }, - modifier = modifier, - color = color, - trackColor = trackColor, - ) + Box(modifier.heightIn(min = 4.dp).clip(CircleShape).background(trackColor)) { + if (animated > 0f) { + Box(Modifier.fillMaxHeight().fillMaxWidth(animated).clip(CircleShape).background(color)) + } + } +} + +/** Circular progress with a label in the middle. */ +@Composable +fun ProgressRing(progress: Float, modifier: Modifier = Modifier, stroke: Dp = 10.dp, label: @Composable () -> Unit) { + val animated by animateFloatAsState(progress.coerceIn(0f, 1f), tween(durationMillis = 380), label = "ring") + val track = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f) + val fill = MaterialTheme.colorScheme.primary + Box(modifier, contentAlignment = Alignment.Center) { + Canvas(Modifier.fillMaxSize()) { + val width = stroke.toPx() + val inset = width / 2 + val arcSize = Size(size.width - width, size.height - width) + drawArc(track, 0f, 360f, false, Offset(inset, inset), arcSize, style = Stroke(width)) + if (animated > 0f) { + drawArc(fill, -90f, 360f * animated, false, Offset(inset, inset), arcSize, style = Stroke(width, cap = StrokeCap.Round)) + } + } + label() + } } @Composable diff --git a/android/shared/src/org/maturita/maturita/ui/Exercises.kt b/android/shared/src/org/maturita/maturita/ui/Exercises.kt index fb9cabe..4bf3a47 100644 --- a/android/shared/src/org/maturita/maturita/ui/Exercises.kt +++ b/android/shared/src/org/maturita/maturita/ui/Exercises.kt @@ -1082,6 +1082,78 @@ fun ChoiceLessonScreen(vm: AppViewModel, lesson: J?, onDone: () -> Unit) { DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey")), onDone) } +@Composable +fun TgQuizScreen(vm: AppViewModel, year: Int, id: Int) { + val lesson = when (year) { + 2 -> vm.content.tg2Lesson(id) + 3 -> vm.content.tg3Lesson(id) + 4 -> vm.content.tg4Lesson(id) + else -> vm.content.tgLesson(id) + } ?: return + val spec = J(org.json.JSONObject().apply { + put("type", "choice") + put("title", vm.tr(lesson.str("exTitleKey"))) + put("sub", lesson.str("quizHeadKey")) + put("questions", lesson.o.optJSONArray("quiz")) + }) + DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { + when (year) { + 2 -> vm.markTg2(id) + 3 -> vm.markTg3(id) + 4 -> vm.markTg4(id) + else -> vm.markTg(id) + } + } +} + +@Composable +fun PgQuizScreen(vm: AppViewModel, year: Int, id: Int) { + val lesson = when (year) { + 2 -> vm.content.pg2Lesson(id) + 3 -> vm.content.pg3Lesson(id) + 4 -> vm.content.pg4Lesson(id) + else -> vm.content.pgLesson(id) + } ?: return + val spec = J(org.json.JSONObject().apply { + put("type", "choice") + put("title", vm.tr(lesson.str("exTitleKey"))) + put("sub", lesson.str("quizHeadKey")) + put("questions", lesson.o.optJSONArray("quiz")) + }) + DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { + when (year) { + 2 -> vm.markPg2(id) + 3 -> vm.markPg3(id) + 4 -> vm.markPg4(id) + else -> vm.markPg(id) + } + } +} + +@Composable +fun SwQuizScreen(vm: AppViewModel, year: Int, id: Int) { + val lesson = when (year) { + 2 -> vm.content.sw2Lesson(id) + 3 -> vm.content.sw3Lesson(id) + 4 -> vm.content.sw4Lesson(id) + else -> vm.content.swLesson(id) + } ?: return + val spec = J(org.json.JSONObject().apply { + put("type", "choice") + put("title", vm.tr(lesson.str("exTitleKey"))) + put("sub", lesson.str("quizHeadKey")) + put("questions", lesson.o.optJSONArray("quiz")) + }) + DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { + when (year) { + 2 -> vm.markSw2(id) + 3 -> vm.markSw3(id) + 4 -> vm.markSw4(id) + else -> vm.markSw(id) + } + } +} + @Composable fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) { val lesson = when (year) { diff --git a/android/shared/src/org/maturita/maturita/ui/Screens.kt b/android/shared/src/org/maturita/maturita/ui/Screens.kt index 1a4e87c..1f57231 100644 --- a/android/shared/src/org/maturita/maturita/ui/Screens.kt +++ b/android/shared/src/org/maturita/maturita/ui/Screens.kt @@ -25,7 +25,13 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.MenuBook +import androidx.compose.material.icons.filled.Apps +import androidx.compose.material.icons.filled.Architecture +import androidx.compose.material.icons.filled.AutoAwesome +import androidx.compose.material.icons.filled.AutoStories import androidx.compose.material.icons.filled.Bolt +import androidx.compose.material.icons.filled.Brush +import androidx.compose.material.icons.filled.Eco import androidx.compose.material.icons.filled.Functions import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close @@ -35,6 +41,7 @@ import androidx.compose.material.icons.filled.Groups import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Quiz import androidx.compose.material.icons.filled.Science +import androidx.compose.material.icons.filled.Terminal import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.outlined.Lightbulb import androidx.compose.material3.Card @@ -99,50 +106,61 @@ fun HomeScreen(vm: AppViewModel) { item { Card( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor = MaterialTheme.colorScheme.onSurface, + ), ) { - Column(Modifier.padding(20.dp)) { - Text( - vm.tr("welcome_title"), - style = MaterialTheme.typography.headlineMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer, - ) - Spacer(Modifier.height(8.dp)) - Text( - vm.tr(vm.platform.welcomeKey), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f), - ) - Spacer(Modifier.height(16.dp)) - SmoothProgress( - progress = pct, - modifier = Modifier.fillMaxWidth().height(6.dp).clip(CircleShape), - color = MaterialTheme.colorScheme.onPrimaryContainer, - trackColor = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.18f), - ) - Spacer(Modifier.height(6.dp)) - Text( - vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f), - ) + Row(Modifier.padding(20.dp), verticalAlignment = Alignment.CenterVertically) { + Column(Modifier.weight(1f)) { + Text(vm.tr("welcome_title"), style = MaterialTheme.typography.headlineMedium) + Spacer(Modifier.height(8.dp)) + Text( + vm.tr(vm.platform.welcomeKey), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + Spacer(Modifier.width(16.dp)) + ProgressRing(pct, Modifier.size(104.dp)) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text(vm.fmt("stats_pct_fmt", (pct * 100).toInt()), style = MaterialTheme.typography.titleLarge) + Text( + vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx), + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } } } } if (vm.update.canInstall || vm.update.status == "downloading" || vm.update.status == "staged") { item { UpdateBanner(vm) } } + val open = vm.content.subjects.filter { it.bool("open") } + val soon = vm.content.subjects.filter { !it.bool("open") } item { SectionHeader(vm.tr("subjects_title")) } - items(vm.content.subjects) { s -> - val open = s.bool("open") + items(open) { s -> + val part = subjectSum(vm, s) NavRow( title = vm.tr(s.str("key")), - subtitle = if (open) null else vm.tr("stats_locked"), - locked = !open, - leading = { SubjectIcon(s.str("icon"), open) }, - onClick = if (open) ({ vm.goPage(s.str("target")) }) else null, + leading = { SubjectIcon(s.str("icon"), true) }, + progress = part.doneEx to part.totalEx, + onClick = { vm.goPage(s.str("target")) }, ) } + if (soon.isNotEmpty()) { + item { SectionHeader(vm.tr("subjects_soon")) } + items(soon) { s -> + NavRow( + title = vm.tr(s.str("key")), + locked = true, + leading = { SubjectIcon(s.str("icon"), false) }, + onClick = null, + ) + } + } } } @@ -152,14 +170,18 @@ private fun ChangelogCard(vm: AppViewModel) { val blocks = vm.content.changelog.take(3) Card( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.tertiaryContainer), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor = MaterialTheme.colorScheme.onSurface, + ), ) { Column(Modifier.padding(20.dp)) { Row(verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Filled.AutoAwesome, null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(20.dp)) + Spacer(Modifier.width(8.dp)) Text( vm.tr("changelog_title"), style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer, modifier = Modifier.weight(1f), ) val date = blocks.firstOrNull()?.str("date").orEmpty() @@ -167,7 +189,7 @@ private fun ChangelogCard(vm: AppViewModel) { Text( date, style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer.copy(alpha = 0.7f), + color = MaterialTheme.colorScheme.onSurfaceVariant, ) } } @@ -178,7 +200,6 @@ private fun ChangelogCard(vm: AppViewModel) { Text( "• $line", style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer, modifier = Modifier.padding(bottom = 4.dp), ) } @@ -225,6 +246,10 @@ private fun SubjectIcon(icon: String, open: Boolean) { "flask" -> Icon(Icons.Filled.Science, null, tint = tint, modifier = Modifier.size(26.dp)) "bolt" -> Icon(Icons.Filled.Bolt, null, tint = tint, modifier = Modifier.size(26.dp)) "function" -> Icon(Icons.Filled.Functions, null, tint = tint, modifier = Modifier.size(26.dp)) + "ruler" -> Icon(Icons.Filled.Architecture, null, tint = tint, modifier = Modifier.size(26.dp)) + "code" -> Icon(Icons.Filled.Terminal, null, tint = tint, modifier = Modifier.size(26.dp)) + "brush" -> Icon(Icons.Filled.Brush, null, tint = tint, modifier = Modifier.size(26.dp)) + "apps" -> Icon(Icons.Filled.Apps, null, tint = tint, modifier = Modifier.size(26.dp)) else -> Icon(Icons.Filled.Lock, null, tint = tint) } } @@ -289,6 +314,75 @@ private fun Metric(label: String, value: String, modifier: Modifier) { } } +/** Done and total exercises behind one row of a years screen, or null when there is no count. */ +fun routeProgress(vm: AppViewModel, r: Route): Pair? { + val c = vm.content + val p = vm.progress + fun tally(lessons: List, done: (Int) -> Boolean) = lessons.count { done(it.int("id")) } to lessons.size + return when (r) { + Route.NetMap -> tally(c.netLessons, p::netDone) + Route.Net2Map -> tally(c.itLessons("net2")) { p.courseDone("net2", it) } + Route.Net3Map -> tally(c.itLessons("net3")) { p.courseDone("net3", it) } + Route.Net4Map -> tally(c.itLessons("net4")) { p.courseDone("net4", it) } + Route.HwMap -> tally(c.hw, p::hwDone) + Route.Hw2Map -> tally(c.itLessons("hw2")) { p.courseDone("hw2", it) } + Route.Hw3Map -> tally(c.itLessons("hw3")) { p.courseDone("hw3", it) } + Route.Hw4Map -> tally(c.itLessons("hw4")) { p.courseDone("hw4", it) } + Route.OnMap -> tally(c.on, p::onDone) + Route.On2Map -> tally(c.on2, p::on2Done) + Route.On3Map -> tally(c.on3, p::on3Done) + Route.On4Map -> tally(c.on4, p::on4Done) + is Route.EnMap -> tally(c.enYear(r.year)) { p.enDone(r.year, it) } + is Route.DeMap -> tally(c.deYear(r.year)) { p.deDone(r.year, it) } + Route.Roadmap -> { + var done = 0 + var total = 0 + c.german.filter { it.bool("unlocked") }.forEach { u -> + val n = u.strs("names").size + total += n + done += (1..n).count { p.germanDone(u.int("id"), it) } + } + done to total + } + Route.LitYears -> listOf( + tally(c.lit, p::litDone), tally(c.lit2, p::lit2Done), tally(c.lit3, p::lit3Done), tally(c.lit4, p::lit4Done), + ).let { parts -> parts.sumOf { it.first } to parts.sumOf { it.second } } + Route.LitMap -> tally(c.lit, p::litDone) + Route.Lit2Map -> tally(c.lit2, p::lit2Done) + Route.Lit3Map -> tally(c.lit3, p::lit3Done) + Route.Lit4Map -> tally(c.lit4, p::lit4Done) + Route.Mluvnice -> tally(c.mluvnice, p::mluvDone) + Route.ChemMap -> tally(c.chem, p::chemDone) + Route.BioMap -> tally(c.bio, p::bioDone) + Route.Mat0Map -> tally(c.mat0, p::mat0Done) + Route.MatMap -> tally(c.mat, p::matDone) + Route.Mat2Map -> tally(c.mat2, p::mat2Done) + Route.Mat3Map -> tally(c.mat3, p::mat3Done) + Route.Mat4Map -> tally(c.mat4, p::mat4Done) + Route.FyzMap -> tally(c.fyz, p::fyzDone) + Route.Fyz2Map -> tally(c.fyz2, p::fyz2Done) + Route.Fyz3Map -> tally(c.fyz3, p::fyz3Done) + Route.Fyz4Map -> tally(c.fyz4, p::fyz4Done) + Route.TgMap -> tally(c.tg, p::tgDone) + Route.Tg2Map -> tally(c.tg2, p::tg2Done) + Route.Tg3Map -> tally(c.tg3, p::tg3Done) + Route.Tg4Map -> tally(c.tg4, p::tg4Done) + Route.PgMap -> tally(c.pg, p::pgDone) + Route.Pg2Map -> tally(c.pg2, p::pg2Done) + Route.Pg3Map -> tally(c.pg3, p::pg3Done) + Route.Pg4Map -> tally(c.pg4, p::pg4Done) + Route.SwMap -> tally(c.sw, p::swDone) + Route.Sw2Map -> tally(c.sw2, p::sw2Done) + Route.Sw3Map -> tally(c.sw3, p::sw3Done) + Route.Sw4Map -> tally(c.sw4, p::sw4Done) + Route.CsMap -> tally(c.cs, p::csDone) + Route.Cs2Map -> tally(c.cs2, p::cs2Done) + Route.Cs3Map -> tally(c.cs3, p::cs3Done) + Route.Cs4Map -> tally(c.cs4, p::cs4Done) + else -> null + } +} + private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { val sum = ProgressSum() when (s.str("target")) { @@ -348,6 +442,34 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum { vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } + vm.content.fyz4.count { vm.progress.fyz4Done(it.int("id")) } } + "csyears", "csmap", "cs2map", "cs3map", "cs4map" -> { + sum.totalEx = vm.content.cs.size + vm.content.cs2.size + vm.content.cs3.size + vm.content.cs4.size + sum.doneEx = vm.content.cs.count { vm.progress.csDone(it.int("id")) } + + vm.content.cs2.count { vm.progress.cs2Done(it.int("id")) } + + vm.content.cs3.count { vm.progress.cs3Done(it.int("id")) } + + vm.content.cs4.count { vm.progress.cs4Done(it.int("id")) } + } + "tgyears", "tgmap", "tg2map", "tg3map", "tg4map" -> { + sum.totalEx = vm.content.tg.size + vm.content.tg2.size + vm.content.tg3.size + vm.content.tg4.size + sum.doneEx = vm.content.tg.count { vm.progress.tgDone(it.int("id")) } + + vm.content.tg2.count { vm.progress.tg2Done(it.int("id")) } + + vm.content.tg3.count { vm.progress.tg3Done(it.int("id")) } + + vm.content.tg4.count { vm.progress.tg4Done(it.int("id")) } + } + "pgyears", "pgmap", "pg2map", "pg3map", "pg4map" -> { + sum.totalEx = vm.content.pg.size + vm.content.pg2.size + vm.content.pg3.size + vm.content.pg4.size + sum.doneEx = vm.content.pg.count { vm.progress.pgDone(it.int("id")) } + + vm.content.pg2.count { vm.progress.pg2Done(it.int("id")) } + + vm.content.pg3.count { vm.progress.pg3Done(it.int("id")) } + + vm.content.pg4.count { vm.progress.pg4Done(it.int("id")) } + } + "swyears", "swmap", "sw2map", "sw3map", "sw4map" -> { + sum.totalEx = vm.content.sw.size + vm.content.sw2.size + vm.content.sw3.size + vm.content.sw4.size + sum.doneEx = vm.content.sw.count { vm.progress.swDone(it.int("id")) } + + vm.content.sw2.count { vm.progress.sw2Done(it.int("id")) } + + vm.content.sw3.count { vm.progress.sw3Done(it.int("id")) } + + vm.content.sw4.count { vm.progress.sw4Done(it.int("id")) } + } "czechmap" -> { val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } + vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } + @@ -649,6 +771,86 @@ private fun buildSearch(vm: AppViewModel): List { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit${l.int("id")}", extra = "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex${l.int("id")}") } + add(vm.tr("Programové vybavení"), vm.tr("search_lesson"), "swyears", extra = "office windows excel mysql") + add(vm.tr("sw_year1"), vm.tr("search_lesson"), "swmap", extra = "rocnik") + add(vm.tr("sw_year2"), vm.tr("search_lesson"), "sw2map", extra = "rocnik") + add(vm.tr("sw_year3"), vm.tr("search_lesson"), "sw3map", extra = "rocnik") + add(vm.tr("sw_year4"), vm.tr("search_lesson"), "sw4map", extra = "rocnik") + vm.content.sw.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "swunit${l.int("id")}", extra = "windows") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "swex${l.int("id")}") + } + vm.content.sw2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw2unit${l.int("id")}", extra = "word excel") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw2ex${l.int("id")}") + } + vm.content.sw3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw3unit${l.int("id")}", extra = "access mysql") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw3ex${l.int("id")}") + } + vm.content.sw4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw4unit${l.int("id")}", extra = "wordpress") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw4ex${l.int("id")}") + } + add(vm.tr("Prezentační grafika"), vm.tr("search_lesson"), "pgyears", extra = "photoshop premiere illustrator") + add(vm.tr("pg_year1"), vm.tr("search_lesson"), "pgmap", extra = "rocnik") + add(vm.tr("pg_year2"), vm.tr("search_lesson"), "pg2map", extra = "rocnik") + add(vm.tr("pg_year3"), vm.tr("search_lesson"), "pg3map", extra = "rocnik") + add(vm.tr("pg_year4"), vm.tr("search_lesson"), "pg4map", extra = "rocnik") + vm.content.pg.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pgunit${l.int("id")}", extra = "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pgex${l.int("id")}") + } + vm.content.pg2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg2unit${l.int("id")}", extra = "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg2ex${l.int("id")}") + } + vm.content.pg3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg3unit${l.int("id")}", extra = "premiere") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg3ex${l.int("id")}") + } + vm.content.pg4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg4unit${l.int("id")}", extra = "illustrator") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg4ex${l.int("id")}") + } + add(vm.tr("Technická grafika"), vm.tr("search_lesson"), "tgyears", extra = "autocad grafika") + add(vm.tr("tg_year1"), vm.tr("search_lesson"), "tgmap", extra = "rocnik") + add(vm.tr("tg_year2"), vm.tr("search_lesson"), "tg2map", extra = "rocnik") + add(vm.tr("tg_year3"), vm.tr("search_lesson"), "tg3map", extra = "rocnik") + add(vm.tr("tg_year4"), vm.tr("search_lesson"), "tg4map", extra = "rocnik") + vm.content.tg.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tgunit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tgex${l.int("id")}") + } + vm.content.tg2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg2unit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg2ex${l.int("id")}") + } + vm.content.tg3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg3unit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg3ex${l.int("id")}") + } + vm.content.tg4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg4unit${l.int("id")}", extra = "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex${l.int("id")}") + } + add(vm.tr("Programování"), vm.tr("search_lesson"), "csyears", extra = "csharp c#") + add(vm.tr("cs_year1"), vm.tr("search_lesson"), "csmap", extra = "rocnik") + add(vm.tr("cs_year2"), vm.tr("search_lesson"), "cs2map", extra = "rocnik") + add(vm.tr("cs_year3"), vm.tr("search_lesson"), "cs3map", extra = "rocnik") + add(vm.tr("cs_year4"), vm.tr("search_lesson"), "cs4map", extra = "rocnik") + vm.content.cs.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "csunit${l.int("id")}", extra = "csharp") + } + vm.content.cs2.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs2unit${l.int("id")}", extra = "csharp") + } + vm.content.cs3.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs3unit${l.int("id")}", extra = "csharp") + } + vm.content.cs4.forEach { l -> + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs4unit${l.int("id")}", extra = "csharp") + } add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie") add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie") vm.content.chem.forEach { l -> @@ -745,10 +947,10 @@ fun NetYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("net_years_title"), vm.tr("net_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("net_year1"), subtitle = vm.tr("net_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.NetMap) } - NavRow(title = vm.tr("net_year2"), subtitle = vm.tr("net2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net2Map) } - NavRow(title = vm.tr("net_year3"), subtitle = vm.tr("net3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net3Map) } - NavRow(title = vm.tr("net_year4"), subtitle = vm.tr("net4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net4Map) } + NavRow(title = vm.tr("net_year1"), subtitle = vm.tr("net_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.NetMap)) { vm.go(Route.NetMap) } + NavRow(title = vm.tr("net_year2"), subtitle = vm.tr("net2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Net2Map)) { vm.go(Route.Net2Map) } + NavRow(title = vm.tr("net_year3"), subtitle = vm.tr("net3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Net3Map)) { vm.go(Route.Net3Map) } + NavRow(title = vm.tr("net_year4"), subtitle = vm.tr("net4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Net4Map)) { vm.go(Route.Net4Map) } } } } @@ -759,10 +961,10 @@ fun HwYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Technické vybavení"), vm.tr("hw_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("hw_year1"), subtitle = vm.tr("hw_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.HwMap) } - NavRow(title = vm.tr("hw_year2"), subtitle = vm.tr("hw2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw2Map) } - NavRow(title = vm.tr("hw_year3"), subtitle = vm.tr("hw3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw3Map) } - NavRow(title = vm.tr("hw_year4"), subtitle = vm.tr("hw4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw4Map) } + NavRow(title = vm.tr("hw_year1"), subtitle = vm.tr("hw_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.HwMap)) { vm.go(Route.HwMap) } + NavRow(title = vm.tr("hw_year2"), subtitle = vm.tr("hw2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Hw2Map)) { vm.go(Route.Hw2Map) } + NavRow(title = vm.tr("hw_year3"), subtitle = vm.tr("hw3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Hw3Map)) { vm.go(Route.Hw3Map) } + NavRow(title = vm.tr("hw_year4"), subtitle = vm.tr("hw4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Hw4Map)) { vm.go(Route.Hw4Map) } } } } @@ -773,7 +975,7 @@ fun DeHomeScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Deutsch"), vm.tr("de_home_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("de_book_title"), subtitle = vm.tr("de_book_sub"), leading = { Text("U", style = MaterialTheme.typography.titleMedium) }) { + NavRow(title = vm.tr("de_book_title"), subtitle = vm.tr("de_book_sub"), leading = { LeadingBadge("U") }, progress = routeProgress(vm, Route.Roadmap)) { vm.go(Route.Roadmap) } } @@ -782,7 +984,8 @@ fun DeHomeScreen(vm: AppViewModel) { NavRow( title = vm.tr("de_year$year"), subtitle = vm.tr("de_y${year}_sub"), - leading = { Text("$year.", style = MaterialTheme.typography.titleMedium) }, + leading = { LeadingBadge("$year") }, + progress = routeProgress(vm, Route.DeMap(year)), ) { vm.go(Route.DeMap(year)) } } } @@ -798,7 +1001,8 @@ fun EnYearsScreen(vm: AppViewModel) { NavRow( title = vm.tr("en_year$year"), subtitle = vm.tr("en_y${year}_sub"), - leading = { Text("$year.", style = MaterialTheme.typography.titleMedium) }, + leading = { LeadingBadge("$year") }, + progress = routeProgress(vm, Route.EnMap(year)), ) { vm.go(Route.EnMap(year)) } } } @@ -810,10 +1014,10 @@ fun OnYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Občanská nauka"), vm.tr("on_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("on_year1"), subtitle = vm.tr("on_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.OnMap) } - NavRow(title = vm.tr("on_year2"), subtitle = vm.tr("on2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On2Map) } - NavRow(title = vm.tr("on_year3"), subtitle = vm.tr("on3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On3Map) } - NavRow(title = vm.tr("on_year4"), subtitle = vm.tr("on4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On4Map) } + NavRow(title = vm.tr("on_year1"), subtitle = vm.tr("on_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.OnMap)) { vm.go(Route.OnMap) } + NavRow(title = vm.tr("on_year2"), subtitle = vm.tr("on2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.On2Map)) { vm.go(Route.On2Map) } + NavRow(title = vm.tr("on_year3"), subtitle = vm.tr("on3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.On3Map)) { vm.go(Route.On3Map) } + NavRow(title = vm.tr("on_year4"), subtitle = vm.tr("on4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.On4Map)) { vm.go(Route.On4Map) } } } } @@ -824,10 +1028,10 @@ fun LitYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Literatura"), vm.tr("lit_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("lit_year1"), subtitle = vm.tr("lit_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.LitMap) } - NavRow(title = vm.tr("lit_year2"), subtitle = vm.tr("lit2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Lit2Map) } - NavRow(title = vm.tr("lit_year3"), subtitle = vm.tr("lit3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Lit3Map) } - NavRow(title = vm.tr("lit_year4"), subtitle = vm.tr("lit4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Lit4Map) } + NavRow(title = vm.tr("lit_year1"), subtitle = vm.tr("lit_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.LitMap)) { vm.go(Route.LitMap) } + NavRow(title = vm.tr("lit_year2"), subtitle = vm.tr("lit2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Lit2Map)) { vm.go(Route.Lit2Map) } + NavRow(title = vm.tr("lit_year3"), subtitle = vm.tr("lit3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Lit3Map)) { vm.go(Route.Lit3Map) } + NavRow(title = vm.tr("lit_year4"), subtitle = vm.tr("lit4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Lit4Map)) { vm.go(Route.Lit4Map) } } } } @@ -838,11 +1042,67 @@ fun MatYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Matematika"), vm.tr("mat_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("mat0_year"), subtitle = vm.tr("mat0_sub"), leading = { Text("ZŠ", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat0Map) } - NavRow(title = vm.tr("mat_year1"), subtitle = vm.tr("mat_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.MatMap) } - NavRow(title = vm.tr("mat_year2"), subtitle = vm.tr("mat2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat2Map) } - NavRow(title = vm.tr("mat_year3"), subtitle = vm.tr("mat3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat3Map) } - NavRow(title = vm.tr("mat_year4"), subtitle = vm.tr("mat4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Mat4Map) } + NavRow(title = vm.tr("mat0_year"), subtitle = vm.tr("mat0_sub"), leading = { LeadingBadge("ZŠ") }, progress = routeProgress(vm, Route.Mat0Map)) { vm.go(Route.Mat0Map) } + NavRow(title = vm.tr("mat_year1"), subtitle = vm.tr("mat_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.MatMap)) { vm.go(Route.MatMap) } + NavRow(title = vm.tr("mat_year2"), subtitle = vm.tr("mat2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Mat2Map)) { vm.go(Route.Mat2Map) } + NavRow(title = vm.tr("mat_year3"), subtitle = vm.tr("mat3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Mat3Map)) { vm.go(Route.Mat3Map) } + NavRow(title = vm.tr("mat_year4"), subtitle = vm.tr("mat4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Mat4Map)) { vm.go(Route.Mat4Map) } + } + } + } +} + +@Composable +fun CsYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Programování"), vm.tr("cs_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("cs_year1"), subtitle = vm.tr("cs_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.CsMap)) { vm.go(Route.CsMap) } + NavRow(title = vm.tr("cs_year2"), subtitle = vm.tr("cs2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Cs2Map)) { vm.go(Route.Cs2Map) } + NavRow(title = vm.tr("cs_year3"), subtitle = vm.tr("cs3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Cs3Map)) { vm.go(Route.Cs3Map) } + NavRow(title = vm.tr("cs_year4"), subtitle = vm.tr("cs4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Cs4Map)) { vm.go(Route.Cs4Map) } + } + } + } +} + +@Composable +fun SwYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Programové vybavení"), vm.tr("sw_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("sw_year1"), subtitle = vm.tr("sw_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.SwMap)) { vm.go(Route.SwMap) } + NavRow(title = vm.tr("sw_year2"), subtitle = vm.tr("sw2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Sw2Map)) { vm.go(Route.Sw2Map) } + NavRow(title = vm.tr("sw_year3"), subtitle = vm.tr("sw3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Sw3Map)) { vm.go(Route.Sw3Map) } + NavRow(title = vm.tr("sw_year4"), subtitle = vm.tr("sw4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Sw4Map)) { vm.go(Route.Sw4Map) } + } + } + } +} + +@Composable +fun PgYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Prezentační grafika"), vm.tr("pg_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("pg_year1"), subtitle = vm.tr("pg_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.PgMap)) { vm.go(Route.PgMap) } + NavRow(title = vm.tr("pg_year2"), subtitle = vm.tr("pg2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Pg2Map)) { vm.go(Route.Pg2Map) } + NavRow(title = vm.tr("pg_year3"), subtitle = vm.tr("pg3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Pg3Map)) { vm.go(Route.Pg3Map) } + NavRow(title = vm.tr("pg_year4"), subtitle = vm.tr("pg4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Pg4Map)) { vm.go(Route.Pg4Map) } + } + } + } +} + +@Composable +fun TgYearsScreen(vm: AppViewModel) { + DetailScaffold(vm, vm.tr("Technická grafika"), vm.tr("tg_years_sub")) { inner -> + LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { + item { + NavRow(title = vm.tr("tg_year1"), subtitle = vm.tr("tg_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.TgMap)) { vm.go(Route.TgMap) } + NavRow(title = vm.tr("tg_year2"), subtitle = vm.tr("tg2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Tg2Map)) { vm.go(Route.Tg2Map) } + NavRow(title = vm.tr("tg_year3"), subtitle = vm.tr("tg3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Tg3Map)) { vm.go(Route.Tg3Map) } + NavRow(title = vm.tr("tg_year4"), subtitle = vm.tr("tg4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Tg4Map)) { vm.go(Route.Tg4Map) } } } } @@ -853,10 +1113,10 @@ fun FyzYearsScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Fyzika"), vm.tr("fyz_years_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("fyz_year1"), subtitle = vm.tr("fyz_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.FyzMap) } - NavRow(title = vm.tr("fyz_year2"), subtitle = vm.tr("fyz2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz2Map) } - NavRow(title = vm.tr("fyz_year3"), subtitle = vm.tr("fyz3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz3Map) } - NavRow(title = vm.tr("fyz_year4"), subtitle = vm.tr("fyz4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz4Map) } + NavRow(title = vm.tr("fyz_year1"), subtitle = vm.tr("fyz_sub"), leading = { LeadingBadge("1") }, progress = routeProgress(vm, Route.FyzMap)) { vm.go(Route.FyzMap) } + NavRow(title = vm.tr("fyz_year2"), subtitle = vm.tr("fyz2_sub"), leading = { LeadingBadge("2") }, progress = routeProgress(vm, Route.Fyz2Map)) { vm.go(Route.Fyz2Map) } + NavRow(title = vm.tr("fyz_year3"), subtitle = vm.tr("fyz3_sub"), leading = { LeadingBadge("3") }, progress = routeProgress(vm, Route.Fyz3Map)) { vm.go(Route.Fyz3Map) } + NavRow(title = vm.tr("fyz_year4"), subtitle = vm.tr("fyz4_sub"), leading = { LeadingBadge("4") }, progress = routeProgress(vm, Route.Fyz4Map)) { vm.go(Route.Fyz4Map) } } } } @@ -870,11 +1130,17 @@ fun SciMapScreen(vm: AppViewModel) { NavRow( title = vm.tr("Chemie"), subtitle = vm.tr("chem_sub"), - leading = { Icon(Icons.Filled.Science, null, tint = MaterialTheme.colorScheme.primary) }, + leading = { LeadingIcon(Icons.Filled.Science) }, + progress = routeProgress(vm, Route.ChemMap), ) { vm.go(Route.ChemMap) } } item { - NavRow(title = vm.tr("Biologie"), subtitle = vm.tr("bio_sub")) { vm.go(Route.BioMap) } + NavRow( + title = vm.tr("Biologie"), + subtitle = vm.tr("bio_sub"), + leading = { LeadingIcon(Icons.Filled.Eco) }, + progress = routeProgress(vm, Route.BioMap), + ) { vm.go(Route.BioMap) } } } } @@ -885,20 +1151,26 @@ fun CzechMapScreen(vm: AppViewModel) { DetailScaffold(vm, vm.tr("Český jazyk a literatura"), vm.tr("czech_sub")) { inner -> LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) { item { - NavRow(title = vm.tr("Literatura"), subtitle = vm.tr("lit_years_sub")) { vm.go(Route.LitYears) } + NavRow( + title = vm.tr("Literatura"), + subtitle = vm.tr("lit_years_sub"), + leading = { LeadingIcon(Icons.Filled.AutoStories) }, + progress = routeProgress(vm, Route.LitYears), + ) { vm.go(Route.LitYears) } } item { NavRow( title = vm.tr("Mluvnice"), subtitle = vm.tr("mluv_sub"), - leading = { Icon(Icons.Filled.FormatListNumbered, null, tint = MaterialTheme.colorScheme.primary) }, + leading = { LeadingIcon(Icons.Filled.FormatListNumbered) }, + progress = routeProgress(vm, Route.Mluvnice), ) { vm.go(Route.Mluvnice) } } item { NavRow( title = vm.tr("Maturitní četba"), subtitle = vm.tr("reading_sub"), - leading = { Icon(Icons.AutoMirrored.Filled.MenuBook, null, tint = MaterialTheme.colorScheme.primary) }, + leading = { LeadingIcon(Icons.AutoMirrored.Filled.MenuBook) }, ) { vm.go(Route.ReadingList) } } } @@ -961,6 +1233,52 @@ fun BookScreen(vm: AppViewModel, id: String) { // --------------------------------------------------------------------------- // Lesson slides +/** One explanation slide: label, title, tip and numbered points. */ +@Composable +fun SlideContent(slide: J) { + Column { + Text(slide.str("kicker"), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) + Text(slide.str("title"), style = MaterialTheme.typography.headlineSmall, modifier = Modifier.padding(top = 4.dp, bottom = 12.dp)) + slide.strOrNull("tip")?.let { tip -> + Card( + modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer), + ) { + Row(Modifier.padding(14.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Outlined.Lightbulb, null, tint = MaterialTheme.colorScheme.onSecondaryContainer) + Spacer(Modifier.width(10.dp)) + Text(tip, style = MaterialTheme.typography.bodyMedium) + } + } + } + slide.strs("lines").forEachIndexed { index, line -> + val numbered = Regex("""^(\d+)\.\s+(.*)$""").find(line) + val badge = numbered?.groupValues?.get(1) ?: "${index + 1}" + val text = numbered?.groupValues?.get(2) ?: line + Row( + Modifier + .fillMaxWidth() + .padding(vertical = 5.dp) + .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f), androidx.compose.foundation.shape.RoundedCornerShape(16.dp)) + .padding(horizontal = 14.dp, vertical = 12.dp), + verticalAlignment = Alignment.Top, + ) { + Box( + Modifier + .padding(top = 2.dp) + .size(28.dp) + .background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), CircleShape), + contentAlignment = Alignment.Center, + ) { + Text(badge, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary) + } + Spacer(Modifier.width(12.dp)) + Text(text, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f)) + } + } + } +} + @OptIn(ExperimentalFoundationApi::class) @Composable fun SlidesScreen( @@ -1011,52 +1329,13 @@ fun SlidesScreen( }, ) { inner -> HorizontalPager(state = pager, modifier = Modifier.fillMaxSize().padding(inner)) { page -> - val slide = slides[page] Column( Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(horizontal = 16.dp, vertical = 12.dp), ) { - Text(slide.str("kicker"), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) - Text(slide.str("title"), style = MaterialTheme.typography.headlineSmall, modifier = Modifier.padding(top = 4.dp, bottom = 12.dp)) - slide.strOrNull("tip")?.let { tip -> - Card( - modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer), - ) { - Row(Modifier.padding(14.dp), verticalAlignment = Alignment.CenterVertically) { - Icon(Icons.Outlined.Lightbulb, null, tint = MaterialTheme.colorScheme.onSecondaryContainer) - Spacer(Modifier.width(10.dp)) - Text(tip, style = MaterialTheme.typography.bodyMedium) - } - } - } - slide.strs("lines").forEachIndexed { index, line -> - val numbered = Regex("""^(\d+)\.\s+(.*)$""").find(line) - val badge = numbered?.groupValues?.get(1) ?: "${index + 1}" - val text = numbered?.groupValues?.get(2) ?: line - Row( - Modifier - .fillMaxWidth() - .padding(vertical = 5.dp) - .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f), androidx.compose.foundation.shape.RoundedCornerShape(16.dp)) - .padding(horizontal = 14.dp, vertical = 12.dp), - verticalAlignment = Alignment.Top, - ) { - Box( - Modifier - .padding(top = 2.dp) - .size(28.dp) - .background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), CircleShape), - contentAlignment = Alignment.Center, - ) { - Text(badge, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary) - } - Spacer(Modifier.width(12.dp)) - Text(text, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f)) - } - } + SlideContent(slides[page]) Spacer(Modifier.height(24.dp)) } } diff --git a/assets/deutsch.png b/assets/deutsch.png new file mode 100644 index 0000000..91ed3e6 Binary files /dev/null and b/assets/deutsch.png differ diff --git a/assets/germanlection.png b/assets/germanlection.png deleted file mode 100644 index ab5fd6f..0000000 Binary files a/assets/germanlection.png and /dev/null differ diff --git a/assets/home.png b/assets/home.png new file mode 100644 index 0000000..4762690 Binary files /dev/null and b/assets/home.png differ diff --git a/assets/neue-freunde.png b/assets/neue-freunde.png new file mode 100644 index 0000000..3c10067 Binary files /dev/null and b/assets/neue-freunde.png differ diff --git a/assets/subjectsscreen.png b/assets/subjectsscreen.png deleted file mode 100644 index 6c09b75..0000000 Binary files a/assets/subjectsscreen.png and /dev/null differ diff --git a/assets/welcomescreen.png b/assets/welcomescreen.png deleted file mode 100644 index 9edb54e..0000000 Binary files a/assets/welcomescreen.png and /dev/null differ diff --git a/content/README.md b/content/README.md index ce160ef..ed6dca9 100644 --- a/content/README.md +++ b/content/README.md @@ -18,7 +18,7 @@ Skript vypíše počet lekcí, nebo chyby s cestou a číslem řádku | Složka | Obsah | |---|---| -| `pocitacove-site/`, `technicke-vybaveni/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | +| `pocitacove-site/`, `technicke-vybaveni/`, `technicka-grafika/`, `prezentacni-grafika/`, `programove-vybaveni/`, `programovani/`, `obcanska-nauka/`, `fyzika/` | `N-rocnik/`, v nich lekce | | `prirodni-vedy/chemie`, `prirodni-vedy/biologie` | lekce | | `cestina/literatura/N-rocnik` | lekce literatury | | `cestina/mluvnice` | cvičení z mluvnice | @@ -97,7 +97,7 @@ a smažte `puvodni`. ## Běžná lekce -Sítě, hardware, občanka, literatura, chemie, biologie, fyzika. +Sítě, hardware, technická grafika, prezentační grafika, programové vybavení, občanka, literatura, chemie, biologie, fyzika. ```md --- diff --git a/content/aplikace/novinky.txt b/content/aplikace/novinky.txt index 1f6aef9..e8365ba 100644 --- a/content/aplikace/novinky.txt +++ b/content/aplikace/novinky.txt @@ -5,6 +5,30 @@ # cs: bullet # en: bullet +--- 2026-10-11 +cs: Programové vybavení: Windows, Word, Excel, Access, MySQL a správa účtů +cs: Čtyři ročníky, od licence a složek po WordPress, Power BI a zásady +en: Software: Windows, Word, Excel, Access, MySQL and account administration +en: Four years, from licences and folders to WordPress, Power BI and policy + +--- 2026-10-11 +cs: Prezentační grafika: Photoshop, Premiere Pro a další programy Adobe +cs: Čtyři ročníky, v každé lekci postup, zadání a kvíz +en: Presentation graphics: Photoshop, Premiere Pro and the other Adobe apps +en: Four years, and every lesson has a method, an assignment and a quiz + +--- 2026-10-11 +cs: Programování: čtyři ročníky C#, od výpisu po LINQ a async +cs: Kód se píše v lekci a hned vedle se spustí +en: Programming: four years of C#, from output to LINQ and async +en: You write the code in the lesson and run it right beside the text + +--- 2026-10-11 +cs: Technická grafika: čtyři ročníky AutoCADu, od výkresu po 3D +cs: V každé lekci je postup, zadání s rozměry a kvíz +en: Technical graphics: four years of AutoCAD, from the drawing sheet to 3D +en: Every lesson has a method, a dimensioned assignment and a quiz + --- 2026-10-07 cs: Sítě a technické vybavení: doplněné 2. až 4. ročníky, v každém 8 lekcí cs: Výklad a kvíz od VLAN a sestavení počítače po maturitu diff --git a/content/aplikace/predmety.md b/content/aplikace/predmety.md index 39592ae..37363dd 100644 --- a/content/aplikace/predmety.md +++ b/content/aplikace/predmety.md @@ -11,7 +11,7 @@ | Matematika | Matematika | Mathematics | function | matyears | ano | | Fyzika | Fyzika | Physics | bolt | fyzyears | ano | | Základy přírodních věd | Základy přírodních věd | Fundamentals of Natural Sciences | flask | scimap | ano | -| Technická grafika | Technická grafika | Technical Drawing | lock | | ne | -| Prezentační grafika | Prezentační grafika | Presentation Graphics | lock | | ne | -| Programování | Programování | Programming | lock | | ne | -| Programové vybavení | Programové vybavení | Software | lock | | ne | +| Technická grafika | Technická grafika | Technical Graphics | ruler | tgyears | ano | +| Prezentační grafika | Prezentační grafika | Presentation Graphics | brush | pgyears | ano | +| Programování | Programování | Programming | code | csyears | ano | +| Programové vybavení | Programové vybavení | Software | apps | swyears | ano | diff --git a/content/aplikace/texty.md b/content/aplikace/texty.md index 089a5b0..fb62a94 100644 --- a/content/aplikace/texty.md +++ b/content/aplikace/texty.md @@ -40,7 +40,7 @@ |---|---|---| | welcome_kicker | Graduately | Graduately | | welcome_title | Maturita?\nTo dáme. | Maturita?\nWe've got this. | -| welcome_body | Procvičování k maturitě v jednom okně. Česky i anglicky, napsané v C studenty ze SSŠVT. | Exam practice in one window. Czech and English, written in C by students from SSŠVT. | +| welcome_body | Procvičování k maturitě v jednom okně. Česky i anglicky, napsané studenty ze SSŠVT. | Exam practice in one window. Czech and English, written by students from SSŠVT. | | welcome_body_android | Procvičování k maturitě v telefonu. Česky i anglicky, stejný obsah jako na počítači. | Exam practice on your phone. Czech and English, the same content as on the computer. | | welcome_body_desktop | Procvičování k maturitě na počítači. Česky i anglicky, stejný obsah jako v telefonu. | Exam practice on your computer. Czech and English, the same content as on your phone. | | welcome_body_ios | Nativní procvičování na iPhonu. Česky i anglicky, stejný obsah, rozhraní podle iOS. | Native exam practice on iPhone. Czech and English, the same content, adapted to iOS. | @@ -117,6 +117,7 @@ | search_page | Stránka | Page | | search_home | Úvod | Welcome | | subjects_title | Předměty | Subjects | +| subjects_soon | Připravujeme | Coming soon | | subjects_sub | Otevřený předmět spustí učební cestu. Zamčené zatím jen čekají. | An open subject opens its path. Locked ones are placeholders for now. | # Čeština @@ -229,6 +230,73 @@ | fyz3_sub | Elektřina a magnetismus. | Electricity and magnetism. | | fyz4_sub | Optika, relativita, mikročástice a vesmír. | Optics, relativity, the micro-world and the universe. | +# Technická grafika + +| Klíč | Česky | Anglicky | +|---|---|---| +| tg_years_sub | Čtyři ročníky AutoCADu, od výkresu po maturitní zadání. | Four years of AutoCAD, from the drawing sheet to the exam project. | +| tg_year1 | 1. ročník | Year 1 | +| tg_year2 | 2. ročník | Year 2 | +| tg_year3 | 3. ročník | Year 3 | +| tg_year4 | 4. ročník | Year 4 | +| tg_sub | Výkres, souřadnice a první příkazy. | The sheet, coordinates and the first commands. | +| tg2_sub | Úpravy, hladiny, šrafy a kóty. | Editing, layers, hatches and dimensions. | +| tg3_sub | Pohledy, řezy, bloky a tisk. | Views, sections, blocks and plotting. | +| tg4_sub | 3D model a výkres k maturitě. | A 3D model and the exam drawing. | + +# Prezentační grafika + +| Klíč | Česky | Anglicky | +|---|---|---| +| pg_years_sub | Od Photoshopu přes Premiere Pro k Illustratoru, After Effects a InDesignu. | From Photoshop through Premiere Pro to Illustrator, After Effects and InDesign. | +| pg_year1 | 1. ročník | Year 1 | +| pg_year2 | 2. ročník | Year 2 | +| pg_year3 | 3. ročník | Year 3 | +| pg_year4 | 4. ročník | Year 4 | +| pg_sub | Photoshop: pixely, vrstvy, výběr a retuš. | Photoshop: pixels, layers, selections and retouching. | +| pg2_sub | Masky, barvy, plakát a export. | Masks, colour, a poster and export. | +| pg3_sub | Premiere Pro: střih, zvuk, titulky a video. | Premiere Pro: the cut, sound, titles and the video. | +| pg4_sub | Illustrator, After Effects, InDesign a maturitní kampaň. | Illustrator, After Effects, InDesign and the exam campaign. | + +# Programové vybavení + +| Klíč | Česky | Anglicky | +|---|---|---| +| sw_years_sub | Operační systém, Office, databáze a správa Windows. | The operating system, Office, databases and Windows administration. | +| sw_year1 | 1. ročník | Year 1 | +| sw_year2 | 2. ročník | Year 2 | +| sw_year3 | 3. ročník | Year 3 | +| sw_year4 | 4. ročník | Year 4 | +| sw_sub | Software, licence a práce ve Windows. | Software, licences and working in Windows. | +| sw2_sub | Word a Excel: dokument, vzorce a graf. | Word and Excel: a document, formulas and a chart. | +| sw3_sub | Access a MySQL: tabulky, dotazy a vztahy. | Access and MySQL: tables, queries and relationships. | +| sw4_sub | WordPress, Power BI, dávkové soubory a zásady. | WordPress, Power BI, batch files and policy. | + +# Programování + +| Klíč | Česky | Anglicky | +|---|---|---| +| cs_years_sub | C# od prvního výpisu po pokročilejší nástroje. Kód se píše a spouští vedle výkladu. | C# from the first line of output to the later tools. You write the code and run it beside the lesson. | +| cs_year1 | 1. ročník | Year 1 | +| cs_year2 | 2. ročník | Year 2 | +| cs_year3 | 3. ročník | Year 3 | +| cs_year4 | 4. ročník | Year 4 | +| cs_sub | Výpis, proměnné, typy, operátory a řetězce. | Output, variables, types, operators and strings. | +| cs2_sub | Podmínky, cykly, pole a metody. | Conditions, loops, arrays and methods. | +| cs3_sub | Třídy, dědičnost, rozhraní a výčty. | Classes, inheritance, interfaces and enums. | +| cs4_sub | Výjimky, kolekce, LINQ, soubory a async. | Exceptions, collections, LINQ, files and async. | +| cs_code | Kód | Code | +| cs_output | Výstup | Output | +| cs_stdin | Vstup pro Console.ReadLine | Input for Console.ReadLine | +| cs_run | Spustit | Run | +| cs_reset | Obnovit ukázku | Reset example | +| cs_running | Překládám… | Compiling… | +| cs_done | Hotovo | Done | +| cs_failed | Chyba | Error | +| cs_empty | Zatím nic. Upravte kód a spusťte ho. | Nothing yet. Edit the code and run it. | +| cs_timeout | Program běžel příliš dlouho a byl zastaven. | The program ran too long and was stopped. | +| cs_unavailable | Tady C# nespustíte. Na počítači s nainstalovaným .NET se výstup objeví vedle kódu. | C# cannot run here. On a computer with .NET installed the output appears beside the code. | + # Angličtina a němčina | Klíč | Česky | Anglicky | diff --git a/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.en.md b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.en.md new file mode 100644 index 0000000..2e28f6f --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.en.md @@ -0,0 +1,6 @@ +--- +nazev: Bitmap and vector +popis: When a picture is pixels and when it is curves. +nazev_cviceni: Quiz: bitmap and vector +nadpis_kvizu: Tell what goes soft when you enlarge it +--- diff --git a/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.md b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.md new file mode 100644 index 0000000..167b175 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/01-bitmapa-a-vektor.md @@ -0,0 +1,79 @@ +--- +id: pg-1-bitmapa-a-vektor +nazev: Bitmapa a vektor +popis: Kdy je obrázek z pixelů a kdy z křivek. +nazev_cviceni: Kvíz: bitmapa a vektor +nadpis_kvizu: Poznejte, co se při zvětšení rozmaže +--- + +# Výklad + +## Dva druhy obrázku +*Pixel a křivka* + +- Bitmapa je mřížka pixelů. Fotka z telefonu je bitmapa a otevírá se ve Photoshopu. +- Vektor je popis křivek. Logo a ikona se kreslí v Illustratoru a při zvětšení zůstávají ostré. +- Photoshop umí obojí, ale fotku v něm nepřekreslíte na křivky. Naopak Illustrator není na retuš. +- Když nevíte, který soubor je který, přibližte ho na 400 %. Bitmapa ukáže čtverečky. + +> Tip: Přípona napoví: JPG a PNG jsou bitmapa, SVG a AI jsou vektor. PSD může obsahovat obojí. + +## Co se stane při zvětšení +*Kvalita* + +- Bitmapu 800 px širokou nelze vytisknout ostře na velký plakát. Pixely se jen roztáhnou. +- Vektor se zvětší přepočtem křivky. Hranu má pořád hladkou. +- Zmenšení bitmapy pixely zahodí. Zvětšení zpět je nevrátí. +- Proto se logo neukládá jako malé JPG a fotka se nefotí až ve chvíli, kdy už je pozdě. + +> Tip: Jednou zmenšenou fotku už nedostanete zpátky. Nechte si původní soubor. + +## Který program kdy +*Volba* + +- Fotku, retuš a plakát z fotografie berte do Photoshopu. +- Logo, piktogram a čisté tvary berte do Illustratoru. Ten přijde ve čtvrtém ročníku. +- Video nepatří ani do jednoho. Na střih je Premiere Pro, ve třeťáku. +- Hotové logo z Illustratoru se do Photoshopu vloží jako chytrý objekt, ne jako malý JPG. + +> Tip: Chytrý objekt drží původní kvalitu. ZJPG-ované logo už křivky nemá. + +## Zadání: porovnání +*Cvičení* + +- Otevřete ve Photoshopu fotku a přibližte ji na 400 %. Okraj předmětu se rozpadne na pixely. +- Stejný pohled udělejte u loga ve formátu SVG nebo AI. Hrana zůstane hladká. +- Do sešitu napište, který soubor je bitmapa a který vektor, a proč. +- Fotku uložte jako porovnani.psd. Logo nepřevádějte na JPG. + +> Tip: Když SVG ve Photoshopu rastruje, zaškrtněte velký rozměr. Malý náhled nic neukáže. + +# Kvíz + +## Co uvidíte na fotce při zvětšení na 400 %? +- Hladkou křivku bez pixelů +- [x] Čtverečky pixelů +- Jen ořezové značky +- Prázdné plátno +> Fotka je bitmapa. Zvětšení roztáhne pixely, křivku z ní neudělá. + +## Kam patří nové logo školy? +- Do Photoshopu jako malé JPG +- [x] Do Illustratoru jako křivky +- Do Premiere jako titulek +- Do JPG o šířce 80 px +> Logo se zvětšuje na plakát i na vizitku. Křivka to zvládne, malé JPG ne. + +## Co se stane, když bitmapu zmenšíte a pak znovu zvětšíte? +- Pixely se vrátí +- [x] Ostrost se ztratí +- Soubor se stane vektorem +- Přibude spadávka +> Zmenšení pixely zahodí. Zvětšení je neumí vymyslet zpět. + +## Jak se má logo vložit do plakátu ve Photoshopu? +- Jako JPG 80 px +- [x] Jako chytrý objekt +- Vložením snímku obrazovky +- Jako zvuková stopa +> Chytrý objekt podrží zdroj. JPG logo už nejde zvětšit bez rozmazání. diff --git a/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.en.md b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.en.md new file mode 100644 index 0000000..902f025 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.en.md @@ -0,0 +1,6 @@ +--- +nazev: Resolution and colour +popis: Pixels, 300 ppi for print, and RGB against CMYK. +nazev_cviceni: Quiz: resolution and colour +nadpis_kvizu: Pick the size and the colour mode +--- diff --git a/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.md b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.md new file mode 100644 index 0000000..86b6f23 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/02-rozliseni-a-barva.md @@ -0,0 +1,79 @@ +--- +id: pg-1-rozliseni-a-barva +nazev: Rozlišení a barva +popis: Pixely, tisk 300 ppi a rozdíl mezi RGB a CMYK. +nazev_cviceni: Kvíz: rozlišení a barva +nadpis_kvizu: Vyberte rozměr a barevný režim +--- + +# Výklad + +## Pixely a tisk +*Rozlišení* + +- Rozměr v pixelech říká, kolik bodů obrázek má. 1920 × 1080 je Full HD. +- Rozlišení v ppi říká, kolik pixelů připadne na palec při tisku. +- Na obrazovku stačí 72 ppi. Do tisku se připravuje 300 ppi. +- Formát A4 při 300 ppi má zhruba 2480 × 3508 px. To je 210 × 297 mm. + +> Tip: 72 ppi na plakát nestačí. 300 ppi na web je zbytečně velký soubor. + +## RGB a CMYK +*Režim* + +- RGB je světlo obrazovky: červená, zelená a modrá. Web i video jsou RGB. +- CMYK jsou tiskové barvy: azurová, purpurová, žlutá a černá. +- Barva, která svítí na monitoru, se v tisku často smrskne. Základní červená v CMYK zešedne. +- Fotku upravujte v RGB. Do CMYK ji převádějte až při exportu pro tiskárnu. + +> Tip: Když tiskárna chce PDF v CMYK, převod nechte na export. Pracovní soubor zůstane RGB. + +## Nový dokument +*Plátno* + +- Webový obrázek: 1920 × 1080 px, 72 ppi, RGB, 8 bitů na kanál. +- Tiskové A4: předvolba A4, 300 ppi, RGB. Ořez řešte až u plakátu ve druháku. +- Pozadí nové školní práce nechte průhledné, ať pod ním není bílá omylem. +- Pojmenujte dokument hned. nazev-skoly.psd je lepší než Bez názvu-1. + +> Tip: 8 bitů na kanál je běžná fotka. 16 bitů nechte na složitou retuš, soubor je větší. + +## Zadání: dva dokumenty +*Cvičení* + +- Založte web.psd: 1920 × 1080 px, 72 ppi, RGB, 8 bitů. +- Založte tisk-a4.psd: A4, 300 ppi, RGB, 8 bitů. +- U obou zapište do vrstvy poznámku s rozměrem v pixelech. U A4 to má být kolem 2480 × 3508. +- Oba soubory uložte jako PSD. Zatím je neexportujte do JPG. + +> Tip: Obraz > Velikost obrazu ukáže pixely i centimetry. Jednotky si přepněte, ať čtete obojí. + +# Kvíz + +## Jaké rozlišení se bere pro tisk? +- 72 ppi +- [x] 300 ppi +- 25 sn/s +- 8 bitů +> 72 ppi je obrazovka. Tisk chce 300 ppi, jinak je fotka na papíře měkká. + +## Který režim patří na web a do videa? +- CMYK +- [x] RGB +- Jen černá +- Spadávka +> Obrazovka skládá barvu ze světla RGB. CMYK je pro tiskárnu. + +## Kolik pixelů má zhruba A4 při 300 ppi? +- 1920 × 1080 +- [x] 2480 × 3508 +- 210 × 297 +- 800 × 600 +> 210 × 297 mm je rozměr papíru. V pixelech při 300 ppi je to asi 2480 × 3508. + +## Kdy se má fotka převést do CMYK? +- Hned po otevření +- [x] Až při exportu pro tisk +- Před každou retuší +- Nikdy, ani pro tisk +> Úpravy nechte v RGB. CMYK je výstup pro tiskárnu, ne pracovní soubor. diff --git a/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.en.md b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.en.md new file mode 100644 index 0000000..3f0a246 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.en.md @@ -0,0 +1,6 @@ +--- +nazev: The Photoshop workspace +popis: Panels, zoom and saving a PSD. +nazev_cviceni: Quiz: the workspace +nadpis_kvizu: Find the shortcut and the right file +--- diff --git a/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.md b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.md new file mode 100644 index 0000000..a2bcd79 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/03-prostredi-photoshopu.md @@ -0,0 +1,79 @@ +--- +id: pg-1-prostredi +nazev: Prostředí Photoshopu +popis: Panely, lupa a ukládání PSD. +nazev_cviceni: Kvíz: prostředí +nadpis_kvizu: Najděte zkratku a správný soubor +--- + +# Výklad + +## Okno +*Panely* + +- Vlevo je panel nástrojů. Nahoře jsou volby vybraného nástroje, mění se s ním. +- Vpravo jsou Vrstvy, Barvy a Vlastnosti. Když panel zmizí, vrátí ho Okno a název panelu. +- Uprostřed je plátno. Šedé okolí není součást obrázku, netiskne se. +- Pracovní plochu Fotografie nebo Základní nastavíte vpravo nahoře. Cizí rozložení si neukládejte přes cizí účet. + +> Tip: Na Macu je Ctrl většinou Cmd. Alt je Option. Zkratky v lekcích jsou windowsové. + +## Pohyb po obraze +*Lupa* + +- Mezerník dočasně vezme ručičku a obraz se posouvá. +- Ctrl+0 vměstná celé plátno do okna. Ctrl+1 ukáže 100 %, jeden pixel na pixel. +- Nástroj lupa je Z. Alt a klik zmenšuje. +- Při retuši kontrolujte 100 %. Na celém plátně drobná chyba zanikne. + +> Tip: Ctrl+Alt+Z krokne zpět o víc než jeden krok. Panel Historie ukáže, kam až se dá vrátit. + +## Uložení +*PSD a JPG* + +- Pracovní soubor je PSD. Drží vrstvy, masky a text. +- JPG je kopie na odevzdání nebo na web. Vrstvy v něm nejsou. +- Soubor > Uložit jako nechá PSD. Export > Exportovat jako udělá JPG nebo PNG. +- JPG kvalitu 10 (nebo 80 %) berte na fotku. Nižší kvalita dělá kostky. + +> Tip: Jednou uložené JPG znovu neukládejte jako jediný zdroj. Každý export ubere detail. + +## Zadání: první soubor +*Cvičení* + +- Otevřete web.psd z minulé lekce, nebo založte nový 1920 × 1080 px. +- Přejmenujte první vrstvu na pozadi a uložte soubor jako uvod.psd. +- Ctrl+0 a Ctrl+1 si vyzkoušejte. Na 100 % má být vidět pixel, ne celé plátno. +- Exportujte kopii uvod.jpg v kvalitě 10. PSD si nechte. + +> Tip: Když Exportovat jako nabídne PNG, přepněte formát na JPG. PNG tu ještě nepotřebujeme. + +# Kvíz + +## Co udělá Ctrl+1? +- Vloží novou vrstvu +- [x] Ukáže obraz v měřítku 100 % +- Smaže plátno +- Převede barvy do CMYK +> 100 % je jeden pixel souboru na jeden pixel obrazovky. Ctrl+0 naopak vměstná celé plátno. + +## Který soubor si necháváte jako pracovní? +- JPG +- [x] PSD +- MP4 +- Jen snímek obrazovky +> PSD drží vrstvy. JPG je plochá kopie. + +## Čím se při přiblížení posouvá obraz? +- Nástrojem text +- [x] Mezerníkem +- Klávesou D +- Exportem +> Mezerník dočasně přepne na ručičku, ať nemusíte měnit nástroj. + +## Jakou kvalitu JPG berete na obyčejnou fotku? +- 1 +- [x] 10, tedy kolem 80 % +- JPG se na fotku nehodí +- Jen PNG +> Kvalita 1 obraz rozbije na kostky. Kolem 10 je rozumný kompromis velikosti a vzhledu. diff --git a/content/prezentacni-grafika/1-rocnik/04-vrstvy.en.md b/content/prezentacni-grafika/1-rocnik/04-vrstvy.en.md new file mode 100644 index 0000000..468d0a9 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/04-vrstvy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Layers +popis: Order, opacity and groups. An edit does not sit on the photo pixels. +nazev_cviceni: Quiz: layers +nadpis_kvizu: Put the layers in the right order +--- diff --git a/content/prezentacni-grafika/1-rocnik/04-vrstvy.md b/content/prezentacni-grafika/1-rocnik/04-vrstvy.md new file mode 100644 index 0000000..25253f5 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/04-vrstvy.md @@ -0,0 +1,79 @@ +--- +id: pg-1-vrstvy +nazev: Vrstvy +popis: Pořadí, krytí a skupiny. Úprava nesedí na pixelech fotky. +nazev_cviceni: Kvíz: vrstvy +nadpis_kvizu: Dejte vrstvy do správného pořadí +--- + +# Výklad + +## Proč vrstva +*Pořádek* + +- Každá úprava má vlastní vrstvu. Fotka vespod zůstane nedotčená. +- Horní vrstva překrývá spodní. Text patří nahoru, fotka dolů. +- Oko u vrstvy ji skryje. Zámek zabrání posunu. +- Vrstvu pojmenujte: fotka, retus, nadpis. Název Vrstva 1 nic neřekne. + +> Tip: Když smažete vrstvu, fotka pod ní zůstane. Když malujete přímo do fotky, už ji neoddělíte. + +## Krytí a pořadí +*Vzhled* + +- Krytí 100 % je plná vrstva. Krytí 70 % nechá prosvítat to, co je pod ní. +- Pořadí změníte tažením v panelu Vrstvy. +- Režim prolnutí nechte na Normální, dokud ho lekce výslovně nezmění. +- Jedna věc na jedné vrstvě. Nadpis a stín textu k sobě patří, retuš ne. + +> Tip: Krytí je u horního okraje panelu Vrstvy. Není to totéž co Výplň, tu zatím nechte 100 %. + +## Skupina +*Složka* + +- Ctrl+G vloží vybrané vrstvy do skupiny. Skupina je složka v panelu. +- Skupina Popisek ať drží nadpis i podnadpis. Skupina Foto ať drží fotku a retuš. +- Skrytí skupiny skryje všechno v ní. Jednotlivé vrstvy tím nesmažete. +- Rozdělit skupinu jde Ctrl+Shift+G. + +> Tip: Nejdřív vyberte vrstvy se Shiftem, pak Ctrl+G. Prázdná skupina sama nic nesrovná. + +## Zadání: tři patra +*Cvičení* + +- V uvod.psd mějte dole vrstvu fotka. +- Nad ní vrstvu uprav, krytí 70 %. Zatím na ni stačí jednobarevný obdélník přes spodní třetinu. +- Úplně nahoru vrstvu nadpis s textem Školní den. +- Vrstvy nadpis a uprav dejte do skupiny popisek. Uložte PSD. + +> Tip: Když text nejde přečíst, není špatně písmo, ale pořadí. Nadpis nesmí skončit pod fotkou. + +# Kvíz + +## Kam patří vrstva s fotkou? +- Úplně nahoru +- [x] Dolu, pod úpravy a text +- Mimo panel Vrstvy +- Do JPG +> Fotka je podklad. Všechno, co se má dát vypnout, leží nad ní. + +## Co znamená krytí 70 %? +- Vrstva je o 70 px menší +- [x] Vrstva je částečně průhledná +- Text má 70 bodů +- Soubor má 70 ppi +> 100 % je plná vrstva. Nižší krytí nechá prosvítat vrstvy pod ní. + +## Čím se vybrané vrstvy seskupí? +- Ctrl+1 +- [x] Ctrl+G +- Klávesou D +- Exportem do JPG +> Ctrl+G udělá skupinu. Ctrl+Shift+G ji zase rozebere. + +## Proč se nemaluje přímo do vrstvy fotky? +- Photoshop to zakazuje +- [x] Úprava pak nejde oddělit ani vypnout +- Fotka by se stala vektorem +- Změní se to na CMYK +> Vlastní vrstvu smažete nebo skryjete. Pixely fotky už od malby neočistíte. diff --git a/content/prezentacni-grafika/1-rocnik/05-vybery.en.md b/content/prezentacni-grafika/1-rocnik/05-vybery.en.md new file mode 100644 index 0000000..fa401db --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/05-vybery.en.md @@ -0,0 +1,6 @@ +--- +nazev: Selections +popis: Mark part of the picture and copy it to a new layer. +nazev_cviceni: Quiz: selections +nadpis_kvizu: Pick the tool and the shortcut +--- diff --git a/content/prezentacni-grafika/1-rocnik/05-vybery.md b/content/prezentacni-grafika/1-rocnik/05-vybery.md new file mode 100644 index 0000000..7d42859 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/05-vybery.md @@ -0,0 +1,79 @@ +--- +id: pg-1-vybery +nazev: Výběry +popis: Označení části obrazu a kopie na novou vrstvu. +nazev_cviceni: Kvíz: výběry +nadpis_kvizu: Vyberte nástroj a zkratku +--- + +# Výklad + +## Nástroje výběru +*Tvar* + +- Obdélníkový a eliptický výběr řežou pravidelný tvar. Hodí se na ořez a banner. +- Laso obkreslí nepravidelný okraj rukou. Na vlasy je nepřesné. +- Rychlý výběr a předmět malují přes plochu, kterou chcete. Photoshop dopočítá hranu. +- Kouzelná hůlka bere souvislou podobnou barvu. Na oblohu ano, na tvář ne. + +> Tip: Shift k výběru přidává, Alt ubírá. Ve volbách nástroje je totéž jako ikony. + +## Hrana +*Prolnutí* + +- Tvrdý výběr dělá nalepený obrys. Prolnutí okraje (feather) hranu změkčí. +- U postavy stačí prolnutí 1 až 2 px. U oblohy může být větší. +- Prolnutí se nastavuje před potvrzením, nebo přes Výběr > Upravit > Prolnout okraj. +- Když je kolem vlasů barevný lem, výběr byl moc hrubý. Zpřesněte ho, neschovávejte ho rozmazáním. + +> Tip: Prolnutí 20 px z člověka udělá ducha. Na výřez postavy je to moc. + +## Nová vrstva z výběru +*Ctrl+J* + +- Ctrl+J zkopíruje výběr na novou vrstvu. Původní fotka zůstane vespod. +- Tak se postava oddělí od pozadí, aniž byste pozadí mazali. +- Výběr zrušíte Ctrl+D. Když ho nezrušíte, další malování se ořeže jen do něj. +- Vrstvu s postavou pojmenujte osoba. Vrstvu pod ní pozadi. + +> Tip: Ctrl+J bez výběru zkopíruje celou vrstvu. Nejdřív musí běžet mravenci kolem výběru. + +## Zadání: osoba bokem +*Cvičení* + +- Vyberte postavu rychlým výběrem. Okraj doplňte se Shiftem a Alt. +- Dejte prolnutí 2 px a Ctrl+J. Vznikne vrstva osoba. +- Vrstvu pozadi ztmavte tak, aby šla vypnout. Postava zůstane ve své vrstvě světlá. +- Uložte vyber.psd. Výběr před uložením zrušte Ctrl+D. + +> Tip: Když ztmavení sedí i na postavě, malovali jste do špatné vrstvy. Ztmavení patří jen na pozadi. + +# Kvíz + +## Který nástroj bere souvislou podobnou barvu, třeba oblohu? +- Text +- [x] Kouzelná hůlka +- Ořez +- Pero v Illustratoru +> Hůlka skočí po podobné barvě. Na tvář, která má víc odstínů, se nehodí. + +## Co udělá Ctrl+J, když je něco vybrané? +- Smaže výběr +- [x] Zkopíruje výběr na novou vrstvu +- Převede obraz do CMYK +- Založí video +> Původní vrstva zůstane. Nová obsahuje jen vybranou část. + +## Jaké prolnutí okraje dává smysl u postavy? +- [x] 2 px +- 80 px +- 300 ppi +- Žádné, ale 40 px stín na celé fotce +> 1 až 2 px hranu změkčí. Desítky pixelů postavu rozmažou. + +## Čím se výběr zruší? +- [x] Ctrl+D +- Ctrl+0 +- Exportem JPG +- Krytím 70 % +> Nezrušený výběr omezí další malování jen dovnitř mravenců. diff --git a/content/prezentacni-grafika/1-rocnik/06-retus.en.md b/content/prezentacni-grafika/1-rocnik/06-retus.en.md new file mode 100644 index 0000000..6ff7911 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/06-retus.en.md @@ -0,0 +1,6 @@ +--- +nazev: Retouching +popis: The clone stamp and the healing brush on their own layer. +nazev_cviceni: Quiz: retouching +nadpis_kvizu: Pick the tool and the layer +--- diff --git a/content/prezentacni-grafika/1-rocnik/06-retus.md b/content/prezentacni-grafika/1-rocnik/06-retus.md new file mode 100644 index 0000000..e7def42 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/06-retus.md @@ -0,0 +1,79 @@ +--- +id: pg-1-retus +nazev: Retuš +popis: Klonovací razítko a opravný štětec na vlastní vrstvě. +nazev_cviceni: Kvíz: retuš +nadpis_kvizu: Vyberte nástroj a vrstvu +--- + +# Výklad + +## Dva nástroje +*Razítko a štětec* + +- Klonovací razítko kopíruje konkrétní místo. Nejdřív Alt+klik do zdroje, pak malujete cíl. +- Opravný štětec bere texturu z okolí sám. Hodí se na pupínek, prach a malou skvrnu. +- Razítko berte tam, kde musí sedět vzor: plot, spára, textura zdi. +- Na velkou plochu oblohy je často rychlejší výběr a výplň než hodina razítka. + +> Tip: Alt+klik není obyčejný klik. Bez zdroje razítko neví, odkud brát. + +## Vrstva retus +*Nesahejte na fotku* + +- Nad fotku přidejte prázdnou vrstvu retus. +- Ve volbách razítka i štětce zapněte Vzorkovat všechny vrstvy, jinak prázdná vrstva nemá co kopírovat. +- Retuš pak jde ztlumit krytím nebo vypnout okem. Fotka vespod zůstane. +- Když se vzor opakuje jako razítko, vezměte zdroj z jiného místa. Jedna cihla pětkrát je vidět. + +> Tip: Tvrdost štětce kolem 50 % a krytí 100 %. Měkké razítko na hraně předmětu rozmaže obrys. + +## Co retušovat +*Míra* + +- Pryč patří prach, datum z fotoaparátu, koš v záběru a drát, který tam nemá být. +- Nepatří pryč pihy, jizva ani rys, který dělá člověka. To už není oprava, ale jiná tvář. +- Datum vypálené v rohu je pixel, ne vrstva. Musí se přemalovat z okolí oblohy nebo zdi. +- Po retuši se podívejte na 100 %. Opakovaný vzor a šmouha jsou vidět zblízka. + +> Tip: Jedna školní fotka, tři konkrétní chyby. Celou kůži nepřemalovávejte. + +## Zadání: tři chyby +*Cvičení* + +- Najděte na fotce tři rušivé věci: třeba koš, drát a datum, nebo tři skvrny. +- Odstraňte je na vrstvě retus. Razítko i štětec vzorkují všechny vrstvy. +- Vrstvu fotka nezamykejte malbou. Po vypnutí retus musí být chyby zpátky. +- Uložte retus.psd a do názvů si poznamenejte, který nástroj byl na které chybě. + +> Tip: Když po vypnutí vrstvy retus obraz vypadá stejně, malovali jste do fotky. Vezměte to z historie zpět. + +# Kvíz + +## Jak se u klonovacího razítka určí zdroj? +- Ctrl+J +- [x] Alt+klik do místa, které se má kopírovat +- Exportem +- Krytím 70 % +> Alt+klik vezme zdroj. Další tahy ho kopírují do cíle. + +## Kam se retuš maluje? +- Přímo do vrstvy fotky +- [x] Na prázdnou vrstvu nad fotkou +- Do textové vrstvy +- Do JPG +> Vlastní vrstva jde vypnout. Fotka zůstane původní. + +## Který nástroj si bere texturu z okolí sám? +- [x] Opravný štětec +- Text +- Ořez na 1920 px +- Pero +> Štětec dopočítá okolí. Razítko kopíruje přesně to místo, které jste označili. + +## Co musí být zapnuté, když razítko maluje do prázdné vrstvy? +- CMYK +- [x] Vzorkovat všechny vrstvy +- Spadávka 3 mm +- 25 sn/s +> Bez vzorkování všech vrstev je zdroj prázdný a razítko nemá co kopírovat. diff --git a/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.en.md b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.en.md new file mode 100644 index 0000000..eb99105 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Type and colour +popis: A headline, the eyedropper and the foreground colour. +nazev_cviceni: Quiz: type and colour +nadpis_kvizu: Name the colour shortcuts +--- diff --git a/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.md b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.md new file mode 100644 index 0000000..9abe13c --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/07-text-a-barvy.md @@ -0,0 +1,79 @@ +--- +id: pg-1-text-a-barvy +nazev: Text a barvy +popis: Nápis, kapátko a barva popředí. +nazev_cviceni: Kvíz: text a barvy +nadpis_kvizu: Poznejte zkratky barev +--- + +# Výklad + +## Barva popředí +*Kapátko* + +- Levý horní čtvereček je barva popředí, pravý dolní je pozadí. +- D vrátí černou a bílou. X je prohodí. +- Kapátko je I. Klik do fotky vezme barvu, která už v obraze je. +- Barvu nadpisu berte z fotky, ne náhodnou jedovatou zelenou, která se s ní pere. + +> Tip: Na světlé nebe patří tmavý text, na tmavý kabát světlý. Stejná šedá na obojí zanikne. + +## Nástroj text +*Písmo* + +- Jedno kliknutí založí bodový text. Tažení myší založí odstavcový rámeček. +- Nadpis školní práce je bodový text. Delší odstavec patří do rámečku. +- Velikost nadpisu na plátně 1920 px je kolem 72 pt. Podnadpis kolem 28 pt. +- Písmo nechte jedno, nejvýš dvě. Třetí řez plakát rozbije. + +> Tip: Textová vrstva se jmenuje podle nápisu. Nepřevádějte ji na pixely, dokud nemusíte. V PSD ať jde text přepsat. + +## Čitelnost +*Kontrast* + +- Když text sedí na rušné fotce, dejte pod něj poloprůhledný pruh, ne jen stín. +- Stín textu: vzdálenost malá, třeba 4 px, rozmazání 8 px, krytí kolem 40 %. +- Bílý text na světlé obloze nečetete ani vy. Ztmavte místo pod ním, nebo změňte barvu písma. +- Zarovnání na střed jen u krátkého nadpisu. Dlouhý text nechte vlevo. + +> Tip: Styl vrstvy je fx dole v panelu Vrstvy. Stín pak jde vypnout, písmo zůstane. + +## Zadání: nápis z fotky +*Cvičení* + +- Kapátkem I vezměte světlou barvu z fotky a nadpis Školní den napište barvou, která je k ní kontrastní. +- Podnadpis se jménem autora dejte na 28 pt. +- Pod text dejte pruh s krytím 70 %, ať nápis drží i na světlém místě. +- Uložte napis.psd. Text nechte jako textovou vrstvu, nerasterizujte ho. + +> Tip: X prohodí barvy, když kapátko vezme pravou. D vás vrátí na černou a bílou, když se ztratíte. + +# Kvíz + +## Co udělá klávesa D? +- Smaže vrstvu +- [x] Vrátí výchozí černou a bílou +- Spustí export +- Založí masku +> D je default. X potom černou a bílou prohodí. + +## Čím se nabere barva z fotky? +- Klonovacím razítkem +- [x] Kapátkem, klávesa I +- Ořezem +- Ctrl+0 +> Kapátko uloží barvu pixelu do popředí. Nadpis pak ladí s fotkou. + +## Jak velký má být podnadpis na plátně 1920 px? +- 4 pt +- [x] kolem 28 pt +- 300 pt +- 2480 pt +> Nadpis je velký, kolem 72 pt. Podnadpis je menší, kolem 28 pt, ať je vidět hierarchie. + +## Proč se text hned nerasterizuje? +- PSD by nešel uložit +- [x] V pracovním souboru má jít nápis přepsat +- Raster je vektor +- Premiere ho jinak nepřečte +> Z pixelů text už neopravíte. Na pixely ho převádějte až v kopii, když musíte. diff --git a/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.en.md b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.en.md new file mode 100644 index 0000000..f4da788 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: edit a photograph +popis: One photo, retouch, a selection, type and two exports. +nazev_cviceni: Quiz: the photograph +nadpis_kvizu: Check the finished piece +--- diff --git a/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.md b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.md new file mode 100644 index 0000000..f330581 --- /dev/null +++ b/content/prezentacni-grafika/1-rocnik/08-zadani-fotografie.md @@ -0,0 +1,79 @@ +--- +id: pg-1-zadani-fotografie +nazev: Zadání: úprava fotografie +popis: Jedna fotka, retuš, výběr, nápis a dva výstupy. +nazev_cviceni: Kvíz: úprava fotografie +nadpis_kvizu: Zkontrolujte hotovou práci +--- + +# Výklad + +## Co se odevzdává +*Rozsah* + +- Jedna fotka školní akce nebo místa, ořez na 1920 × 1080 px, RGB, 72 ppi. +- Pracovní soubor fotografie.psd a plochá kopie fotografie.jpg v kvalitě 10. +- Na plátně je nápis Školní den a pod ním vaše jméno. +- JPG nesmí být jediný soubor. Bez PSD nejde práce opravit. + +> Tip: Ořez udělá nástroj Ořez. Poměr stran zadejte 16 : 9, ať z fotky nezbude čtverec omylem. + +## Vrstvy, které musí být +*Skladba* + +- Dole fotka. Nad ní retus s aspoň jednou odstraněnou věcí, vzorkuje všechny vrstvy. +- Osoba nebo hlavní předmět je vybraný a leží na vlastní vrstvě, prolnutí okraje 2 px. +- Pozadí jde ztmavit samostatně. Po vypnutí této úpravy je pozadí zpět světlé. +- Skupina popisek drží pruh s krytím 70 % a oba texty. Texty nejsou rasterizované. + +> Tip: Pět vteřin kontroly: vypínejte skupiny okem. Každá musí něco změnit a po zapnutí to vrátit. + +## Nápis +*Čitelnost* + +- Barva nadpisu je z kapátka nebo k němu kontrastní. Na světlém místě nesmí zmizet. +- Podnadpis má asi 28 pt. Nadpis je výrazně větší. +- Pruh pod textem nelpí na horní hraně, sedí ve spodní třetině. +- Žádný text není oříznutý hranou plátna. Nechte od okraje aspoň 48 px. + +> Tip: 48 px je bezpečný okraj i na telefonu, když se JPG někde ořízne na krajích. + +## Kontrola před odevzdáním +*Seznam* + +- Obraz je 1920 × 1080 px. Větší export zmenšete, menší nezvětšujte rozmazáním. +- Retuš na 100 % nemá opakovaný vzor ani šmouhu přes obličej. +- V JPG nejsou vrstvy, v PSD jsou. Oba soubory se jmenují fotografie, liší se příponou. +- Původní fotku nemažte. Když ořez přetáhnete, musí být z čeho začít znovu. + +> Tip: Soubor > Exportovat jako ukáže rozměr. Když tam není 1920 × 1080, ořez ještě nesedí. + +# Kvíz + +## Jaký má být výsledný rozměr? +- 2480 × 3508 px +- [x] 1920 × 1080 px +- A4 při 300 ppi +- 80 × 80 px +> Zadání je obrazovka, Full HD, 16 : 9. A4 při 300 ppi by bylo do tisku. + +## Kde smí ležet retuš? +- Jen v JPG +- [x] Na vlastní vrstvě nad fotkou +- V rasterizovaném textu +- V dokumentu CMYK +> Vrstva retus jde vypnout. JPG je až plochá kopie na odevzdání. + +## Jaké krytí má pruh pod nápisem? +- 10 % +- [x] 70 % +- 300 % +- Pruh se nedělá +> 70 % podrží text a ještě nechá tušit fotku. Plný pruh ji zakryje celou. + +## Co se odevzdává? +- Jen JPG +- [x] PSD i JPG +- Jen SVG +- Projekt Premiere +> PSD je práce s vrstvami. JPG je náhled, který otevře i člověk bez Photoshopu. diff --git a/content/prezentacni-grafika/2-rocnik/01-masky.en.md b/content/prezentacni-grafika/2-rocnik/01-masky.en.md new file mode 100644 index 0000000..a1ed65d --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/01-masky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Masks +popis: A mask hides, it does not delete. Black conceals, white reveals. +nazev_cviceni: Quiz: masks +nadpis_kvizu: Read the mask +--- diff --git a/content/prezentacni-grafika/2-rocnik/01-masky.md b/content/prezentacni-grafika/2-rocnik/01-masky.md new file mode 100644 index 0000000..8d02198 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/01-masky.md @@ -0,0 +1,79 @@ +--- +id: pg-2-masky +nazev: Masky +popis: Maska skrývá, nemaže. Černá kryje, bílá ukazuje. +nazev_cviceni: Kvíz: masky +nadpis_kvizu: Přečtěte masku +--- + +# Výklad + +## K čemu maska je +*Nedestruktivně* + +- Smazaný pixel je pryč. Maska ho jen schová a jde přemalovat zpátky. +- Každá vrstva může mít jednu masku. Bílá plocha masky obraz ukazuje, černá ho kryje. +- Šedá je poloprůhledná. 50 % šedá nechá vrstvu napůl vidět. +- Masku přidáte ikonou obdélníku s kolečkem dole v panelu Vrstvy. + +> Tip: Když mažete gumou, nemáte cestu zpět. Když malujete do masky černou, stačí přetřít bílou. + +## Malování do masky +*Štětec* + +- Klik na miniaturu masky, ne na miniaturu obrazu. Jinak malujete do fotky. +- Alt+klik na masku ji ukáže přes celé plátno. Černobílý obraz je jen maska, ne výsledek. +- Měkký štětec, tvrdost 0 %, krytí 30 %, černá barva. Okraj předmětu pak není vystřižený nůžkami. +- X prohodí černou a bílou, takže stejný štětec umí skrýt i vrátit. + +> Tip: Kolem miniatury masky je rámeček, když je aktivní. Rámeček kolem fotky znamená, že malujete špatně. + +## Výběr a maska +*Spolu* + +- Výběr postavy a pak ikona masky schová všechno mimo výběr. +- Prolnutí 2 px udělejte ještě ve výběru, nebo okraj domalujte měkkým štětcem. +- Maska není nová vrstva. V panelu sedí vpravo vedle miniatury. +- Masku neslučujte s vrstvou, dokud učitel neřekne. Sloučení už nejde odmaskovat. + +> Tip: Ctrl+I na masce, když je vybraná, černou a bílou otočí. Hodí se, když jste skryli opak. + +## Zadání: výřez maskou +*Cvičení* + +- Postavu oddělte maskou, ne gumou. Původní fotka po Alt+klik a přemalování bílou musí být celá. +- Okraj dopracujte štětcem tvrdost 0 % a krytí 30 %. +- Pozadí nechte ve vrstvě pod ní, ať jde vyměnit. +- Uložte maska.psd. V souboru nesmí být jen jedna sloučená vrstva. + +> Tip: Vypněte a zapněte masku Shiftem a klikem na ni. Obraz se musí vrátit celý. + +# Kvíz + +## Co dělá černá barva v masce? +- Vrstvu ukáže +- [x] Vrstvu skryje +- Převede ji na text +- Zvýší rozlišení na 300 ppi +> Černá kryje, bílá ukazuje, šedá je mezi tím. + +## Čím se maska zobrazí přes celé plátno? +- Ctrl+1 +- [x] Alt+klik na miniaturu masky +- Exportem JPG +- Klávesou D na textu +> Alt+klik ukáže černobílou masku. Další Alt+klik vrátí normální obraz. + +## Proč se výřez nedělá gumou? +- Guma je v Illustratoru +- [x] Smazané pixely už nevrátíte, masku ano +- Guma umí jen CMYK +- Premiere gumu nepustí +> Masku přemalujete. Guma na obyčejné vrstvě pixely zahodí. + +## Jaké krytí štětce se hodí na jemný okraj masky? +- 100 % tvrdý štětec +- [x] 30 % a tvrdost 0 % +- Krytí textu 28 pt +- 10 Mb/s +> Nízké krytí a měkký štětec skládají hranu postupně. Tvrdý tah je vidět jako střih. diff --git a/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.en.md b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.en.md new file mode 100644 index 0000000..0ae3082 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Levels and curves +popis: Brightness and contrast as an adjustment layer, not a rewrite of the pixels. +nazev_cviceni: Quiz: curves +nadpis_kvizu: Recognise a non-destructive edit +--- diff --git a/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.md b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.md new file mode 100644 index 0000000..cc53a24 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/02-urovne-a-krivky.md @@ -0,0 +1,79 @@ +--- +id: pg-2-krivky +nazev: Úrovně a křivky +popis: Světlost a kontrast jako vrstva úprav, ne jako přepis pixelů. +nazev_cviceni: Kvíz: křivky +nadpis_kvizu: Poznejte nedestruktivní úpravu +--- + +# Výklad + +## Vrstva úprav +*Nad pixely* + +- Vrstva úprav mění vzhled všeho pod sebou a pixely fotky nepřepisuje. +- Přidá se kolečkem dole v panelu Vrstvy. Položka Křivky nebo Úrovně. +- Když úpravu vypnete okem, fotka je jako na začátku. +- Úprava přímo přes Obraz > Přizpůsobení fotku přepíše. Ve školní práci ji neberte. + +> Tip: Vrstva úprav má vlastní masku. Černou v masce úpravu na části obrazu vypnete. + +## Úrovně +*Tři body* + +- Černý bod říká, co má být nejtmavší. Bílý bod, co má být nejsvětlejší. +- Šedý bod srovná nádech. Klik do místa, které má být neutrální šedé. +- Bod světel nechte kolem 245, ne na 255, ať v obloze nezmizí kresba. +- Bod stínů kolem 15, ne na 0, ať černá není slitá. + +> Tip: Když histogram na obou koncích nic nemá, fotka je mdlá. Body přitáhněte k místu, kde data začínají. + +## Křivka S +*Kontrast* + +- Křivka zdola zleva nahoru doprava je světlost od stínů ke světlům. +- Jemná křivka do S zvedne kontrast: stíny o trochu níž, světla o trochu výš. +- Ostré S přepálí tváře a zabije stíny. Stačí malý oblouk. +- Jedna vrstva Křivky na celou fotku. Druhou přidejte jen na oblohu, a maskou ji tam omezte. + +> Tip: Na křivce táhněte málo. Oko si na přepálený náhled rychle zvykne a původní fotka pak vypadá špatně, i když byla lepší. + +## Zadání: dvě úpravy +*Cvičení* + +- Nad fotku dejte vrstvu úprav Křivky. Bod stínů nechte u 15, bod světel u 245, mezi tím jemné S. +- Druhou křivku omezte maskou jen na pozadí, ať tvář nejde do přepalu. +- Obě úpravy pojmenujte. Fotka vespod ať nemá žádné Přizpůsobení zapékané v pixelech. +- Uložte krivky.psd. Vypnutí obou úprav vrátí původní světlost. + +> Tip: Alt při tahu černého a bílého bodu v Úrovních ukáže, které místo už je slité. U křivek stačí oko a 100 %. + +# Kvíz + +## Kam patří školní zesvětlení fotky? +- Obraz > Přizpůsobení, přímo do pixelů +- [x] Vrstva úprav nad fotkou +- Do textové vrstvy +- Do exportu JPG jako jediná úprava +> Vrstva úprav jde vypnout. Přizpůsobení přes nabídku Obraz pixely přepíše. + +## Kam dát bod světel, aby obloha neztratila kresbu? +- 0 +- [x] kolem 245 +- 255 a přes +- 28 pt +> 255 je čistá bílá bez kresby. 245 nechá ve světlech detail. + +## Co dělá jemná křivka ve tvaru S? +- Sníží rozlišení +- [x] Zvedne kontrast +- Převede RGB na vektor +- Přidá spadávku +> Stíny jdou trochu níž a světla trochu výš. Kontrast stoupne, barva se nemění v jiný režim. + +## Čím se křivka omezí jen na pozadí? +- Gumou na fotce +- [x] Maskou vrstvy úprav +- Exportem +- Krytím textu +> Černá v masce úpravy řekne: tady křivku nepoužívej. Tvář může zůstat bílá, tedy upravená jinou vrstvou, nebo naopak. diff --git a/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.en.md b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.en.md new file mode 100644 index 0000000..5811b67 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.en.md @@ -0,0 +1,6 @@ +--- +nazev: Smart objects and filters +popis: Blur and sharpening you can still change. +nazev_cviceni: Quiz: filters +nadpis_kvizu: Fill in the filter values +--- diff --git a/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.md b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.md new file mode 100644 index 0000000..78c29d2 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/03-chytre-objekty-a-filtry.md @@ -0,0 +1,79 @@ +--- +id: pg-2-chytre-objekty +nazev: Chytré objekty a filtry +popis: Rozostření a doostření, které jde změnit. +nazev_cviceni: Kvíz: filtry +nadpis_kvizu: Doplňte hodnoty filtru +--- + +# Výklad + +## Chytrý objekt +*Obal* + +- Vrstva > Chytré objekty > Převést na chytrý objekt zabalí vrstvu do obalu. +- Filtr na chytrém objektu zůstane jako položka pod vrstvou. Dvojklik ho otevře znovu. +- Filtr na obyčejné vrstvě pixely přepíše. Ve školním plakátu ho neberte. +- Logo z Illustratoru vkládejte rovnou jako chytrý objekt, ať jde zvětšit. + +> Tip: V rohu miniatury chytrého objektu je malá ikona. Když tam není, filtr se zapíše natvrdo. + +## Rozostření pozadí +*Hloubka* + +- Gaussovské rozostření oddělí pozadí od předmětu. Na pozadí stačí 8 px. +- Předmět rozostřením nechte být. Buď je na vrstvě nad ním, nebo má masku. +- Silnější rozostření, třeba 30 px, už pozadí smaže. Berte ho jen jako záměr. +- Rozostření celého obrazu včetně tváře je chyba, ne hloubka ostrosti. + +> Tip: Nejdřív výřez maskou z minulé lekce, potom rozostření jen vrstvy pozadí. + +## Doostření +*Unsharp* + +- Filtr Doostření (Unsharp Mask) zvedne kontrast na hraně. Není to zvětšení pixelů. +- Školní hodnoty na fotku: množství 80 %, poloměr 1 px, práh 2. +- Vyšší množství dělá světlý a tmavý obrys kolem větví a vlasů. +- Doostřujte až nakonec a jen jednou. Doostřené doostření je vidět. + +> Tip: Náhled filtru přepínejte, ať vidíte rozdíl. Na 100 %, ne na zmenšeném plátně. + +## Zadání: hloubka a hrana +*Cvičení* + +- Pozadí převeďte na chytrý objekt a dejte mu Gaussovské rozostření 8 px. +- Předmět nechte ostrý. Maska nebo samostatná vrstva ho chrání. +- Na předmět dejte Doostření 80 %, poloměr 1 px, práh 2, taky jako filtr chytrého objektu. +- Uložte filtry.psd. Oba filtry musí jít otevřít dvojklikem a hodnoty změnit. + +> Tip: Když dvojklik filtr neotevře, vrstva nebyla chytrý objekt. Vezměte krok z historie a převeďte ji znovu. + +# Kvíz + +## Jaké rozostření má školní pozadí? +- [x] 8 px Gaussovské +- 80 px na celou fotku včetně tváře +- 0 px +- Rozostření se dělá v Premiere +> 8 px pozadí oddělí a ještě ho nechá poznat. Celý obraz včetně předmětu se nerozostřuje. + +## Jaké hodnoty má školní Doostření? +- [x] množství 80 %, poloměr 1 px, práh 2 +- množství 300 %, poloměr 20 px +- jen krytí 70 % +- 300 ppi +> Malý poloměr drží hranu. Velký poloměr kreslí kolem předmětů svatozář. + +## Proč filtr patří na chytrý objekt? +- Jinak nejde uložit PSD +- [x] Hodnoty jdou potom změnit a pixely se nepřepíšou +- Chytrý objekt je vždy CMYK +- Kvůli 25 sn/s +> Pod vrstvou zůstane řádek filtru. Dvojklik ho otevře se stejnými čísly. + +## Co Doostření ve skutečnosti dělá? +- Dopočítá nové pixely jako při zvětšení +- [x] Zvedne kontrast na hraně +- Převede fotku na křivky +- Přidá masku +> Unsharp Mask není zvětšení. Měkkou fotku z malého souboru nezachrání, jen zdůrazní hranu. diff --git a/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.en.md b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.en.md new file mode 100644 index 0000000..82e7d6e --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.en.md @@ -0,0 +1,6 @@ +--- +nazev: Shapes and layer styles +popis: A vector bar, a shadow and a stroke inside Photoshop. +nazev_cviceni: Quiz: shapes +nadpis_kvizu: Fill in the bar and the shadow +--- diff --git a/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.md b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.md new file mode 100644 index 0000000..fba437e --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/04-tvary-a-styly.md @@ -0,0 +1,79 @@ +--- +id: pg-2-tvary +nazev: Tvary a styly vrstvy +popis: Vektorový pruh, stín a tah přímo ve Photoshopu. +nazev_cviceni: Kvíz: tvary +nadpis_kvizu: Doplňte rozměr pruhu a stínu +--- + +# Výklad + +## Tvar +*Vektor ve Photoshopu* + +- Nástroj obdélník a elipsa kreslí tvarovou vrstvu. Není to namalovaný pixel. +- Tvar jde zvětšit a hrana zůstane hladká, podobně jako v Illustratoru. +- Barvu změníte dvojklikem na miniaturu. Nemalujte přes tvar štětcem. +- Na fotku 1920 × 1080 patří spodní pruh 1920 × 280 px, od kraje ke kraji. + +> Tip: Když podržíte Shift, elipsa je kružnice a obdélník čtverec. + +## Styl vrstvy +*fx* + +- fx dole v panelu přidá stín, tah nebo přechod. Písmo ani tvar se nerasterizuje. +- Stín školního pruhu: vzdálenost 8 px, velikost 16 px, krytí 40 %. +- Tah 2 px berte jen na tlačítko nebo rámeček. Na velký pruh je zbytečný. +- Styl zkopírujete Alt+tažením řádku fx na jinou vrstvu. + +> Tip: Krytí ve stínu není krytí celé vrstvy. Pruh může být plný a stín lehký. + +## Pruh a text +*Spolu* + +- Pruh je jedna vrstva, text druhá. Neslučujte je. +- Pruh má krytí 75 %, ať fotka pod ním nezmizí celá. +- Text je bílý, když je pruh tmavý. Sedí aspoň 48 px od levého okraje pruhu. +- Skupina spodni drží pruh i text. Vypnutí skupiny je sundá najednou. + +> Tip: Zarovnání textu k pruhu: vyberte obě vrstvy a v nástroji přesun použijte ikony zarovnání. + +## Zadání: spodní pruh +*Cvičení* + +- Na plátně 1920 × 1080 nakreslete tvar 1920 × 280 px, krytí vrstvy 75 %. +- Stín: vzdálenost 8, velikost 16, krytí stínu 40 %. +- Bílý nadpis a podnadpis 28 pt jsou ve skupině s pruhem, 48 px od levého kraje. +- Uložte pruh.psd. Tvar nesmí být rasterizovaný. + +> Tip: V panelu Vrstvy je u tvaru ikona, ne jen malba. Když chybí, kreslili jste štětcem. + +# Kvíz + +## Jak velký je spodní pruh na Full HD? +- 280 × 280 px +- [x] 1920 × 280 px +- 2480 × 3508 px +- A4 +> Šířka plátna je 1920. Výška pruhu 280 nechá fotce horní část. + +## Jaké krytí má vrstva pruhu? +- [x] 75 % +- 10 % +- 100 % a navíc CMYK +- Krytí se nenastavuje +> 75 % pruh podrží text a fotku pod ním ještě tušíte. + +## Jaký stín má školní pruh? +- [x] vzdálenost 8, velikost 16, krytí 40 % +- vzdálenost 80, velikost 80, krytí 100 % +- žádný, stín nejde u tvaru +- jen filtr 8 px na fotku +> Malý stín pruh odlepí od fotky. Velký stín přes celou fotku je špína, ne styl. + +## Proč se tvar nespojuje s textem do jedné vrstvy? +- Photoshop to neumí oddělit +- [x] Text by nešel přepsat a pruh přebarvit zvlášť +- Kvůli 25 sn/s +- JPG umí vrstvy líp +> Každá věc vlastní vrstvu. Sloučení nechte až na plochý export. diff --git a/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.en.md b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.en.md new file mode 100644 index 0000000..41ced0b --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.en.md @@ -0,0 +1,6 @@ +--- +nazev: Poster composition +popis: A3, a 3 mm bleed and one main headline. +nazev_cviceni: Quiz: the poster +nadpis_kvizu: Pick the size and the bleed +--- diff --git a/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.md b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.md new file mode 100644 index 0000000..a52cbb3 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/05-kompozice-plakatu.md @@ -0,0 +1,79 @@ +--- +id: pg-2-kompozice +nazev: Kompozice plakátu +popis: A3, spadávka 3 mm a jeden hlavní nápis. +nazev_cviceni: Kvíz: plakát +nadpis_kvizu: Vyberte formát a spadávku +--- + +# Výklad + +## Formát a spadávka +*A3* + +- Školní plakát je A3 na výšku, 297 × 420 mm, 300 ppi, RGB. +- Tiskárna ořízne papír na čistý A3. Barva, která má jít do kraje, musí přesahovat. +- Spadávka je 3 mm na každé straně. Plátno tedy má 303 × 426 mm. +- Vodítka dejte 3 mm od okraje. Dovnitř vodítek patří text, ven až k okraji plátna smí jen pozadí. + +> Tip: 303 mm je 297 plus 3 a 3. Když je plátno jen 297, spadávka tam není. + +## Jeden plakát, jedna věc +*Hierarchie* + +- Největší je název akce. Pod ním datum a místo, menším písmem. +- Datum školního zadání je 12. 6. Místo je tělocvična. +- Hlavní prvek, třeba postava, sedí na třetině, ne uprostřed jako pasové foto. +- Dva stejně velké nadpisy se perou. Jeden musí vyhrát. + +> Tip: Třetiny: dvě svislé a dvě vodorovné čáry dělí plochu na devět polí. Průsečík je silné místo. + +## Okraj textu +*Klid* + +- Text je aspoň 10 mm od čistého ořezu, tedy dál než vodítko spadávky. +- Řádek delší než dvě třetiny šířky se čte špatně. Nadpis zalomte. +- Písmo nejvýš dvě. Jedno tučné na název, jedno obyčejné na údaj. +- Logo školy je malé a v rohu. Není to druhý plakát. + +> Tip: Když logo nemáte ve vektoru, berte největší PNG, které škola má, a zmenšete ho. Ne naopak. + +## Zadání: kostra plakátu +*Cvičení* + +- Nový dokument 303 × 426 mm, 300 ppi, RGB. Vodítka 3 mm od každého kraje. +- Fotku dejte přes celou spadávku. Pruh a text až za vodítka. +- Název akce, pod ním 12. 6. a tělocvična. Název je výrazně větší. +- Uložte plakat-kostra.psd. Čistý formát A3 ať jde poznat podle vodítek. + +> Tip: Zobrazení > Pravítka a táhněte vodítko z pravítka. Přichytávání k němu zapněte v menu Zobrazení. + +# Kvíz + +## Jak velké je plátno plakátu se spadávkou 3 mm? +- 297 × 420 mm +- [x] 303 × 426 mm +- 1920 × 1080 px +- 210 × 297 mm +> A3 je 297 × 420. Z každé strany 3 mm navíc dělá 303 × 426. + +## Kam nesmí text? +- [x] Do spadávky a blízko ořezu +- Za vodítko, dovnitř čistého formátu +- Pod název +- Na vlastní vrstvu +> Ořez spadávku uřízne. Text 10 mm od čistého kraje přežije i křivý řez. + +## Kam se má strefit hlavní prvek? +- Přesně na střed jako na občanku +- [x] Na třetinu plochy +- Do razítka +- Mimo plátno +> Střed je klidný a často nudný. Průsečík třetin drží oko. + +## Jaké rozlišení má tiskový plakát? +- 72 ppi +- [x] 300 ppi +- 25 sn/s +- 8 px +> Plakát je tisk. 300 ppi. 72 ppi by na A3 bylo vidět jako mřížka. diff --git a/content/prezentacni-grafika/2-rocnik/06-mockup.en.md b/content/prezentacni-grafika/2-rocnik/06-mockup.en.md new file mode 100644 index 0000000..538410f --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/06-mockup.en.md @@ -0,0 +1,6 @@ +--- +nazev: A mockup +popis: The poster design placed on a photograph of a wall. +nazev_cviceni: Quiz: the mockup +nadpis_kvizu: Pick the blend mode and the opacity +--- diff --git a/content/prezentacni-grafika/2-rocnik/06-mockup.md b/content/prezentacni-grafika/2-rocnik/06-mockup.md new file mode 100644 index 0000000..bc41379 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/06-mockup.md @@ -0,0 +1,79 @@ +--- +id: pg-2-mockup +nazev: Mockup +popis: Návrh plakátu usazený na fotografii zdi. +nazev_cviceni: Kvíz: mockup +nadpis_kvizu: Vyberte režim a krytí +--- + +# Výklad + +## K čemu mockup je +*Náhled* + +- Mockup ukáže návrh v situaci: plakát na zdi, vizitka na stole, tričko na postavě. +- Není to náhrada tiskového PDF. Je to obrázek pro prezentaci práce. +- Fotka zdi má být velká aspoň jako výstup, ideálně 1920 px na delší straně. +- Návrh plakátu vložte jako chytrý objekt, ať ho jde ještě opravit. + +> Tip: Obraz > Umístit vložené, nebo přetáhněte PSD a potvrďte. Vznikne chytrý objekt. + +## Perspektiva +*Deformace* + +- Úpravy > Transformovat > Pokřivit, nebo Deformace, srovná rohy plakátu s rohy na zdi. +- Rohy táhněte na fotografii, ne od oka do prázdna. +- Shift při tažení rohu není vždy správně. Zeď má úběžník, plakát na ní není obdélník v pohledu. +- Po potvrzení transformace chytrý objekt deformaci podrží. + +> Tip: Když je zeď focená z boku, horní hrana plakátu je kratší než dolní, nebo naopak. To má mockup ukázat. + +## Potisk +*Násobit* + +- Režim prolnutí Násobit (Multiply) nechá prosvítit strukturu zdi. +- Krytí návrhu dejte 90 %. 100 % vypadá jako nalepená samolepka bez papíru. +- Bílá v režimu Násobit zmizí. Na bílý plakát se Násobit nehodí, nechte Normální a lehce snižte krytí. +- Stín pod plakátem: měkký, malý, na vlastní vrstvě, ať odlepí papír od zdi. + +> Tip: Násobit násobí barvy. Tmavé místa zůstanou tmavá, bílá se ztratí. + +## Zadání: plakát na zdi +*Cvičení* + +- Vezměte kostru plakátu a usaďte ji na fotku zdi jako chytrý objekt. +- Deformací srovnejte čtyři rohy. Krytí 90 %. +- Když plakát není bílý, zkuste Násobit. Když zbělá, vraťte Normální. +- Uložte mockup.psd na 1920 px delší strany, RGB. Tiskové PDF z toho nedělejte. + +> Tip: Mockup je obrazovka, 72 ppi stačí. Tiskové rozměry zůstávají v plakat-kostra.psd. + +# Kvíz + +## K čemu je mockup? +- Je to tiskové PDF pro tiskárnu +- [x] Ukáže návrh na fotografii místa +- Nahradí vrstvy v PSD +- Je to střih videa +> Tiskárna dostane PDF v ořezu. Mockup je na obhajobu a na web. + +## Jak se návrh srovná se zdí, která je focená z boku? +- Ořezem na čtverec +- [x] Deformací rohů +- Filtrem Doostření +- Převodem do CMYK +> Pokřivení nebo deformace vezme každý roh zvlášť. Obyčejné zmenšení úhel zdi neopraví. + +## Kdy dává smysl režim Násobit? +- [x] Když má prosvítit struktura zdi a plakát není bílý +- Vždy, i na bílý plakát +- Jen u textu 28 pt +- Jen v Premiere +> Násobit nechá tmavou strukturu a bílou zruší. Bílý plakát by zmizel. + +## Jaké krytí má návrh na zdi? +- [x] 90 % +- 10 % +- 100 % bez výjimky +- 300 % +> 90 % lehce spojí papír se zdí. 100 % často vypadá jako nalepený obdélník. diff --git a/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.en.md b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.en.md new file mode 100644 index 0000000..1619e68 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.en.md @@ -0,0 +1,6 @@ +--- +nazev: Export for screen and print +popis: JPG and PNG for the screen, a PDF with crop marks for the printer. +nazev_cviceni: Quiz: export +nadpis_kvizu: Match the format to the job +--- diff --git a/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.md b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.md new file mode 100644 index 0000000..593d41c --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/07-export-web-a-tisk.md @@ -0,0 +1,79 @@ +--- +id: pg-2-export +nazev: Export pro web a tisk +popis: JPG a PNG na obrazovku, PDF s ořezem do tiskárny. +nazev_cviceni: Kvíz: export +nadpis_kvizu: Přiřaďte formát k použití +--- + +# Výklad + +## Tři výstupy +*Formát* + +- JPG je fotka bez průhlednosti. Kvalita 10, tedy kolem 80 %, na web. +- PNG drží průhlednost. Berte ho na logo a ikonu, ne na velkou fotku. +- PDF je pro tiskárnu. Vrstvy v něm být nemusí, musí sedět rozměr a ořez. +- PSD není výstup. PSD je pracovní soubor a odevzdává se k němu. + +> Tip: GIF na fotku neberte. Umí málo barev a školní plakát v něm pruhuje. + +## Web +*sRGB* + +- Delší strana webového JPG je 1600 px. Větší soubor na stránku školy netřeba. +- Barvy převeďte do sRGB, ať je telefon i projektor čtou stejně. +- Do webového JPG nedávejte spadávku ani ořezové značky. Ty jsou pro tiskárnu. +- Náhled pojmenujte plakat-web.jpg. Jméno Bez názvu-1.jpg se ve složce ztratí. + +> Tip: Úpravy > Převést do profilu, cíl sRGB. Obrazovkový JPG pak není v tajném profilu tiskárny. + +## Tisk +*Značky* + +- PDF plakátu je v rozměru se spadávkou, 303 × 426 mm, 300 ppi. +- Ořezové značky ukážou, kudy vede čistý A3. Ve Photoshopu je přidáte při exportu PDF, nebo je nakreslíte do spadávky, ne do textu. +- Do PDF nepatří vodítka a názvy vrstev. Patří do něj plochý obraz. +- Písmo, které tiskárna nemá, do PDF vloží export. V Photoshopu je text stejně často už vykreslený v obraze, to tiskárně nevadí. + +> Tip: Soubor > Uložit kopii a formát Photoshop PDF. Zaškrtněte, že se nemá ptát na vrstvy, ať je PDF ploché. + +## Zadání: dva výstupy z jednoho PSD +*Cvičení* + +- Z plakátu udělejte plakat-web.jpg, delší strana 1600 px, sRGB, kvalita 10, bez značek. +- Z téhož PSD udělejte plakat-tisk.pdf, 303 × 426 mm, 300 ppi, s ořezovými značkami. +- PSD plakat.psd si nechte s vrstvami. Do PDF vrstvy nedávejte. +- Obě kopie otevřete a zkontrolujte: JPG nemá značky, PDF nemá oříznutý text. + +> Tip: Když je JPG větší než 1600 px, obraz jste zmenšili špatně. Obraz > Velikost obrazu, přepočet Automaticky. + +# Kvíz + +## Který formát drží průhledné logo? +- JPG +- [x] PNG +- MP4 +- WAV +> JPG průhlednost vyplní bílou nebo černou. PNG ji umí. + +## Jak široký je webový náhled plakátu? +- [x] 1600 px na delší straně +- 303 mm +- 80 px +- Full HD a zároveň 300 ppi +> 1600 px stačí na obrazovku. Tiskové milimetry do JPG nepatří. + +## Co musí mít tiskové PDF plakátu? +- [x] Rozměr se spadávkou a ořezové značky +- Vrstvy Photoshopu a vodítka +- 25 sn/s +- Průhledné pozadí jako PNG +> Tiskárna řeže podle značek. Vrstvy a vodítka jsou věc PSD, ne PDF. + +## Který soubor je pracovní a nejde nahradit exportem? +- JPG +- [x] PSD +- PDF +- PNG +> Z PSD uděláte JPG i PDF znovu. Z JPG vrstvy neobnovíte. diff --git a/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.en.md b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.en.md new file mode 100644 index 0000000..6f7f064 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a poster +popis: A3 with bleed, a mask, curves and two exports. +nazev_cviceni: Quiz: the finished poster +nadpis_kvizu: Check the print file and the web file +--- diff --git a/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.md b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.md new file mode 100644 index 0000000..994b6d0 --- /dev/null +++ b/content/prezentacni-grafika/2-rocnik/08-zadani-plakat.md @@ -0,0 +1,79 @@ +--- +id: pg-2-zadani-plakat +nazev: Zadání: plakát +popis: A3 se spadávkou, maskou, křivkami a dvěma exporty. +nazev_cviceni: Kvíz: hotový plakát +nadpis_kvizu: Zkontrolujte tisk i web +--- + +# Výklad + +## Zadání akce +*Obsah* + +- Plakát zve na školní den 12. 6. v tělocvičně. Název si zvolte jeden a držte ho. +- Formát A3 na výšku, plátno 303 × 426 mm, 300 ppi, RGB, spadávka 3 mm. +- Na plakátu je fotka, jeden hlavní nápis, datum, místo a malé logo nebo značka školy. +- Text je aspoň 10 mm od čistého ořezu. Ve spadávce smí být jen pozadí. + +> Tip: Jeden plakát, jedna akce. Nepřidávejte druhé datum a druhý sál. + +## Jak je složený +*Vrstvy* + +- Skupina foto: fotka, maska předmětu, křivky jako vrstva úprav, pozadí rozostřené 8 px. +- Skupina grafika: tvarový pruh 75 % a případný další tvar. Stín pruhu 8 / 16 / 40 %. +- Skupina text: název a údaje, pořád jako text, ne jako pixely. +- Vypnutí kterékoli skupiny musí být vidět. Když ne, je skupina prázdná nebo je všechno sloučené. + +> Tip: Křivka má bod světel kolem 245 a stínů kolem 15. Doostření předmětu 80 %, 1 px, práh 2. + +## Dva výstupy +*Soubory* + +- plakat.psd s vrstvami. +- plakat-web.jpg, delší strana 1600 px, sRGB, kvalita 10, bez ořezových značek. +- plakat-tisk.pdf, 303 × 426 mm, ořezové značky, bez vrstev. +- Mockup na zdi je navíc, ne místo PDF. Když ho přidáte, je to mockup.psd. + +> Tip: Soubory leží v jedné složce plakat. Název Bez názvu v odevzdání neberte. + +## Kontrola +*Než to odevzdáte* + +- Na 100 % nemá retuš opakovaný vzor a maska nemá barevný lem. +- Datum 12. 6. je celé a čitelné. Logo není rozmazaný JPG zvětšený z 80 px. +- Webový JPG otevřený na celou obrazovku nemá značky ani šedé okraje plátna. +- PDF má stejný název akce jako PSD. Dvě verze textu jsou chyba. + +> Tip: Vytiskněte si PDF na A4 jako náhled. Když je text u kraje už na A4, na A3 bude u řezu taky. + +# Kvíz + +## Jaké je plátno tiskového plakátu? +- 1920 × 1080 px, 72 ppi +- [x] 303 × 426 mm, 300 ppi +- A4 bez spadávky +- 1600 px +> A3 je 297 × 420, spadávka 3 mm z každé strany přidá 6 mm. 300 ppi je tisk. + +## Jak je rozostřené pozadí? +- [x] 8 px, předmět zůstane ostrý +- 8 px přes celý plakát včetně textu +- 80 % Doostření na pozadí +- Nijak +> Rozostření oddělí pozadí. Text a hlavní předmět jsou ostré. + +## Co je ve webovém JPG navíc chyba? +- sRGB a 1600 px +- [x] Ořezové značky +- Kvalita kolem 10 +- Název akce +> Značky jsou pro tiskárnu. Na webu vypadají jako čáry v rozích. + +## Který soubor musí obsahovat vrstvy? +- plakat-web.jpg +- [x] plakat.psd +- plakat-tisk.pdf +- Žádný +> PSD je práce. JPG a PDF jsou ploché kopie. diff --git a/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.en.md b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.en.md new file mode 100644 index 0000000..2474ba9 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.en.md @@ -0,0 +1,6 @@ +--- +nazev: Project and media +popis: A Full HD sequence, 25 fps and bins for the media. +nazev_cviceni: Quiz: the project +nadpis_kvizu: Set up the right sequence +--- diff --git a/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.md b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.md new file mode 100644 index 0000000..791315e --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/01-projekt-a-media.md @@ -0,0 +1,79 @@ +--- +id: pg-3-projekt +nazev: Projekt a média +popis: Sekvence Full HD, 25 sn/s a složky materiálu. +nazev_cviceni: Kvíz: projekt +nadpis_kvizu: Založte správnou sekvenci +--- + +# Výklad + +## Projekt +*Soubor* + +- Premiere ukládá práci do projektu s příponou prproj. Video v něm není, jsou v něm odkazy. +- Materiál a projekt mějte v jedné složce. Když video přesunete, Premiere ho nenajde. +- Ztracený soubor se připojí znovu: pravý klik, Propojit médium, a ukážete novou cestu. +- Automatické ukládání nechte zapnuté. Projekt se jinak po pádu nevrátí celý. + +> Tip: prproj není mp4. Otevře ho jen Premiere. Na odevzdání videa je až export. + +## Sekvence +*25 sn/s* + +- Sekvence je časová osa s daným rozměrem a snímkovou frekvencí. +- Školní zadání: 1920 × 1080, čtvercové pixely, 25 snímků za sekundu. +- 25 sn/s je běžné evropské video. Materiál z telefonu má často 30. Sekvenci přesto založte na 25, Premiere snímky přepočítá. +- Pojmenujte ji skolni-den. Název Sekvence 01 se ve větším projektu ztratí. + +> Tip: Nová sekvence je v panelu Projekt ikonou nového objektu. Předvolba DSLR nebo Digital SLR 1080p25 se trefí. + +## Složky +*Pořádek* + +- V panelu Projekt založte složky Záběry, Zvuk a Grafika. +- Import je Ctrl+I, nebo soubor přetáhnete do správné složky. +- Do Grafiky patří logo a titulky. Do Zvuku hudba a komentář. Do Záběrů jen video. +- Na časovou osu netáhněte deset záběrů najednou, dokud ve složce nemají pořádek. + +> Tip: Přejmenujte záběry podle místa, ne podle DSC_1842. Za rok nepoznáte, co to bylo. + +## Zadání: prázdný projekt +*Cvičení* + +- Založte skolni-den.prproj ve složce, kde už leží záběry. +- Sekvence skolni-den: 1920 × 1080, 25 sn/s. +- Tři složky: Zabery, Zvuk, Grafika. Importujte aspoň jeden záběr a jednu hudbu do správných složek. +- Projekt uložte. Mp4 ještě nedělejte. + +> Tip: Když Premiere hlásí, že sekvence nesedí se záběrem, nechte sekvenci na 25. Je to zadání, ne chyba souboru. + +# Kvíz + +## Jakou příponu má projekt Premiere? +- mp4 +- [x] prproj +- psd +- svg +> prproj je projekt. mp4 vznikne až exportem. + +## Jaká je školní sekvence? +- 1280 × 720, 60 sn/s +- [x] 1920 × 1080, 25 sn/s +- 303 × 426 mm +- A4, 300 ppi +> Full HD a 25 snímků za sekundu. Milimetry patří plakátu, ne videu. + +## Kam v projektu patří hudba? +- Do složky Záběry +- [x] Do složky Zvuk +- Přímo do názvu sekvence +- Do PSD +> Záběry, Zvuk a Grafika se nemíchají. Jinak desátý soubor už nenajdete. + +## Co uděláte, když Premiere po přesunu složky video nevidí? +- Natočíte ho znovu +- [x] Propojíte médium a ukážete novou cestu +- Převedete projekt do JPG +- Změníte sekvenci na 72 ppi +> Projekt si pamatuje cestu. Propojit médium ji opraví, záběr se netočil znovu. diff --git a/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.en.md b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.en.md new file mode 100644 index 0000000..5a9e1e6 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.en.md @@ -0,0 +1,6 @@ +--- +nazev: The timeline and the cut +popis: Cuts with no gaps, 45 seconds long. +nazev_cviceni: Quiz: the cut +nadpis_kvizu: Pick the tool and the length +--- diff --git a/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.md b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.md new file mode 100644 index 0000000..5a047a2 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/02-casova-osa-a-strih.md @@ -0,0 +1,79 @@ +--- +id: pg-3-strih +nazev: Časová osa a střih +popis: Střih záběrů bez mezer, délka 45 sekund. +nazev_cviceni: Kvíz: střih +nadpis_kvizu: Vyberte nástroj a délku +--- + +# Výklad + +## Stopy +*Obraz a zvuk* + +- V1 je první obrazová stopa, A1 první zvuková. Vyšší V leží vizuálně nad nižší. +- Záběr z kamery přinese obraz i zvuk. Na ose jsou spojené, dokud je nerozdělíte. +- Titulek patří na V2, ať nepřepíše záběr na V1. Sundáte ho a obraz pod ním zůstane. +- Mezera na ose je černé pole ve filmu. Mezi záběry být nemá. + +> Tip: Když přetáhnete jen obraz, držte Alt. Zvuk zůstane na místě, nebo se naopak nevezme. + +## Nástroje +*Žiletka* + +- V je výběr. C je žiletka, rozřízne záběr v místě přehrávací hlavy. +- I označí začátek, O konec. Čárka vloží výběr a zbytek odsunete. Tečka výběr přepíše. +- Delete smaže a nechá díru. Shift+Delete, nebo vyjmutí s uzavřením mezery, díru zatáhne. +- Mezerník přehrává. Šipky vlevo a vpravo jedou po střihu. + +> Tip: Po žiletce přepněte zpět na V. Jinak další klik zase řeže. + +## Co nechat +*45 sekund* + +- Ze surového záznamu nechte to, co nese větu nebo děj. Rozjezd a konec kamery pryč. +- Školní sestřih tohoto cvičení má 45 sekund. Kratší nestihne nic říct, delší se rozpadá. +- Střih je na pohybu nebo na začátku věty, ne uprostřed slova. +- Stejný záběr dvakrát po sobě nechte jen schválně. Jinak to vypadá jako chyba. + +> Tip: Délku sekvence čtete vpravo dole nebo na konci posledního záběru. 00:00:45:00 je 45 sekund. + +## Zadání: sestřih +*Cvičení* + +- Na ose skolni-den sestříhejte 45 sekund. Začátek je 00:00:00:00, konec 00:00:45:00. +- Mezi záběry není mezera. Černý snímek uprostřed je chyba. +- Aspoň čtyři různé záběry. Žádný kratší než jedna sekunda. +- Uložte projekt. Export až v pozdější lekci. + +> Tip: Když je konec 00:00:45:10, máte o 10 snímků navíc. U 25 sn/s je to necelá půlsekunda, zkraťte to. + +# Kvíz + +## Čím se záběr rozřízne? +- Nástrojem V +- [x] Žiletkou, klávesa C +- Exportem H.264 +- Krytím 70 % +> C je žiletka. V je výběr, tím se záběr posouvá, neřeže. + +## Jak dlouhý je sestřih v této lekci? +- [x] 45 sekund +- 8 sekund +- 4 minuty +- 303 mm +> 00:00:45:00. Titulky a export přijdou později, teď je čistý střih. + +## Co je chyba uprostřed sestřihu? +- Střih na začátku věty +- [x] Černá mezera mezi záběry +- Záběr delší než sekunda +- Stopa V1 +> Díra na ose je ve filmu černá. Po smazání ji zatáhněte. + +## Kam patří titulek, aby šel sundat bez ztráty záběru? +- Místo obrazu na V1 +- [x] Na stopu V2 nad záběrem +- Do složky Zvuk +- Do PSD plakátu +> V2 leží nad V1. Vypnete ho a záběr pod ním pokračuje. diff --git a/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.en.md b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.en.md new file mode 100644 index 0000000..c124f37 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.en.md @@ -0,0 +1,6 @@ +--- +nazev: Pace and transitions +popis: A hard cut as the default, one short dissolve. +nazev_cviceni: Quiz: transitions +nadpis_kvizu: Choose the length of the dissolve +--- diff --git a/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.md b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.md new file mode 100644 index 0000000..132fc8e --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/03-tempo-a-prechody.md @@ -0,0 +1,79 @@ +--- +id: pg-3-prechody +nazev: Tempo a přechody +popis: Tvrdý střih jako základ, jedna krátká prolínačka. +nazev_cviceni: Kvíz: přechody +nadpis_kvizu: Zvolte délku prolínačky +--- + +# Výklad + +## Střih stačí +*Přechod* + +- Obyčejný střih je nejčistší. Přechod mezi každými dvěma záběry film zpomalí a vypadá jako šablona. +- Prolínačku berte na změnu času nebo na vstup titulku, ne na každý řez. +- Stírání, hvězdičky a stránkování ve školním dokumentu nemají co dělat. +- Jeden typ přechodu v celém videu. Druhý je už výstřednost. + +> Tip: Přechod přetáhnete z panelu Účinky mezi dva záběry. Dvojklik otevře délku. + +## Délka +*12 snímků* + +- Při 25 sn/s je sekunda 25 snímků. Půl sekundy je zhruba 12 snímků. +- Školní prolínačka má 12 snímků. Delší než sekunda už je nálada, ne střih. +- Přechod potřebuje materiál za řezem. Když záběr končí přesně na řezu, Premiere ho nemá čím prolnout a konec zmrazí. +- Proto neřežte záběr úplně na doraz, když víte, že na něj přijde prolínačka. + +> Tip: 12 snímků je 00:00:00:12. V časovém kódu jsou snímky až za poslední dvojtečkou. + +## Rychlost +*Polovina* + +- Pravý klik na záběr, Rychlost a trvání. 50 % ho přehraje poloviční rychlostí a dvakrát prodlouží. +- 100 % je normální tempo. 200 % je dvojnásobná rychlost a poloviční délka. +- Zpomalení použijte na jeden záběr, který má být čtený: vstup do dveří, pohled, detail. +- Zvuk zpomalený na 50 % je bručivý. U zpomaleného záběru ho oddělte a nechte původní, nebo ho ztichte. + +> Tip: Když má zůstat délka a změnit se jen tempo, hlídejte zaškrtnutí, které drží nebo nedrží délku záběru. + +## Zadání: jeden přechod, jedno tempo +*Cvičení* + +- V 45sekundovém sestřihu nechte tvrdé střihy. +- Jednu prolínačku dejte jen před titulek nebo mezi dvě místa. Délka 12 snímků. +- Jeden záběr zpomalte na 50 % a jeho zvuk nechte v původní rychlosti, nebo ho vypněte. +- Délka celku ať zůstane čitelná. Když se sekvence protáhne, zkraťte jiný záběr. + +> Tip: Přehrávání od prolínačky: mezerník. Když obraz na vteřinu zmrzne, záběru chybí přesah. + +# Kvíz + +## Jak dlouhá je školní prolínačka? +- [x] 12 snímků +- 12 sekund +- 4 minuty +- 3 mm +> 12 snímků při 25 sn/s je necelá půlsekunda. 12 sekund by bylo uspávací. + +## Kolik prolínaček má být v tomto cvičení? +- Mezi všemi záběry +- [x] Jedna +- Žádná nikdy, ani před titulkem +- Deset různých typů +> Základ je tvrdý střih. Jedna prolínačka má důvod, deset je šablona. + +## Co udělá rychlost 50 %? +- Záběr zrychlí na dvojnásobek +- [x] Přehrá ho poloviční rychlostí a prodlouží +- Ořízne ho na 50 px +- Převede ho do CMYK +> 50 % je polovina tempa. 200 % je dvojnásobná rychlost. + +## Proč prolínačka na konci záběru někdy zmrazí obraz? +- Sekvence je v PNG +- [x] Za řezem už není materiál, kterým by šla prolnout +- Chybí spadávka +- Hudba je moc potichu +> Přechod potřebuje snímky za střihem. Doraz na posledním snímku je nemá. diff --git a/content/prezentacni-grafika/3-rocnik/04-titulky.en.md b/content/prezentacni-grafika/3-rocnik/04-titulky.en.md new file mode 100644 index 0000000..0393aa0 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/04-titulky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Titles +popis: An opening and a closing, four seconds each. +nazev_cviceni: Quiz: titles +nadpis_kvizu: Fill in the time and the size +--- diff --git a/content/prezentacni-grafika/3-rocnik/04-titulky.md b/content/prezentacni-grafika/3-rocnik/04-titulky.md new file mode 100644 index 0000000..88bceec --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/04-titulky.md @@ -0,0 +1,79 @@ +--- +id: pg-3-titulky +nazev: Titulky +popis: Úvod a závěr, každé po čtyřech sekundách. +nazev_cviceni: Kvíz: titulky +nadpis_kvizu: Doplňte čas a velikost +--- + +# Výklad + +## Kde se píšou +*Grafika* + +- Titulek je vrstva na ose, ne text vypálený do záběru ve Photoshopu. +- Panel Základní grafika, nebo nástroj text nad obrazem v programu, založí textovou vrstvu. +- Úvodní titulek leží na začátku na V2. Závěrečný na konci. +- Písmo nechte jedno. Bílé na tmavém podkladu, nebo tmavé na světlém pruhu. + +> Tip: Když text píšete do záběru razítkem, už ho nepřepíšete. Vrstva textu jde opravit kdykoli. + +## Velikost a čas +*4 sekundy* + +- Nadpis Školní den má kolem 72 pt. Jméno autora kolem 36 pt. +- Každý titulek trvá 4 sekundy. Kratší nestačí přečíst, delší drží obraz zbytečně. +- 4 sekundy při 25 sn/s jsou 100 snímků. +- Mezi úvodním titulkem a prvním záběrem může být prolínačka 12 snímků z minulé lekce. + +> Tip: Délku textové vrstvy táhnete za konec na ose jako záběr. Číslo 00:00:04:00 je přesně čtyři sekundy. + +## Bezpečný okraj +*Nepřeříznout* + +- Text nepatří až k hraně obrazu. Od kraje nechte aspoň 80 px. +- Televize a některé přehrávače kraj oříznou. Co je 40 px od hrany, může zmizet. +- Dlouhý název rozdělte na dva řádky. Jeden řádek přes celou šířku je malý, nebo přeteče. +- Zarovnání na střed jen u titulku. U seznamu jmen nechte zarovnání vlevo. + +> Tip: V nastavení náhledu se dá zapnout bezpečná oblast. Vnitřní rámeček je pro text. + +## Zadání: úvod a závěr +*Cvičení* + +- Na začátek dejte 4 sekundy titulku Školní den, kolem 72 pt, aspoň 80 px od kraje. +- Na konec 4 sekundy se jménem autora, kolem 36 pt. +- Oba texty jsou na V2 a jdou přepsat. Nejsou vypálené v záběru. +- Projekt uložte. Délka sekvence teď smí být delší než 45 sekund o těchto 8 sekund. + +> Tip: Když titulek leží na V1 místo záběru, přesuňte ho na V2. Záběr pod ním ať pokračuje, nebo ať je pod úvodem prázdná plocha schválně. + +# Kvíz + +## Jak dlouho trvá úvodní titulek? +- [x] 4 sekundy +- 12 snímků +- 45 sekund +- 1 snímek +> Čtyři sekundy se dají přečíst. 12 snímků je délka prolínačky, ne titulku. + +## Jak velké je zhruba jméno autora? +- 8 pt +- [x] kolem 36 pt +- 300 pt +- 1600 px +> Nadpis je kolem 72 pt, jméno menší, kolem 36. Pořád čitelné na telefonu. + +## Jak daleko od kraje má text být? +- Až v posledních 10 px +- [x] Aspoň 80 px +- Ve spadávce 3 mm +- To je jedno, video se neořezává +> Kraj obrazu přehrávač někdy seřízne. 80 px text udrží uvnitř. + +## Na které stopě má titulek ležet? +- A1 +- [x] V2 +- Jen v JPG plakátu +- Místo zvuku +> V2 je nad obrazem. Titulek jde sundat a záběr zůstane. diff --git a/content/prezentacni-grafika/3-rocnik/05-zvuk.en.md b/content/prezentacni-grafika/3-rocnik/05-zvuk.en.md new file mode 100644 index 0000000..2bbc076 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/05-zvuk.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sound +popis: Speech, music under it and a one-second fade. +nazev_cviceni: Quiz: sound +nadpis_kvizu: Set the levels +--- diff --git a/content/prezentacni-grafika/3-rocnik/05-zvuk.md b/content/prezentacni-grafika/3-rocnik/05-zvuk.md new file mode 100644 index 0000000..a1779e7 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/05-zvuk.md @@ -0,0 +1,79 @@ +--- +id: pg-3-zvuk +nazev: Zvuk +popis: Řeč, hudba pod ní a jedna sekunda náběhu. +nazev_cviceni: Kvíz: zvuk +nadpis_kvizu: Nastavte hladiny +--- + +# Výklad + +## Dvě hladiny +*Řeč a hudba* + +- Řeč je hlavní. Na ukazateli ať se drží kolem −6 dB a do červené nuly nelezete. +- Hudba sama o sobě kolem −15 dB. Pod řečí ji stáhněte ještě na −24 dB. +- Dva stejně hlasité zdroje se přebijí. Divák pak neslyší ani jeden pořádně. +- Ztlumení je klíčový snímek hlasitosti na klipu, ne jednou provždy na celou píseň, pokud pod ní jen občas mluvíte. + +> Tip: Pravý klik na zvukový klip a zobrazení klíčových snímků hlasitosti. Přidáte je perem na čáře. + +## Náběh +*1 sekunda* + +- Hudba nemá nastoupit v plné hlasitosti na prvním snímku. Náběh 1 sekundu. +- Konec hudby stejně tak: jednu sekundu do ticha, ať video neuskakuje. +- Náběh je taky klíčový snímek, z ticha na cílovou hladinu. +- Střih hudby uprostřed taktu je slyšet. Řežte v pauze nebo na začátku fráze. + +> Tip: 1 sekunda je 25 snímků. Na ose je to krátký úsek, ne celý titulek. + +## Střih zvuku napřed +*J-cut* + +- J-cut znamená, že zvuk dalšího záběru začne dřív než jeho obraz. +- Nejdřív je slyšet věta, až pak se obraz přepne na mluvčího. Střih je měkčí než tvrdý řez obou stop. +- Obraz a zvuk jednoho klipu rozdělíte pravým klikem, Zrušit propojení, a zvuk přetáhnete doleva. +- Ve školním videu stačí jeden takový střih. Nemusí být všude. + +> Tip: Když zvuk předběhne obraz o víc než půl sekundy, už to působí jako chyba synchronu. Držte se kolem 10 až 15 snímků. + +## Zadání: hudba pod řečí +*Cvičení* + +- Hudbu dejte pod sestřih. Mimo řeč kolem −15 dB, pod řečí −24 dB. +- Náběh i doběh hudby 1 sekunda. +- Řeč srovnejte kolem −6 dB. Červená nula na ukazateli je chyba. +- Jeden přechod mezi záběry udělejte jako J-cut: zvuk o kousek dřív než obraz. + +> Tip: Sluchátka odhalí hukot, reproduktor v učebně ho schová. Poslechněte si to jednou na sluchátkách. + +# Kvíz + +## Kde má být hudba, když zrovna někdo mluví? +- [x] Kolem −24 dB +- Na nule +- Vypnutá natrvalo i v místech bez řeči +- Hlasitější než řeč +> Řeč je kolem −6 dB. Hudba pod ní musí ustoupit, školní hladina je −24 dB. + +## Jak dlouhý je náběh hudby? +- [x] 1 sekunda +- 12 sekund +- 4 minuty +- Náběh se nedělá +> Jedna sekunda z ticha. Nástup v plné hlasitosti na prvním snímku je skok. + +## Co je J-cut? +- Obraz dalšího záběru začne dřív než jeho zvuk +- [x] Zvuk dalšího záběru začne dřív než jeho obraz +- Střih uprostřed slova +- Přechod hvězdičkou +> Nejdřív je slyšet, až pak je vidět. Opačné pořadí je L-cut. + +## Kam nesmí ukazatel řeči? +- K −6 dB +- [x] Do červené nuly +- Na stopu A1 +- Pod hudbu +> Nula je přebuzení. Řeč se drží pod ní, kolem −6 dB. diff --git a/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.en.md b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.en.md new file mode 100644 index 0000000..59224c3 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.en.md @@ -0,0 +1,6 @@ +--- +nazev: Colour in video +popis: Exposure first, then contrast, saturation last. +nazev_cviceni: Quiz: video colour +nadpis_kvizu: Put the steps in order +--- diff --git a/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.md b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.md new file mode 100644 index 0000000..c6f8264 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/06-barva-ve-videu.md @@ -0,0 +1,79 @@ +--- +id: pg-3-barva +nazev: Barva ve videu +popis: Nejdřív expozice, potom kontrast, sytost až nakonec. +nazev_cviceni: Kvíz: barva videa +nadpis_kvizu: Seřaďte postup +--- + +# Výklad + +## Lumetri +*Jeden panel* + +- Barva záběru se dělá efektem Lumetri Color, ne exportem přes Photoshop. +- Efekt dejte na záběr, nebo na úpravu vrstvy nad stopou, ať se dá vypnout. +- Nejdřív srovnejte expozici, ať není záběr tmavý nebo přepálený. +- Pak kontrast, nakonec sytost. Sytá tmavá fotka se špatně zachraňuje. + +> Tip: Panel Barva Lumetri otevřete z nabídky Okno. Když efekt jen přetáhnete a panel zavřete, hodnoty stejně zůstanou na klipu. + +## Dva záběry vedle sebe +*Srovnat* + +- Střih mezi teplým a studeným záběrem je vidět víc než samotná barva. +- Druhý záběr přiblížte prvnímu: podobné stíny, podobná pleť. +- Pleť nenechte oranžovou. Když je sytost vysoko, uberte ji dřív, než přidáte teplou barvu. +- Jeden záběr smí být černobílý schválně. Sytost 0. Ostatní nechte barevné. + +> Tip: Oko porovná záběry, když je přehrajete přes střih. Zastavený snímek klame. + +## LUT až potom +*Míra* + +- LUT je hotová tabulka vzhledu. Dá se na záběr jako vzhled, ne jako první krok. +- Intenzitu LUT nechte kolem 50 %, když záběr přebarví moc. +- Dva různé LUT v jednom minutovém videu působí jako dvě školy. Stačí jeden, nebo žádný. +- Bez LUT, jen se srovnanou expozicí, je video často čistší. + +> Tip: LUT na přepálenou oblohu kresbu nevrátí. Nejdřív expozice, tabulka až na srovnaný obraz. + +## Zadání: srovnat a jeden bez barvy +*Cvičení* + +- Dva sousední záběry srovnejte Lumetri: nejdřív expozice, pak kontrast, sytost jen mírně, třeba +10. +- Jeden jiný záběr dejte na sytost 0, ať je černobílý schválně a krátce. +- LUT buď žádný, nebo jeden na intenzitě nejvýš 50 %. +- Efekt ať jde vypnout. Vypnuté Lumetri vrátí původní barvu záběru. + +> Tip: Když vypnutí efektu nic nezmění, upravovali jste jiný klip. Efekt je na tom, který je na ose vybraný. + +# Kvíz + +## V jakém pořadí se barva srovnává? +- Nejdřív sytost, pak třeba expozice +- [x] Nejdřív expozice, pak kontrast, sytost nakonec +- Nejdřív LUT, surový záběr se nechá +- Barva se ve videu nedělá +> Špatně světlý záběr sytost jen zhorší. Nejdřív světlo, potom síla barvy. + +## Jak uděláte jeden záběr černobílý? +- Smažete stopu V1 +- [x] Sytost na 0 +- Exportem do JPG +- Rychlostí 50 % +> Sytost 0 nechá jas a vezme barvu. Ostatní záběry zůstanou barevné. + +## Jak vysoko necháte intenzitu LUT, když barví moc? +- 100 % vždy +- [x] Kolem 50 % +- 0 %, ale LUT stejně všechno přebarví +- 300 % +> LUT je volba vzhledu, ne povinnost. Poloviční intenzita ho podrží na uzdě. + +## Čím se barva na záběru vypne? +- Smazáním projektu +- [x] Vypnutím efektu Lumetri +- Převodem sekvence na 72 ppi +- To nejde, export je trvalý už na ose +> Efekt na klipu jde vypnout. Dokud není video exportované, původní záběr zůstává. diff --git a/content/prezentacni-grafika/3-rocnik/07-export-videa.en.md b/content/prezentacni-grafika/3-rocnik/07-export-videa.en.md new file mode 100644 index 0000000..9644011 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/07-export-videa.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exporting the video +popis: H.264, Full HD, 25 fps and 10 Mb/s. +nazev_cviceni: Quiz: the video export +nadpis_kvizu: Pick the format and the bitrate +--- diff --git a/content/prezentacni-grafika/3-rocnik/07-export-videa.md b/content/prezentacni-grafika/3-rocnik/07-export-videa.md new file mode 100644 index 0000000..9675ca9 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/07-export-videa.md @@ -0,0 +1,79 @@ +--- +id: pg-3-export +nazev: Export videa +popis: H.264, Full HD, 25 sn/s a 10 Mb/s. +nazev_cviceni: Kvíz: export videa +nadpis_kvizu: Vyberte formát a datový tok +--- + +# Výklad + +## Formát +*H.264* + +- Odevzdává se MP4 s kodekem H.264. Otevře ho telefon, projektor i Windows. +- Formát projektu prproj nikdo bez Premiere nepustí. Není to výstup. +- Sekvence 1920 × 1080 a 25 sn/s se do exportu přenese. Neměňte ji při exportu na 1280 × 720, pokud to zadání nechce. +- Zvuk nechte v exportu zapnutý. Němý výstup je často jen odškrtnutý zvuk, ne záměr. + +> Tip: Soubor > Export > Média. Zkratka Ctrl+M. Náhled vpravo ukáže první snímek, ne celý střih. + +## Datový tok +*10 Mb/s* + +- Cílový datový tok 10 Mb/s stačí na školní Full HD. Vyšší soubor zbytečně naroste. +- 1 Mb/s už na 1080p kostkuje, hlavně v pohybu. 10 je klidná střední hodnota. +- Nastavení zdroje, nebo shoda se sekvencí, podrží 25 sn/s a rozměr. +- Dva průchody export zpřesní, ale trvá déle. Na školní video stačí jeden. + +> Tip: Mb/s je megabit za sekundu, ne megabajt. Minutové video při 10 Mb/s má zhruba 75 MB. + +## Rozsah +*Celá sekvence* + +- Exportujte celou sekvenci, ne jen výběr mezi body In a Out, pokud jste je nechali uprostřed. +- Zaškrtnutí, které exportuje jen vstup až výstup, umí oříznout film na pět sekund. +- Název souboru skolni-den.mp4. Cesta ať vede do složky projektu, ne na plochu. +- Po exportu soubor pusťte mimo Premiere. V náhledu programu chybu často nevidíte. + +> Tip: Když je film moc krátký, otevřete body In a Out na ose. Ctrl+Shift+X je zruší. + +## Zadání: první mp4 +*Cvičení* + +- Exportujte skolni-den.mp4, H.264, 1920 × 1080, 25 sn/s, cílový tok 10 Mb/s. +- Zvuk je v souboru. Úvodní titulek je hned na začátku, ne po deseti sekundách černé. +- Projekt prproj nechte vedle mp4. Mp4 samo o sobě nejde znovu střihnout po vrstvách. +- Soubor přehrajte celý. Jednou. Zastavte se na titulku, na prolínačce a na konci hudby. + +> Tip: Když přehrávač hlásí, že soubor nejde otevřít, export nedoběhl. V Premiere je fronta, podívejte se, jestli neskončil chybou. + +# Kvíz + +## Který soubor se odevzdává jako video? +- prproj +- [x] mp4 v H.264 +- psd +- svg +> MP4 otevře i člověk bez Premiere. Projekt je zdroj. + +## Jaký datový tok má školní Full HD? +- [x] 10 Mb/s +- 1 Mb/s +- 300 ppi +- 72 Mb/s vždy +> 10 Mb/s je na 1080p25 dost. 1 Mb/s se v pohybu rozpadá. + +## Co zkontrolujete, když je export dlouhý jen pár sekund? +- Spadávku +- [x] Jestli se neexportoval jen úsek mezi In a Out +- Režim CMYK +- Krytí pruhu +> Body In a Out omezí export na výběr. Zrušte je a exportujte celou sekvenci. + +## Proč nestačí odevzdat jen prproj? +- Premiere ho neumí uložit +- [x] Bez Premiere ho nikdo nepřehraje a média mohou chybět +- Nemá 25 sn/s +- Je to totéž co mp4 +> Projekt je odkaz na střih. Film je až mp4. diff --git a/content/prezentacni-grafika/3-rocnik/08-zadani-video.en.md b/content/prezentacni-grafika/3-rocnik/08-zadani-video.en.md new file mode 100644 index 0000000..4bacdcf --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/08-zadani-video.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a short video +popis: 40 to 60 seconds, titles, sound and one J-cut. +nazev_cviceni: Quiz: the short video +nadpis_kvizu: Check the length and the export +--- diff --git a/content/prezentacni-grafika/3-rocnik/08-zadani-video.md b/content/prezentacni-grafika/3-rocnik/08-zadani-video.md new file mode 100644 index 0000000..f415877 --- /dev/null +++ b/content/prezentacni-grafika/3-rocnik/08-zadani-video.md @@ -0,0 +1,79 @@ +--- +id: pg-3-zadani-video +nazev: Zadání: krátké video +popis: 40 až 60 sekund, titulky, zvuk a jeden J-cut. +nazev_cviceni: Kvíz: krátké video +nadpis_kvizu: Zkontrolujte délku a export +--- + +# Výklad + +## Délka a obsah +*40 až 60 s* + +- Hotové video trvá 40 až 60 sekund včetně titulků. +- Téma je školní den: místo, lidé, jedna věta o tom, co se děje. +- Úvodní titulek Školní den trvá 4 sekundy. Závěrečný se jménem taky 4 sekundy. +- Aspoň čtyři záběry. Žádný pod jednu sekundu, kromě případného černobílého detailu. + +> Tip: Délku čtete na konci posledního titulku. Titulky se do limitu počítají. + +## Střih a obraz +*Pravidla* + +- Mezi záběry nejsou černé mezery. Střihy jsou tvrdé. +- Jedna prolínačka, 12 snímků, nejlépe mezi titulkem a záběrem. +- Jeden záběr smí být na 50 % rychlosti. Jeden smí mít sytost 0. +- Barva je srovnaná Lumetri: expozice, kontrast, sytost. Nejvýš jeden LUT na 50 %. + +> Tip: V2 je na titulky. Obrazové záběry ať zůstanou na V1, ať se ve stopách neztratíte. + +## Zvuk +*Hladiny* + +- Řeč kolem −6 dB, bez červené nuly. +- Hudba mimo řeč −15 dB, pod řečí −24 dB. Náběh a doběh 1 sekunda. +- Jeden J-cut: zvuk dalšího záběru začne dřív než obraz. +- Hudba nekončí uprostřed slova ani uprostřed taktu, pokud to jde střihnout jinak. + +> Tip: Poslechněte si export, ne jen osu. Export umí zvuk vynechat, když je odškrtnutý. + +## Odevzdání +*Dva soubory* + +- skolni-den.prproj ve složce s médií, složky Zabery, Zvuk, Grafika. +- skolni-den.mp4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s. +- Mp4 pusťte od začátku do konce mimo Premiere. +- Když média přesouváte, nejdřív složku celou. Rozbitý odkaz je neodevzdaná práce. + +> Tip: Na začátku mp4 má být titulek, ne černá. Černá znamená, že sekvence začíná později než export. + +# Kvíz + +## Jak dlouhé je hotové video i s titulky? +- Přesně 45 sekund bez titulků +- [x] 40 až 60 sekund +- 8 sekund +- 4 minuty +> Titulky se počítají. Pod 40 sekund téma nestačí, nad minutu se školní sestřih rozpadá. + +## Jak dlouhé jsou oba titulky? +- 12 snímků +- [x] 4 sekundy každý +- 1 sekunda +- Dokud hraje hudba +> Čtyři sekundy na úvod, čtyři na jméno. 12 snímků je prolínačka. + +## Kam patří hudba v okamžiku, kdy mluví člověk? +- [x] −24 dB +- 0 dB +- Stejně nahlas jako řeč +- Hudba se pod řeč nedává nikdy, ani ztišená +> Řeč zůstává kolem −6 dB. Hudba pod ní ustoupí na −24 dB. + +## Jaký je export? +- prproj jako jediný soubor +- [x] mp4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s +- JPG 1600 px +- PDF se spadávkou +> Film je mp4. Projekt se odevzdává k němu, ne místo něj. diff --git a/content/prezentacni-grafika/4-rocnik/01-illustrator.en.md b/content/prezentacni-grafika/4-rocnik/01-illustrator.en.md new file mode 100644 index 0000000..29c372b --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/01-illustrator.en.md @@ -0,0 +1,6 @@ +--- +nazev: Illustrator: the artboard +popis: Curves, the V, A and P tools, an artboard for the screen. +nazev_cviceni: Quiz: Illustrator +nadpis_kvizu: Name the tool +--- diff --git a/content/prezentacni-grafika/4-rocnik/01-illustrator.md b/content/prezentacni-grafika/4-rocnik/01-illustrator.md new file mode 100644 index 0000000..d6171c5 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/01-illustrator.md @@ -0,0 +1,79 @@ +--- +id: pg-4-illustrator +nazev: Illustrator: plocha a nástroje +popis: Křivky, nástroje V, A a P, plátno pro obrazovku. +nazev_cviceni: Kvíz: Illustrator +nadpis_kvizu: Poznejte nástroj +--- + +# Výklad + +## Proč Illustrator +*Křivka* + +- Illustrator kreslí křivky. Logo z něj vytisknete na vizitku i na banner a hrana zůstane hladká. +- Photoshopové logo z malého PNG sem nepatří. Vektor se tu staví znovu, nebo se otevře SVG a AI. +- Soubor je AI. Na výměnu s jiným programem se hodí i SVG a PDF. +- Na fotku Illustrator není. Fotka sem patří jen jako podklad, který pak skryjete. + +> Tip: Když v náhledu na 800 % uvidíte pixely, není to křivka. Je to vložená bitmapa. + +## Tři nástroje +*V, A, P* + +- V je výběr. Posouvá a mění celý objekt. +- A je přímý výběr. Táhne jeden kotevní bod, zbytek tvaru nechá. +- P je pero. Klik klade body, tah klade křivku. Pero je hlavní kreslicí nástroj. +- Na první cvičení pero nepotřebujete celé. Obdélník a elipsa stačí. + +> Tip: Na Macu jsou zkratky stejné. Přepínají se jednou klávesou, bez Cmd. + +## Plátno +*1920 × 1080* + +- Nový dokument pro obrazovku: 1920 × 1080 px, RGB, jeden artboard. +- Pro tisk se později založí milimetrový artboard. Teď je to obrazovka, ať sedí s Premiere a Photoshopem. +- Artboard není vrstva. Je to papír. Objekty mimo něj se neexportují, ale v souboru jsou. +- Bílé pozadí artboardu není objekt. Když chcete barvu v pozadí, nakreslete obdélník. + +> Tip: Více artboardů přijde u loga ve variantách. Zatím jeden. + +## Zadání: dva tvary +*Cvičení* + +- Dokument znacka-podklad.ai, 1920 × 1080 px, RGB. +- Nakreslete jeden obdélník a jednu elipsu. Každý ať má výplň a žádný tah, nebo tah 2 pt schválně. +- Elipsu vyberte nástrojem A a vytáhněte jeden kotevní bod, ať není pravidelná. +- Uložte AI. Do JPG to zatím nepřevádějte. + +> Tip: Výplň a tah jsou dole v panelu nástrojů. Prázdná výplň, červený přeškrtnutý čtverec, objekt neukáže, dokud nemá tah. + +# Kvíz + +## Co dělá nástroj A? +- Posouvá celý objekt +- [x] Táhne jeden kotevní bod +- Píše text ve Photoshopu +- Stříhá video +> A je přímý výběr bodu. V posouvá celý tvar. + +## Který nástroj kreslí křivku po bodech? +- V +- [x] P, pero +- C v Premiere +- Kapátko I +> Pero je P. Žiletka C patří do Premiere, sem ne. + +## Jak velký je školní dokument pro obrazovku? +- [x] 1920 × 1080 px, RGB +- A4, CMYK, 300 ppi jako jediná možnost +- 303 × 426 mm hned +- 80 × 80 px +> Obrazovka je Full HD a RGB. Tiskový artboard přijde, až bude značka na papír. + +## Kdy v náhledu poznáte vloženou bitmapu? +- Na 800 % zůstane hrana hladká +- [x] Na 800 % se objeví pixely +- Soubor má příponu AI, takže pixely nemá nikdy +- Podle stopy V2 +> Křivka se nezvětší do čtverečků. Bitmapa ano. diff --git a/content/prezentacni-grafika/4-rocnik/02-logo.en.md b/content/prezentacni-grafika/4-rocnik/02-logo.en.md new file mode 100644 index 0000000..2a00d3a --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/02-logo.en.md @@ -0,0 +1,6 @@ +--- +nazev: Curves and a logo +popis: A mark made of three shapes, a full version and one colour. +nazev_cviceni: Quiz: the logo +nadpis_kvizu: Check the versions and the export +--- diff --git a/content/prezentacni-grafika/4-rocnik/02-logo.md b/content/prezentacni-grafika/4-rocnik/02-logo.md new file mode 100644 index 0000000..31c6033 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/02-logo.md @@ -0,0 +1,79 @@ +--- +id: pg-4-logo +nazev: Křivky a logo +popis: Značka ze tří tvarů, plná verze a jedna barva. +nazev_cviceni: Kvíz: logo +nadpis_kvizu: Zkontrolujte verze a export +--- + +# Výklad + +## Málo bodů +*Čistá křivka* + +- Logo je jednoduché. Tři tvary stačí: třeba kniha, paprsek a kruh. +- Každý tvar ať má co nejmíň kotevních bodů. Deset bodů na kružnici je zbytečných, elipsa je jeden objekt. +- Tah převeďte na obrys, až když má být tloušťka součástí tvaru. Do té doby ať je tah tahem a jde změnit. +- Text názvu školy nechte jako text, dokud nemáte vybrané písmo. Pak ho můžete převést na křivky. + +> Tip: Objekt > Cesta > Obrysy tahu, nebo Písmo > Vytvořit obrysy. Až v kopii. Původní text ať jde přepsat. + +## Dvě verze +*Plná a jedna barva* + +- Plná verze má barvy školy, nejvýš dvě plus bílou nebo černou. +- Jednobarevná verze je celá černá. Hodí se do razítka, do raznice a na světlý papír. +- Obě verze jsou dva artboardy v jednom AI, ne dva náhodné JPG. +- Na bílém i na černém podkladu musí být značka čitelná. Bílá značka na bílé není verze, je to chyba. + +> Tip: Třetí artboard si nechte na černé pozadí a bílou značku, když ji škola bude dávat na tričko. + +## Export +*SVG a PDF* + +- SVG je pro web. Křivky v něm zůstanou, stránka ho zvětší. +- PDF je pro tisk a pro vložení do InDesignu. +- JPG z loga dělejte jen jako náhled, velký aspoň 1600 px. Jako jediný zdroj je zakázaný. +- Názvy: logo.svg, logo.pdf, logo-cerna.svg. + +> Tip: Při exportu SVG nechte text v obrysech, pokud má značku otevřít někdo bez vašeho písma. + +## Zadání: značka školy +*Cvičení* + +- Nakreslete značku ze tří tvarů a názvu školy. +- Artboard plné verze a artboard černé verze. +- Exportujte SVG a PDF u obou. JPG jen jako náhled 1600 px, navíc. +- Soubor logo.ai si nechte. V něm musí jít značku přebarvit. + +> Tip: Tři tvary, ne třicet. Když značku nepřečtete na 16 px, je moc podrobná. + +# Kvíz + +## Z kolika tvarů je školní značka? +- [x] Ze tří +- Z jedné fotky +- Z dvaceti přechodů +- Z videa +> Tři tvary se pamatují a vejdou se na vizitku. Fotka není logo. + +## Která verze se hodí do razítka? +- Plnobarevná s přechodem +- [x] Jednobarevná černá +- JPG 80 px +- Černobílé video +> Razítko neumí přechod. Černá verze se vytiskne jednou barvou. + +## Který export podrží křivky na webu? +- JPG +- [x] SVG +- MP4 +- prproj +> SVG je vektor. JPG je bitmapa a při zvětšení se rozmaže. + +## Proč nestačí odevzdat jen JPG loga? +- JPG nejde otevřít +- [x] Nejsou v něm křivky a malý soubor nejde zvětšit +- JPG je totéž co AI +- Illustrator JPG neuloží +> Zdroj je AI. SVG a PDF jsou výstupy. JPG je jen náhled. diff --git a/content/prezentacni-grafika/4-rocnik/03-after-effects.en.md b/content/prezentacni-grafika/4-rocnik/03-after-effects.en.md new file mode 100644 index 0000000..dcfedbb --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/03-after-effects.en.md @@ -0,0 +1,6 @@ +--- +nazev: After Effects: a composition +popis: Eight seconds, Full HD, 25 fps and the logo layers. +nazev_cviceni: Quiz: the composition +nadpis_kvizu: Set up the composition +--- diff --git a/content/prezentacni-grafika/4-rocnik/03-after-effects.md b/content/prezentacni-grafika/4-rocnik/03-after-effects.md new file mode 100644 index 0000000..2d9786e --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/03-after-effects.md @@ -0,0 +1,79 @@ +--- +id: pg-4-kompozice +nazev: After Effects: kompozice +popis: Osm sekund, Full HD, 25 sn/s a vrstvy loga. +nazev_cviceni: Kvíz: kompozice +nadpis_kvizu: Založte kompozici +--- + +# Výklad + +## Kompozice +*Jako sekvence* + +- Kompozice v After Effects je plátno v čase. Má rozměr, snímkovou frekvenci a délku. +- Školní úvod: 1920 × 1080, 25 sn/s, délka 8 sekund. +- 8 sekund je 200 snímků. Titulek z Premiere trval 4, tady je čas i na pohyb. +- Projekt má příponu aep. Není to mp4. + +> Tip: Nová kompozice je Ctrl+N. Délku pište 0:00:08:00, ne 8 snímků. + +## Vrstvy +*Stejný princip* + +- Horní vrstva překrývá spodní, jako ve Photoshopu. +- Logo z AI vložte jako kompozici nebo jako vrstvy, ať jde každý tvar animovat zvlášť. +- PSD s vrstvami importujte jako kompozici, ne jako sloučený snímek. +- Vrstvy pojmenujte. Tvar 1 a Tvar 2 se při animaci pletou. + +> Tip: Když je logo jedna sloučená vrstva, jednotlivý paprsek nerozhýbáte. Importujte znovu s vrstvami. + +## Kotva +*Kolem čeho se to točí* + +- Kotva je bod, kolem kterého vrstva roste a otáčí se. +- U loga má sedět uprostřed značky, ne v rohu plátna. +- Nástroj kotva, nebo dvojklik na ikonu kotvy v panelu, ji srovná na střed vrstvy. +- Špatná kotva je nejčastější důvod, proč logo přijede z boku, i když jste chtěli zvětšení. + +> Tip: Vrstvu si najeďte na začátek času, srovnejte kotvu, a teprve potom dávejte klíčové snímky. + +## Zadání: prázdný úvod +*Cvičení* + +- Projekt uvod.aep, kompozice uvod, 1920 × 1080, 25 sn/s, 8 sekund. +- Importujte logo tak, aby tvary byly samostatné vrstvy, a pojmenujte je. +- Kotvu každého tvaru dejte na jeho střed. +- Zatím nic neanimujte. Uložte aep. Mp4 z After Effects ještě nedělejte. + +> Tip: Délku kompozice přečtete nahoře v časové ose. Když končí na 10 sekundách, zkraťte ji na 8. + +# Kvíz + +## Jak dlouhá je školní kompozice úvodu? +- [x] 8 sekund +- 45 sekund +- 4 snímky +- 303 mm +> Osm sekund stačí na nástup loga a textu. Delší úvod sežere krátké video. + +## Jaká je snímková frekvence? +- [x] 25 sn/s +- 300 ppi +- 72 sn/s vždy +- 1 sn/s +> Stejně jako Premiere, 25. Jinak se úvod ve filmu cuká nebo přepočítává. + +## Kam má sedět kotva loga? +- Do levého horního rohu plátna +- [x] Na střed značky +- Mimo kompozici +- Na stopu A1 +> Od kotvy se počítá zvětšení a otočení. V rohu plátna značka odjíždí. + +## Jak se má importovat logo z Illustratoru? +- Jako jeden JPG +- [x] Po vrstvách, ať jde každý tvar zvlášť +- Jako zvuk +- Sloučené a hned exportované +> Sloučená vrstva se hýbe jen celá. Tvary potřebují vlastní vrstvy. diff --git a/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.en.md b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.en.md new file mode 100644 index 0000000..8d9a26f --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Keyframes +popis: The logo arrives in one second, and the motion eases. +nazev_cviceni: Quiz: keyframes +nadpis_kvizu: Fill in the times +--- diff --git a/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.md b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.md new file mode 100644 index 0000000..c586b4b --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/04-klicove-snimky.md @@ -0,0 +1,79 @@ +--- +id: pg-4-klice +nazev: Klíčové snímky +popis: Nástup loga za jednu sekundu a zjemnění pohybu. +nazev_cviceni: Kvíz: klíčové snímky +nadpis_kvizu: Doplňte časy +--- + +# Výklad + +## Dva snímky +*Od a do* + +- Klíčový snímek je hodnota v čase: pozice, měřítko nebo krytí. +- Na čase 0 má logo měřítko 0 %. Na čase 1 sekunda má 100 %. +- Mezi těmito dvěma snímky After Effects pohyb dopočítá. +- Jeden snímek nic neanimuje. Bez druhého hodnoty není kam dojít. + +> Tip: Stopky vedle vlastnosti v časové ose založí snímek. Druhý vznikne, když v jiném čase změníte hodnotu. + +## Zjemnění +*F9* + +- Rovnoměrný pohyb vypadá strojově. Easy Ease, klávesa F9, začátek a konec zpomalí. +- F9 mačkejte, když jsou snímky vybrané. Jinak se nezmění nic. +- Na nástup loga Easy Ease patří. Na střih záběru v Premiere ne, to je jiný program. +- Krytí textu nechte běžet taky mezi dvěma snímky, ne skokem z 0 na 100. + +> Tip: Změněný snímek má po F9 jinou ikonu, přesýpací, ne kosočtverec. Podle toho poznáte, že zjemnění sedí. + +## Text +*1 + 4 + 1* + +- Text se objeví za 1 sekundu, zůstane 4 sekundy a zmizí za 1 sekundu. +- To je 6 sekund textu. Logo nastupuje v první sekundě, text může začít po ní. +- Celá kompozice má 8 sekund, takže na konci zůstanou 2 sekundy klidu nebo doběhu. +- Text pište v After Effects. Nevypalujte ho do PSD, pokud ho chcete ještě posunout. + +> Tip: Čtyři sekundy klidu jsou stejné jako titulek v Premiere. Divák čte stejně pomalu. + +## Zadání: nástup +*Cvičení* + +- Logo z měřítka 0 % na 100 % za první sekundu. Oba snímky s F9. +- Název Školní den: nástup 1 sekunda, klid 4 sekundy, odchod 1 sekunda. +- Kotvy jsou uprostřed. Logo neodjíždí z rohu. +- Uložte uvod.aep. Export mp4 až po kontrole přehrávání, délka pořád 8 sekund. + +> Tip: Mezerník přehrává. Když logo skočí, chybí druhý snímek nebo je na stejném čase jako první. + +# Kvíz + +## Jaké měřítko má logo v čase 0 a v čase 1 sekunda? +- 100 % a 100 % +- [x] 0 % a 100 % +- 50 % a 50 % +- 300 ppi +> Z nuly na plnou velikost za sekundu. Bez dvou snímků se nic nepohne. + +## Čím se nástup zjemní? +- [x] F9, Easy Ease +- Ctrl+J +- Sytostí 0 +- Spadávkou +> F9 na vybraných snímcích zpomalí rozjezd a dojezd. Pohyb přestane být pravítko. + +## Jak je rozložený text? +- [x] 1 s nástup, 4 s klid, 1 s odchod +- 12 snímků celkem +- 8 s nástup a hned pryč +- Jen vypálený v JPG +> Jedna sekunda se objeví, čtyři se čte, jedna zmizí. Dohromady šest sekund. + +## Proč logo odjíždí z rohu, i když měníte jen měřítko? +- Sekvence je 30 sn/s +- [x] Kotva není uprostřed značky +- Chybí CMYK +- F9 je zapnuté +> Měřítko roste od kotvy. Kotva v rohu plátna žene značku přes obraz. diff --git a/content/prezentacni-grafika/4-rocnik/05-indesign.en.md b/content/prezentacni-grafika/4-rocnik/05-indesign.en.md new file mode 100644 index 0000000..1eb4419 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/05-indesign.en.md @@ -0,0 +1,6 @@ +--- +nazev: InDesign: a page +popis: A two-page A4 leaflet, a master and a placed image. +nazev_cviceni: Quiz: InDesign +nadpis_kvizu: Recognise the master and the link +--- diff --git a/content/prezentacni-grafika/4-rocnik/05-indesign.md b/content/prezentacni-grafika/4-rocnik/05-indesign.md new file mode 100644 index 0000000..1c2d5a1 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/05-indesign.md @@ -0,0 +1,79 @@ +--- +id: pg-4-indesign +nazev: InDesign: stránka +popis: Dvoustránkový leták A4, předloha a vložený obraz. +nazev_cviceni: Kvíz: InDesign +nadpis_kvizu: Poznejte předlohu a odkaz +--- + +# Výklad + +## Stránka, ne plátno +*A4* + +- InDesign skládá stránku: text, obraz a logo v rámcích. +- Leták je A4, 210 × 297 mm. Spadávka 3 mm se nastaví u dokumentu, stránka zůstane A4. +- Na rozdíl od Photoshopu se tu plátno nezvětšuje na 216 × 303. Přesah je spadávka mimo stránku. +- Dvě strany: přední s názvem akce, zadní s krátkým programem dne. + +> Tip: Nový dokument: záměr Tisk, A4, dvě stránky, spadávka 3 mm všude. + +## Rámce +*Text a obraz* + +- Text žije v textovém rámci. Obraz v rámci obrazu. Nejsou to vrstvy Photoshopu. +- Obraz se vkládá Soubor > Umístit. Nevkládejte ho přes schránku jako snímek obrazovky. +- Umístěný PSD nebo JPG je odkaz. Když opravíte PSD a uložíte, InDesign ho umí aktualizovat. +- Vložený obraz, který v souboru uvízne bez odkazu, se po opravě fotky sám nezmění. + +> Tip: Panel Odkazy ukáže, jestli je soubor aktuální, nebo chybí. Chybějící odkaz je červeně. + +## Předloha +*Číslo strany* + +- Předloha, master, je stránka pod ostatními. Co je na ní, opakuje se. +- Na předlohu dejte malé logo a číslo strany. Na běžnou stránku už jen obsah. +- Prvek z předlohy na jedné straně odemknete Ctrl+Shift+klik, když ho máte výjimečně posunout. +- Číslo strany je znak čísla stránky, ne ručně napsaná jednička. Druhá strana se pak očísluje sama. + +> Tip: Předloha je nahoře v panelu Stránky. Dvojklik ji otevře. Dvojklik na stránku 1 vás vrátí do dokumentu. + +## Zadání: dvě strany +*Cvičení* + +- letak.indd, A4, dvě strany, spadávka 3 mm, okraje aspoň 12 mm. +- Předloha: logo a automatické číslo strany. +- Strana 1: název Školní den, datum 12. 6., umístěná fotka jako odkaz. +- Strana 2: tři řádky programu. Uložte INDD a export nechte na pozdější lekci, nebo udělejte náhled PDF. + +> Tip: Fotku nejdřív upravte ve Photoshopu a uložte PSD. Do InDesignu ji umístěte, nekopírujte ji znovu. + +# Kvíz + +## Jak je v InDesignu nastavená spadávka? +- Stránka se zvětší na 303 × 426 mm +- [x] Stránka zůstane A4 a spadávka 3 mm je navíc +- Spadávka se v sazbě nedělá +- 72 ppi +> A4 je čistý format. 3 mm přesah je vlastnost dokumentu, ne jiný rozměr stránky. + +## Jak se má dostat fotka do letáku? +- Snímkem obrazovky +- [x] Soubor > Umístit, jako odkaz +- Vypálením do předlohy jednou provždy +- Jako mp4 +> Odkaz jde po opravě PSD aktualizovat. Snímek obrazovky je druhá, horší fotka. + +## Co patří na předlohu? +- Celý program dne, na každé straně jiný +- [x] Logo a automatické číslo strany +- Časová osa Premiere +- Maska z Photoshopu +> Předloha je to, co se opakuje. Program dne je obsah konkrétní strany. + +## Proč se číslo strany nepíše ručně jako 1? +- InDesign čísla neumí +- [x] Znak čísla stránky se na druhé straně změní sám +- Ruční jednička je vektor +- Kvůli 25 sn/s +> Znak čísla stránky čte, na které straně je. Ruční 1 zůstane 1 i na straně 2. diff --git a/content/prezentacni-grafika/4-rocnik/06-propojeni.en.md b/content/prezentacni-grafika/4-rocnik/06-propojeni.en.md new file mode 100644 index 0000000..362f549 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/06-propojeni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Linking the programs +popis: The Illustrator logo in After Effects, and the opener in Premiere without a middle export. +nazev_cviceni: Quiz: linking +nadpis_kvizu: Pick what updates +--- diff --git a/content/prezentacni-grafika/4-rocnik/06-propojeni.md b/content/prezentacni-grafika/4-rocnik/06-propojeni.md new file mode 100644 index 0000000..0c545f2 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/06-propojeni.md @@ -0,0 +1,79 @@ +--- +id: pg-4-propojeni +nazev: Propojení programů +popis: Logo z Illustratoru v After Effects a úvod v Premiere bez mezivýstupu. +nazev_cviceni: Kvíz: propojení +nadpis_kvizu: Vyberte, co se aktualizuje +--- + +# Výklad + +## Jeden zdroj +*Odkaz* + +- Logo má jeden zdroj, logo.ai. Photoshop, After Effects i InDesign ať na něj odkazují. +- Když značku přebarvíte v AI a uložíte, odkazy ji mohou načíst znovu. Kopírování JPG to neumí. +- JPG 80 px vložené do čtyř programů jsou čtyři různé značky. Jedna se opraví, tři zůstanou staré. +- Pojmenujte zdroj jednou. logo-final-final2.ai je cesta, jak odevzdat špatnou verzi. + +> Tip: V After Effects se odkaz obnoví v panelu projektu. V InDesignu v panelu Odkazy. + +## Dynamic Link +*AE do Premiere* + +- Dynamic Link vloží kompozici After Effects na časovou osu Premiere bez mezivýstupu mp4. +- Když v úvodu posunete text, Premiere po uložení aep ukáže novou verzi. +- Oba programy mají být stejná generace. Starší Premiere nový aep často neotevře. +- Soubory aep a prproj mějte ve stejné složce. Odkaz jinak spadne při přesunu jen jednoho z nich. + +> Tip: V Premiere: Soubor > Adobe Dynamic Link > Importovat kompozici After Effects. Na osu přijde jako záběr. + +## Co nepropojovat +*Kopie* + +- Zvuk do Illustratoru nepatří. Video do InDesignu taky ne, kromě náhledu, který tu neděláme. +- Plakát v PSD zůstává tisk. Do videa z něj berte jen logo nebo krátký náhled, ne celou spadávku se značkami. +- Ořezové značky plakátu ve videu jsou chyba. Ty má jen tiskové PDF. +- Když Dynamic Link nejde, exportujte úvod jako mp4 v ProRes nebo aspoň v kvalitním H.264 a ten vložte. Je to kopie, už se sama neopraví. + +> Tip: ProRes je mezisoubor, velký a kvalitní. H.264 je menší a už jednou stlačený. Do dalšího exportu je horší. + +## Zadání: úvod na ose +*Cvičení* + +- V Premiere na začátek sekvence vložte kompozici uvod z After Effects přes Dynamic Link. +- Délka ať sedí: 8 sekund úvodu, pak pokračuje sestřih. +- Změňte v AI jednu barvu loga, uložte, a obnovte odkaz až do videa. Když se barva nezmění, máte někde JPG. +- Uložte prproj i aep. Mp4 celého filmu exportujte znovu až na konci, ne po každém kroku. + +> Tip: Když se úvod na ose tváří jako offline, chybí aep nebo je jiná verze programu. Neexportujte přes to černou. + +# Kvíz + +## Kde má žít značka, aby šla opravit všude? +- [x] V jednom logo.ai +- Ve čtyřech různých JPG +- Jen vypálená ve videu +- V titulku Premiere jako jediném místě +> Jeden zdroj. Ostatní programy na něj odkazují. + +## Co dělá Dynamic Link? +- Převede plakát do CMYK +- [x] Vloží kompozici After Effects do Premiere bez mezivýstupu +- Smaže vrstvy v PSD +- Nahradí InDesign +> Úprava v aep se po uložení ukáže na ose. Mp4 mezi tím není potřeba. + +## Co do videa z plakátu nepatří? +- Logo +- [x] Ořezové značky +- Název akce +- Barva značky +> Značky jsou pro tiskárnu. Ve snímku videa vypadají jako omylem vyfocený tiskový arch. + +## Kdy je mp4 z After Effects horší než Dynamic Link? +- [x] Když chcete, aby se oprava textu sama propsala do Premiere +- Když video má 25 sn/s +- Když je úvod dlouhý 8 sekund +- Mp4 je vždycky zdroj +> Mp4 je kopie. Další oprava textu už do něj sama nevleze, musíte exportovat znovu. diff --git a/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.en.md b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.en.md new file mode 100644 index 0000000..af63637 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.en.md @@ -0,0 +1,6 @@ +--- +nazev: Export and formats +popis: What goes to the printer, to the web and out as video. +nazev_cviceni: Quiz: formats +nadpis_kvizu: Match the file to the job +--- diff --git a/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.md b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.md new file mode 100644 index 0000000..d98d04a --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/07-export-a-formaty.md @@ -0,0 +1,79 @@ +--- +id: pg-4-export +nazev: Export a formáty +popis: Co se odevzdává tiskárně, na web a jako video. +nazev_cviceni: Kvíz: formáty +nadpis_kvizu: Přiřaďte soubor k použití +--- + +# Výklad + +## Tabulka výstupů +*Kam co* + +- Tiskárna dostane PDF. Plakát se spadávkou, leták A4 se spadávkou 3 mm. +- Web dostane JPG nebo PNG. Logo navíc SVG. +- Video dostane MP4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s. +- Pracovní soubory PSD, AI, AEP, PRPROJ a INDD se odevzdávají do složky zdroj, ne místo výstupů. + +> Tip: Jeden formát nespasí všechno. SVG na projektor s videem nehraje a MP4 tiskárna nevytiskne. + +## Jména +*Verze* + +- Název ať říká, co soubor je: plakat-tisk.pdf, plakat-web.jpg, skolni-den.mp4, logo.svg. +- Datum verze pište jako 2026-06-12, rok-měsíc-den. Seřadí se samo. +- Nepoužívejte final, final2 a oprava. Nová verze má nové datum, stará zůstane ve složce stare. +- Mezery a háčky v názvech na Windows i Macu projdou, ale odkaz mezi programy občas spadne. Bez háčků je jistější. + +> Tip: skolni-den_2026-06-12.mp4 je jasnější než video1.mp4. + +## Složka odevzdání +*Dvě hromádky* + +- Složka zdroj: ai, psd, aep, prproj, indd a použitá média. +- Složka export: pdf, jpg, png, svg, mp4. +- Média videa leží tam, kam vede odkaz projektu. Kopie na ploše se nepočítá. +- Když učitel otevře jen složku export, musí vidět plakát, leták, logo i video bez Adobe. + +> Tip: INDD bez odkazovaných fotek otevře prázdné rámečky. Fotky patří k němu, nebo se před odevzdáním zabalí. + +## Zadání: srovnat složku +*Cvičení* + +- Založte složku skolni-den se složkami zdroj a export. +- Do export dejte aspoň logo.svg, plakat-tisk.pdf, plakat-web.jpg a skolni-den.mp4. +- Do zdroj dejte odpovídající ai, psd, aep a prproj. +- Cizí soubor Bez názvu-1 smažte nebo přejmenujte. Ve složce ať není nic, co neumíte vysvětlit. + +> Tip: InDesign umí Soubor > Sbalit. Vznikne složka s INDD i s obrázky. Tu dejte do zdroj. + +# Kvíz + +## Co dostane tiskárna? +- prproj +- [x] PDF +- AEP +- Jen SVG na plakát A3 +> Tisk je PDF se spadávkou. Projekt Premiere tiskárna neotevře. + +## Který soubor loga patří na web a zůstane ostrý? +- logo.jpg o šířce 80 px +- [x] logo.svg +- skolni-den.mp4 +- plakat-tisk.pdf jako ikona +> SVG je křivka. Malé JPG se na webu rozmaže, jakmile ho stránka zvětší. + +## Jak se má jmenovat nová verze videa z 12. 6. 2026? +- final2.mp4 +- [x] skolni-den_2026-06-12.mp4 +- oprava oprava.mp4 +- DSC_1842.mp4 +> Datum rok-měsíc-den se seřadí a je vidět, která verze je nová. + +## Co musí být ve složce export, aby to otevřel člověk bez Adobe? +- Jen AEP a PRPROJ +- [x] PDF, JPG nebo PNG, SVG a MP4 +- Jen PSD +- Jen INDD +> Export je plochý výstup. Pracovní soubory Adobe bez programu nejdou přehrát ani otevřít naplno. diff --git a/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.en.md b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.en.md new file mode 100644 index 0000000..93917ab --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exam assignment +popis: A Školní den campaign: a mark, a poster, a leaflet and a video. +nazev_cviceni: Quiz: the campaign +nadpis_kvizu: Walk through the whole hand-in +--- diff --git a/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.md b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.md new file mode 100644 index 0000000..0dd6875 --- /dev/null +++ b/content/prezentacni-grafika/4-rocnik/08-maturitni-zadani.md @@ -0,0 +1,79 @@ +--- +id: pg-4-maturita +nazev: Maturitní zadání +popis: Kampaň Školní den: značka, plakát, leták a video. +nazev_cviceni: Kvíz: kampaň +nadpis_kvizu: Projdi celé odevzdání +--- + +# Výklad + +## Značka +*Illustrator* + +- Značka ze tří tvarů a názvu. Plná verze a černá verze, obě v logo.ai. +- Export logo.svg a logo.pdf. Černá verze jako logo-cerna.svg. +- Na 16 px musí být značka pořád čitelná. Když není, uberete detail. +- JPG není zdroj. Smí být jen náhled 1600 px. + +> Tip: Jedna značka na plakátu, v letáku i ve videu. Tři různé značky jsou tři práce, ne kampaň. + +## Tisk +*Plakát a leták* + +- Plakát A3 na výšku, plátno 303 × 426 mm, 300 ppi, spadávka 3 mm, text mimo ořez. +- Na plakátu je 12. 6. a tělocvična, fotka, maska, křivky a skupiny foto, grafika, text. +- Leták InDesign, A4, dvě strany, spadávka 3 mm, předloha s číslem strany, fotka jako odkaz. +- Výstupy: plakat-tisk.pdf se značkami, plakat-web.jpg 1600 px, letak.pdf. + +> Tip: Stejný název akce, stejné datum. Plakát a leták se nesmí rozejít. + +## Video +*Premiere a After Effects* + +- Úvod 8 sekund z After Effects: logo z 0 % na 100 % za 1 sekundu, text 1 + 4 + 1, snímky s F9. +- Úvod je v Premiere přes Dynamic Link, nebo jako jednou exportovaná kopie, když Link nejde. +- Celé video 40 až 60 sekund, titulky, jeden J-cut, hudba −15 a pod řečí −24 dB, řeč kolem −6 dB. +- Export skolni-den.mp4, H.264, 1920 × 1080, 25 sn/s, 10 Mb/s. + +> Tip: Na začátku mp4 hraje úvod, ne černá a ne ořezové značky z plakátu. + +## Složka +*Obhajoba* + +- skolni-den/zdroj drží ai, psd, aep, prproj, indd a média. +- skolni-den/export drží svg, pdf, jpg a mp4. Otevře je i člověk bez Adobe. +- Na obhajobu ukážete nejdřív video, pak plakát, pak jeden pracovní soubor s vrstvami. +- Řeknete, co je bitmapa a co vektor a proč je spadávka 3 mm. To je obsah čtyř ročníků. + +> Tip: Když se zeptají, proč není všechno v jednom JPG, odpověď je: tisk, web a video chtějí každý svůj soubor. + +# Kvíz + +## Co musí mít značka? +- [x] Tři tvary, plnou a černou verzi, SVG a PDF +- Jen JPG 80 px +- Jen titulek v Premiere +- Čtyři různé barvy na každém materiálu +> Zdroj je AI. SVG a PDF jsou výstupy. Černá verze je pro jednu barvu. + +## Jaké je plátno plakátu? +- A4 bez spadávky +- [x] 303 × 426 mm při 300 ppi +- 1920 × 1080 jako jediný tiskový rozměr +- 8 sekund +> A3 plus 3 mm z každé strany. 300 ppi. Video je zvlášť, ve Full HD. + +## Jak je dlouhý úvod z After Effects? +- [x] 8 sekund, logo nastoupí za 1 sekundu +- 45 sekund +- 12 snímků celé +- 4 minuty +> Kompozice má 8 sekund. Měřítko loga jde z 0 % na 100 % během první z nich. + +## Co leží ve složce export? +- Jen prproj a aep +- [x] SVG, PDF, JPG a MP4 +- Jen PSD s vrstvami +- Rozpracované bez názvu +> Export otevře i člověk bez Adobe. Pracovní soubory jsou ve složce zdroj. diff --git a/content/programovani/1-rocnik/01-ahoj-svete.en.md b/content/programovani/1-rocnik/01-ahoj-svete.en.md new file mode 100644 index 0000000..ee64874 --- /dev/null +++ b/content/programovani/1-rocnik/01-ahoj-svete.en.md @@ -0,0 +1,4 @@ +--- +nazev: Hello, world +popis: The first program: writing to the console. +--- diff --git a/content/programovani/1-rocnik/01-ahoj-svete.md b/content/programovani/1-rocnik/01-ahoj-svete.md new file mode 100644 index 0000000..c150fe8 --- /dev/null +++ b/content/programovani/1-rocnik/01-ahoj-svete.md @@ -0,0 +1,42 @@ +--- +id: cs-1-ahoj-svete +nazev: Ahoj světe +popis: První program: výpis na konzoli. +--- + +# Výklad + +## Výpis +*WriteLine* + +- Console.WriteLine vypíše text a odřádkuje. +- Console.Write vypíše text a zůstane na stejném řádku. +- Text se píše do uvozovek. Středník ukončuje příkaz. + +> Tip: Bez středníku se program nepřeloží. + +## Krátký a dlouhý zápis +*Main* + +- Starší učebnice mají třídu Program a metodu Main. +- Nový projekt stejný program zapíše několika řádky bez třídy. +- Obojí spustí stejný výpis. V lekcích používáme krátký zápis. + +> Tip: Krátký zápis je pořád C#. Není to jiný jazyk. + +## Vyzkoušejte +*Editor* + +- Vpravo je kód. Upravte ho a stiskněte Spustit. +- Výstup se objeví hned vedle. +- Obnovit ukázku vrátí původní program. + +> Tip: Změňte text v uvozovkách a spusťte znovu. + +# Kód + +``` +Console.WriteLine("Ahoj světe"); +Console.Write("Toto je "); +Console.WriteLine("C#"); +``` diff --git a/content/programovani/1-rocnik/02-komentare.en.md b/content/programovani/1-rocnik/02-komentare.en.md new file mode 100644 index 0000000..c80ebee --- /dev/null +++ b/content/programovani/1-rocnik/02-komentare.en.md @@ -0,0 +1,4 @@ +--- +nazev: Comments +popis: Notes the program does not run. +--- diff --git a/content/programovani/1-rocnik/02-komentare.md b/content/programovani/1-rocnik/02-komentare.md new file mode 100644 index 0000000..a54c0b2 --- /dev/null +++ b/content/programovani/1-rocnik/02-komentare.md @@ -0,0 +1,35 @@ +--- +id: cs-1-komentare +nazev: Komentáře +popis: Poznámky, které se nespouštějí. +--- + +# Výklad + +## Jednořádkový +*//* + +- Vše za // na stejném řádku počítač přeskočí. +- Komentář vysvětluje, proč tam řádek je, ne co je vidět. +- Kód bez komentáře pořád běží stejně. + +> Tip: Komentář není výpis. Na konzoli se neobjeví. + +## Více řádků +*/* */* + +- Blok začíná /* a končí */. +- Hodí se, když chcete dočasně vypnout několik řádků. +- Zapomenuté */ rozbije zbytek souboru. + +> Tip: Nevnořujte blokové komentáře do sebe. + +# Kód + +``` +// Tohle se nespustí. +Console.WriteLine("Tento řádek ano"); +/* +Console.WriteLine("A tento ne"); +*/ +``` diff --git a/content/programovani/1-rocnik/03-promenne.en.md b/content/programovani/1-rocnik/03-promenne.en.md new file mode 100644 index 0000000..e302009 --- /dev/null +++ b/content/programovani/1-rocnik/03-promenne.en.md @@ -0,0 +1,4 @@ +--- +nazev: Variables +popis: A named value you can change. +--- diff --git a/content/programovani/1-rocnik/03-promenne.md b/content/programovani/1-rocnik/03-promenne.md new file mode 100644 index 0000000..c17316f --- /dev/null +++ b/content/programovani/1-rocnik/03-promenne.md @@ -0,0 +1,36 @@ +--- +id: cs-1-promenne +nazev: Proměnné +popis: Pojmenovaná hodnota, kterou lze změnit. +--- + +# Výklad + +## Deklarace +*typ jméno* + +- Nejdřív typ, pak jméno, pak hodnota: int vek = 17; +- Jméno začíná písmenem. Mezery v něm být nesmí. +- Do stejné proměnné lze později uložit novou hodnotu stejného typu. + +> Tip: int drží celé číslo. string drží text. + +## Čtení +*jméno* + +- Jméno bez uvozovek je proměnná. Uvozovky by z něj udělaly text. +- WriteLine umí vypsat proměnnou i text spojený plusem. +- Hodnota se dosadí až při běhu, ne při psaní. + +> Tip: Změňte vek na 18 a spusťte znovu. + +# Kód + +``` +string jmeno = "Anna"; +int vek = 17; +jmeno = "Eva"; +Console.WriteLine(jmeno); +Console.WriteLine(vek); +Console.WriteLine(jmeno + " má " + vek + " let"); +``` diff --git a/content/programovani/1-rocnik/04-typy.en.md b/content/programovani/1-rocnik/04-typy.en.md new file mode 100644 index 0000000..0973087 --- /dev/null +++ b/content/programovani/1-rocnik/04-typy.en.md @@ -0,0 +1,4 @@ +--- +nazev: Data types +popis: int, double, bool, char and string. +--- diff --git a/content/programovani/1-rocnik/04-typy.md b/content/programovani/1-rocnik/04-typy.md new file mode 100644 index 0000000..bda4cec --- /dev/null +++ b/content/programovani/1-rocnik/04-typy.md @@ -0,0 +1,40 @@ +--- +id: cs-1-typy +nazev: Datové typy +popis: int, double, bool, char a string. +--- + +# Výklad + +## Čísla +*int a double* + +- int je celé číslo: 42. +- double je číslo s desetinnou tečkou: 3.14. +- V kódu je desetinná tečka, ne čárka. + +> Tip: 3,14 by C# přečetl jako dva údaje oddělené čárkou. + +## Text a ano/ne +*string, char, bool* + +- string je text v uvozovkách. +- char je jeden znak v apostrofech: 'A'. +- bool je jen true nebo false. + +> Tip: Apostrof a uvozovky nejsou totéž. 'A' je znak, "A" je text. + +# Kód + +``` +int cele = 42; +double desetinne = 3.14; +bool prsi = false; +char pismeno = 'A'; +string text = "ahoj"; +Console.WriteLine(cele); +Console.WriteLine(desetinne); +Console.WriteLine(prsi); +Console.WriteLine(pismeno); +Console.WriteLine(text); +``` diff --git a/content/programovani/1-rocnik/05-prevody.en.md b/content/programovani/1-rocnik/05-prevody.en.md new file mode 100644 index 0000000..291c616 --- /dev/null +++ b/content/programovani/1-rocnik/05-prevody.en.md @@ -0,0 +1,4 @@ +--- +nazev: Type conversion +popis: From double to int and from text to a number. +--- diff --git a/content/programovani/1-rocnik/05-prevody.md b/content/programovani/1-rocnik/05-prevody.md new file mode 100644 index 0000000..3dd2e2a --- /dev/null +++ b/content/programovani/1-rocnik/05-prevody.md @@ -0,0 +1,36 @@ +--- +id: cs-1-prevody +nazev: Převody typů +popis: Z double na int a z textu na číslo. +--- + +# Výklad + +## Ořez desetin +*(int)* + +- (int)3.9 je 3. Desetinná část se odřízne, nezaokrouhlí. +- Opačně int do double ztrátu nemá: 3 se stane 3.0. +- Převod napište před hodnotu do závorky. + +> Tip: Když potřebujete zaokrouhlit, (int) na to není. + +## Text na číslo +*Parse* + +- int.Parse("20") udělá z textu číslo 20. +- Když text číslo není, program spadne. To později chytíme výjimkou. +- Číslo na text udělá ToString nebo zápis do řetězce. + +> Tip: Vstup z konzole je vždy text, i když uživatel napíše číslici. + +# Kód + +``` +double cena = 19.9; +int cele = (int)cena; +Console.WriteLine(cele); +string vstup = "20"; +int cislo = int.Parse(vstup); +Console.WriteLine(cislo + 1); +``` diff --git a/content/programovani/1-rocnik/06-operatory.en.md b/content/programovani/1-rocnik/06-operatory.en.md new file mode 100644 index 0000000..e4c6901 --- /dev/null +++ b/content/programovani/1-rocnik/06-operatory.en.md @@ -0,0 +1,4 @@ +--- +nazev: Operators +popis: Arithmetic, remainder, comparison and logic. +--- diff --git a/content/programovani/1-rocnik/06-operatory.md b/content/programovani/1-rocnik/06-operatory.md new file mode 100644 index 0000000..b7c962a --- /dev/null +++ b/content/programovani/1-rocnik/06-operatory.md @@ -0,0 +1,37 @@ +--- +id: cs-1-operatory +nazev: Operátory +popis: Počítání, zbytek, porovnání a a zároveň. +--- + +# Výklad + +## Počet +*+ - * / %* + +- Sčítání, odčítání a násobení fungují, jak čekáte. +- Dělení dvou int je celočíselné: 17 / 5 je 3. +- % je zbytek po dělení: 17 % 5 je 2. + +> Tip: Když chcete desetinný podíl, jedno číslo udělejte double: 17 / 5.0. + +## Porovnání +*true a false* + +- == je rovnost. = je uložení. Nejsou totéž. +- && znamená a zároveň. || znamená nebo. +- ! otočí bool: !true je false. + +> Tip: Jedno = v podmínce je častá chyba. Podmínka chce ==. + +# Kód + +``` +int a = 17; +int b = 5; +Console.WriteLine(a + b); +Console.WriteLine(a / b); +Console.WriteLine(a % b); +Console.WriteLine(a > b); +Console.WriteLine(a > 10 && b < 10); +``` diff --git a/content/programovani/1-rocnik/07-retezce.en.md b/content/programovani/1-rocnik/07-retezce.en.md new file mode 100644 index 0000000..7229ab2 --- /dev/null +++ b/content/programovani/1-rocnik/07-retezce.en.md @@ -0,0 +1,4 @@ +--- +nazev: Strings +popis: Joining text, length and a slice. +--- diff --git a/content/programovani/1-rocnik/07-retezce.md b/content/programovani/1-rocnik/07-retezce.md new file mode 100644 index 0000000..044c950 --- /dev/null +++ b/content/programovani/1-rocnik/07-retezce.md @@ -0,0 +1,36 @@ +--- +id: cs-1-retezce +nazev: Řetězce +popis: Skládání textu, délka a výřez. +--- + +# Výklad + +## Interpolace +*$* + +- $"Ahoj, {jmeno}" dosadí hodnotu do složených závorek. +- Je přehlednější než spojování plusem. +- Do závorek může i výpočet: {vek + 1}. + +> Tip: Bez dolaru před uvozovkami se {jmeno} vypíše doslova. + +## Metody textu +*Length* + +- Length je počet znaků. +- ToUpper vrátí velká písmena a původní text nezmění. +- Substring(0, 2) vezme dva znaky od začátku. + +> Tip: První znak má index 0, ne 1. + +# Kód + +``` +string jmeno = "Anna"; +int vek = 17; +Console.WriteLine($"Ahoj, {jmeno}. Za rok ti bude {vek + 1}."); +Console.WriteLine(jmeno.Length); +Console.WriteLine(jmeno.ToUpper()); +Console.WriteLine(jmeno.Substring(0, 2)); +``` diff --git a/content/programovani/1-rocnik/08-vstup.en.md b/content/programovani/1-rocnik/08-vstup.en.md new file mode 100644 index 0000000..aaa9f2d --- /dev/null +++ b/content/programovani/1-rocnik/08-vstup.en.md @@ -0,0 +1,4 @@ +--- +nazev: Console input +popis: Console.ReadLine reads a line. +--- diff --git a/content/programovani/1-rocnik/08-vstup.md b/content/programovani/1-rocnik/08-vstup.md new file mode 100644 index 0000000..ea4349c --- /dev/null +++ b/content/programovani/1-rocnik/08-vstup.md @@ -0,0 +1,34 @@ +--- +id: cs-1-vstup +nazev: Vstup z konzole +popis: Console.ReadLine přečte řádek. +--- + +# Výklad + +## ReadLine +*text* + +- ReadLine počká na řádek a vrátí ho jako string. +- V této lekci řádek napište do pole Vstup a pak Spustit. +- Každé další ReadLine vezme další řádek vstupu. + +> Tip: Když vstup necháte prázdný, ReadLine dostane konec a program může spadnout. + +## Číslo ze vstupu +*Parse* + +- ReadLine nikdy nevrací int. Vrací text. +- int.Parse z něj číslo udělá. +- Napište do vstupu třeba 17. + +> Tip: Zkuste ve vstupu Anna a uvidíte chybu. Parse čeká číslo. + +# Kód + +``` +Console.WriteLine("Kolik ti je?"); +string radek = Console.ReadLine(); +int vek = int.Parse(radek); +Console.WriteLine($"Za rok ti bude {vek + 1}."); +``` diff --git a/content/programovani/2-rocnik/01-if.en.md b/content/programovani/2-rocnik/01-if.en.md new file mode 100644 index 0000000..177d905 --- /dev/null +++ b/content/programovani/2-rocnik/01-if.en.md @@ -0,0 +1,4 @@ +--- +nazev: If and else +popis: A branch that runs only sometimes. +--- diff --git a/content/programovani/2-rocnik/01-if.md b/content/programovani/2-rocnik/01-if.md new file mode 100644 index 0000000..c235df4 --- /dev/null +++ b/content/programovani/2-rocnik/01-if.md @@ -0,0 +1,43 @@ +--- +id: cs-2-if +nazev: If a else +popis: Větev, která se spustí jen někdy. +--- + +# Výklad + +## Podmínka +*if* + +- if (vek >= 18) spustí blok ve složených závorkách, jen když je podmínka true. +- else je větev pro opačný případ. +- else if přidá další podmínku mezi ně. + +> Tip: Za if není středník. Středník by podmínku oddělil od bloku. + +## Porovnání +*==* + +- Čísla porovnávejte >, <, >=, <=, == a !=. +- Text se porovnává ==. "Anna" == "anna" je false. +- Do podmínky patří bool, ne přiřazení. + +> Tip: Změňte vek a sledujte, která věta se vypíše. + +# Kód + +``` +int vek = 17; +if (vek >= 18) +{ + Console.WriteLine("plnoletý"); +} +else if (vek >= 15) +{ + Console.WriteLine("téměř"); +} +else +{ + Console.WriteLine("ještě ne"); +} +``` diff --git a/content/programovani/2-rocnik/02-switch.en.md b/content/programovani/2-rocnik/02-switch.en.md new file mode 100644 index 0000000..54377bb --- /dev/null +++ b/content/programovani/2-rocnik/02-switch.en.md @@ -0,0 +1,4 @@ +--- +nazev: Switch +popis: Several branches for one value. +--- diff --git a/content/programovani/2-rocnik/02-switch.md b/content/programovani/2-rocnik/02-switch.md new file mode 100644 index 0000000..d46a17c --- /dev/null +++ b/content/programovani/2-rocnik/02-switch.md @@ -0,0 +1,43 @@ +--- +id: cs-2-switch +nazev: Switch +popis: Víc větví podle jedné hodnoty. +--- + +# Výklad + +## Větve +*case* + +- switch vybere case, který se rovná hodnotě. +- break ukončí větev. Bez něj se překlad v klasickém zápisu zastaví. +- default je větev, když nesedí žádný case. + +> Tip: Na známku 1 až 5 se switch hodí víc než řada else if. + +## Nový zápis +*šipka* + +- case 1 => "výborně" je krátká větev bez break. +- Výraz switch umí hodnotu rovnou vrátit. +- V lekci je klasický zápis, ať je vidět break. + +> Tip: Přidejte case 5 a spusťte se znamkou 5. + +# Kód + +``` +int znamka = 2; +switch (znamka) +{ + case 1: + Console.WriteLine("výborně"); + break; + case 2: + Console.WriteLine("chvalitebně"); + break; + default: + Console.WriteLine("jiná známka"); + break; +} +``` diff --git a/content/programovani/2-rocnik/03-while.en.md b/content/programovani/2-rocnik/03-while.en.md new file mode 100644 index 0000000..cc1f71b --- /dev/null +++ b/content/programovani/2-rocnik/03-while.en.md @@ -0,0 +1,4 @@ +--- +nazev: While +popis: Repeat while a condition holds. +--- diff --git a/content/programovani/2-rocnik/03-while.md b/content/programovani/2-rocnik/03-while.md new file mode 100644 index 0000000..37455ca --- /dev/null +++ b/content/programovani/2-rocnik/03-while.md @@ -0,0 +1,38 @@ +--- +id: cs-2-while +nazev: While +popis: Opakování, dokud podmínka platí. +--- + +# Výklad + +## Smyčka +*while* + +- Tělo se opakuje, dokud je podmínka true. +- Když podmínku nikdy nezměníte, smyčka neskončí. +- Aplikace dlouhý běh po chvíli zastaví. + +> Tip: i = i + 1 nebo i++ posune čítač. Bez toho se i nemění. + +## Součet +*1 až 4* + +- Začněte součtem 0 a přičítejte i. +- 1+2+3+4 je 10. +- Podmínka i <= 4 pustí i číslo 4 dovnitř. + +> Tip: Změňte 4 na 10. Součet 1 až 10 je 55. + +# Kód + +``` +int i = 1; +int soucet = 0; +while (i <= 4) +{ + soucet = soucet + i; + i++; +} +Console.WriteLine(soucet); +``` diff --git a/content/programovani/2-rocnik/04-for.en.md b/content/programovani/2-rocnik/04-for.en.md new file mode 100644 index 0000000..757f660 --- /dev/null +++ b/content/programovani/2-rocnik/04-for.en.md @@ -0,0 +1,4 @@ +--- +nazev: For +popis: A loop with its counter in one place. +--- diff --git a/content/programovani/2-rocnik/04-for.md b/content/programovani/2-rocnik/04-for.md new file mode 100644 index 0000000..2e6afac --- /dev/null +++ b/content/programovani/2-rocnik/04-for.md @@ -0,0 +1,34 @@ +--- +id: cs-2-for +nazev: For +popis: Smyčka s čítačem na jednom místě. +--- + +# Výklad + +## Tři části +*start; podmínka; krok* + +- for (int i = 0; i < 3; i++) má začátek, podmínku a krok. +- i < 3 pustí 0, 1 a 2. Trojku už ne. +- Krok i++ se provede až po těle. + +> Tip: Když potřebujete přesný počet opakování, for je přehlednější než while. + +## Násobilka +*výpis* + +- V těle můžete počítat i * 2. +- WriteLine v těle vypíše jeden řádek na každé opakování. +- Čítač nemusí začínat nulou. + +> Tip: Přepište podmínku na i <= 5 a sledujte další řádek. + +# Kód + +``` +for (int i = 1; i <= 3; i++) +{ + Console.WriteLine($"{i} krát 2 je {i * 2}"); +} +``` diff --git a/content/programovani/2-rocnik/05-pole.en.md b/content/programovani/2-rocnik/05-pole.en.md new file mode 100644 index 0000000..a4664a1 --- /dev/null +++ b/content/programovani/2-rocnik/05-pole.en.md @@ -0,0 +1,4 @@ +--- +nazev: Arrays +popis: Several values of one type under one name. +--- diff --git a/content/programovani/2-rocnik/05-pole.md b/content/programovani/2-rocnik/05-pole.md new file mode 100644 index 0000000..4653b09 --- /dev/null +++ b/content/programovani/2-rocnik/05-pole.md @@ -0,0 +1,35 @@ +--- +id: cs-2-pole +nazev: Pole +popis: Víc hodnot stejného typu pod jedním jménem. +--- + +# Výklad + +## Index +*od nuly* + +- new int[] { 2, 4, 6 } je pole tří čísel. +- První prvek je znamky[0], poslední znamky[2]. +- Length je počet prvků, ne index posledního. + +> Tip: znamky[3] u pole délky 3 spadne. Poslední index je 2. + +## Zápis +*prvku* + +- Do prvku se dá uložit nová hodnota: znamky[1] = 5. +- Ostatní prvky zůstanou. +- Pole má pevnou délku. Přidat čtvrtý prvek nejde, na to je později List. + +> Tip: Vyměňte první známku a spusťte znovu. + +# Kód + +``` +int[] znamky = { 1, 3, 2 }; +Console.WriteLine(znamky[0]); +Console.WriteLine(znamky.Length); +znamky[1] = 5; +Console.WriteLine(znamky[1]); +``` diff --git a/content/programovani/2-rocnik/06-foreach.en.md b/content/programovani/2-rocnik/06-foreach.en.md new file mode 100644 index 0000000..3a4186e --- /dev/null +++ b/content/programovani/2-rocnik/06-foreach.en.md @@ -0,0 +1,4 @@ +--- +nazev: Foreach +popis: Visit every item without an index. +--- diff --git a/content/programovani/2-rocnik/06-foreach.md b/content/programovani/2-rocnik/06-foreach.md new file mode 100644 index 0000000..1c0a779 --- /dev/null +++ b/content/programovani/2-rocnik/06-foreach.md @@ -0,0 +1,35 @@ +--- +id: cs-2-foreach +nazev: Foreach +popis: Projít každý prvek bez indexu. +--- + +# Výklad + +## Projítí +*foreach* + +- foreach (int z in znamky) vezme prvky od prvního po poslední. +- z je kopie hodnoty. U int se zápisem do z pole nezmění. +- Když index nepotřebujete, foreach je kratší než for. + +> Tip: Součet ve smyčce přičítá aktuální z. + +## Texty +*string[]* + +- Pole může být i ze stringů. +- Stejný foreach funguje pro oba typy, jen se liší typ prvku. +- Pořadí je pořadí v závorce při vytvoření. + +> Tip: Přidejte do pole třetí jméno. + +# Kód + +``` +string[] jmena = { "Anna", "Eva", "Jan" }; +foreach (string jmeno in jmena) +{ + Console.WriteLine(jmeno); +} +``` diff --git a/content/programovani/2-rocnik/07-metody.en.md b/content/programovani/2-rocnik/07-metody.en.md new file mode 100644 index 0000000..3b6fe65 --- /dev/null +++ b/content/programovani/2-rocnik/07-metody.en.md @@ -0,0 +1,4 @@ +--- +nazev: Methods +popis: A named piece of the program you can call. +--- diff --git a/content/programovani/2-rocnik/07-metody.md b/content/programovani/2-rocnik/07-metody.md new file mode 100644 index 0000000..2c0164a --- /dev/null +++ b/content/programovani/2-rocnik/07-metody.md @@ -0,0 +1,37 @@ +--- +id: cs-2-metody +nazev: Metody +popis: Pojmenovaný kus programu, který lze zavolat. +--- + +# Výklad + +## Volání +*jméno()* + +- static void Pozdrav() je metoda bez výsledku. +- Zavolá se Pozdrav(); včetně závorek. +- Bez závorek by to bylo jen jméno, ne volání. + +> Tip: Metodu lze zavolat víckrát a pokaždé udělá totéž. + +## Proč +*jednou* + +- Kód, který se opakuje, patří do metody. +- Jméno metody říká, co dělá: Pozdrav, ne Metoda1. +- void znamená, že nevrací hodnotu. + +> Tip: Přidejte druhé volání Pozdrav(). + +# Kód + +``` +Pozdrav(); +Pozdrav(); + +static void Pozdrav() +{ + Console.WriteLine("Ahoj"); +} +``` diff --git a/content/programovani/2-rocnik/08-navrat.en.md b/content/programovani/2-rocnik/08-navrat.en.md new file mode 100644 index 0000000..8199f32 --- /dev/null +++ b/content/programovani/2-rocnik/08-navrat.en.md @@ -0,0 +1,4 @@ +--- +nazev: Parameters and return +popis: A method receives values and can return one. +--- diff --git a/content/programovani/2-rocnik/08-navrat.md b/content/programovani/2-rocnik/08-navrat.md new file mode 100644 index 0000000..2a9f1cc --- /dev/null +++ b/content/programovani/2-rocnik/08-navrat.md @@ -0,0 +1,38 @@ +--- +id: cs-2-navrat +nazev: Parametry a návrat +popis: Metoda dostane hodnoty a může jednu vrátit. +--- + +# Výklad + +## Parametr +*vstup* + +- int a v závorce je parametr. Volající dodá argument. +- Součet(2, 5) uloží 2 do a a 5 do b. +- Jména parametrů platí jen uvnitř metody. + +> Tip: Změňte argumenty a výsledek se změní, metoda ne. + +## return +*výstup* + +- int před jménem je typ výsledku. +- return hodnotu vrátí a metodu ukončí. +- Volající si výsledek může uložit: int s = Soucet(2, 5); + +> Tip: return 0 omylem na začátku by zbytek metody nespustil. + +# Kód + +``` +int s = Soucet(2, 5); +Console.WriteLine(s); +Console.WriteLine(Soucet(10, 1)); + +static int Soucet(int a, int b) +{ + return a + b; +} +``` diff --git a/content/programovani/3-rocnik/01-trida.en.md b/content/programovani/3-rocnik/01-trida.en.md new file mode 100644 index 0000000..1ac6f34 --- /dev/null +++ b/content/programovani/3-rocnik/01-trida.en.md @@ -0,0 +1,4 @@ +--- +nazev: Class and object +popis: Your own type and one concrete piece of it. +--- diff --git a/content/programovani/3-rocnik/01-trida.md b/content/programovani/3-rocnik/01-trida.md new file mode 100644 index 0000000..54fc345 --- /dev/null +++ b/content/programovani/3-rocnik/01-trida.md @@ -0,0 +1,40 @@ +--- +id: cs-3-trida +nazev: Třída a objekt +popis: Vlastní typ a jeho konkrétní kus. +--- + +# Výklad + +## Třída +*návod* + +- class Zak popisuje, co žák má: jméno a věk. +- new Zak() vytvoří objekt, jeden konkrétní kus. +- Dva new jsou dva objekty. Změna jednoho druhý nemění. + +> Tip: Třída sama o sobě není žák. Je to popis. + +## Pole objektu +*tečka* + +- z.Jmeno se čte a zapisuje přes tečku. +- Na začátek dáme veřejná pole, ať je vidět, komu patří. +- Později je schováme za vlastnost. + +> Tip: Vytvořte druhého žáka a vypište obě jména. + +# Kód + +``` +Zak z = new Zak(); +z.Jmeno = "Anna"; +z.Vek = 17; +Console.WriteLine($"{z.Jmeno}, {z.Vek}"); + +class Zak +{ + public string Jmeno; + public int Vek; +} +``` diff --git a/content/programovani/3-rocnik/02-konstruktor.en.md b/content/programovani/3-rocnik/02-konstruktor.en.md new file mode 100644 index 0000000..22b38d1 --- /dev/null +++ b/content/programovani/3-rocnik/02-konstruktor.en.md @@ -0,0 +1,4 @@ +--- +nazev: Constructor +popis: A method that fills the object immediately. +--- diff --git a/content/programovani/3-rocnik/02-konstruktor.md b/content/programovani/3-rocnik/02-konstruktor.md new file mode 100644 index 0000000..dcd171e --- /dev/null +++ b/content/programovani/3-rocnik/02-konstruktor.md @@ -0,0 +1,44 @@ +--- +id: cs-3-konstruktor +nazev: Konstruktor +popis: Metoda, která objekt hned naplní. +--- + +# Výklad + +## Jméno třídy +*new Zak* + +- Konstruktor se jmenuje jako třída a nemá návratový typ. +- new Zak("Anna", 17) ho zavolá. +- Prázdný new Zak() přestane fungovat, jakmile napíšete vlastní konstruktor. + +> Tip: Hodnoty patří do new, ne až na další řádky. + +## this +*tento objekt* + +- this.Jmeno je pole objektu. Jmeno bez this by v konstruktoru byl parametr. +- Když se jmenují stejně, this je nutný. +- Mimo konstruktor this často není potřeba. + +> Tip: Přidejte do výpisu věk z konstruktoru. + +# Kód + +``` +Zak z = new Zak("Anna", 17); +Console.WriteLine($"{z.Jmeno}, {z.Vek}"); + +class Zak +{ + public string Jmeno; + public int Vek; + + public Zak(string jmeno, int vek) + { + Jmeno = jmeno; + Vek = vek; + } +} +``` diff --git a/content/programovani/3-rocnik/03-vlastnosti.en.md b/content/programovani/3-rocnik/03-vlastnosti.en.md new file mode 100644 index 0000000..d20739a --- /dev/null +++ b/content/programovani/3-rocnik/03-vlastnosti.en.md @@ -0,0 +1,4 @@ +--- +nazev: Properties +popis: get and set instead of a bare field. +--- diff --git a/content/programovani/3-rocnik/03-vlastnosti.md b/content/programovani/3-rocnik/03-vlastnosti.md new file mode 100644 index 0000000..e9cea23 --- /dev/null +++ b/content/programovani/3-rocnik/03-vlastnosti.md @@ -0,0 +1,50 @@ +--- +id: cs-3-vlastnosti +nazev: Vlastnosti +popis: get a set místo holého pole. +--- + +# Výklad + +## get a set +*tečka zůstává* + +- public string Jmeno { get; set; } se zvenku používá jako pole. +- Uvnitř může set odmítnout špatnou hodnotu. +- get hodnotu vrátí, set ji uloží. + +> Tip: Zápis z.Vek = -1 může set zarazit, holé pole ne. + +## Kontrola +*set* + +- V příkladu set u věku záporné číslo neuloží. +- get pořád vrátí poslední platnou hodnotu. +- Tak se objekt nepokazí zvenku. + +> Tip: Zkuste z.Vek = -3 a vypište věk. Zůstane 17. + +# Kód + +``` +Zak z = new Zak("Anna", 17); +z.Vek = -3; +Console.WriteLine(z.Vek); + +class Zak +{ + public string Jmeno { get; set; } + private int vek; + public int Vek + { + get { return vek; } + set { if (value >= 0) vek = value; } + } + + public Zak(string jmeno, int vek) + { + Jmeno = jmeno; + Vek = vek; + } +} +``` diff --git a/content/programovani/3-rocnik/04-zapouzdreni.en.md b/content/programovani/3-rocnik/04-zapouzdreni.en.md new file mode 100644 index 0000000..08bb91c --- /dev/null +++ b/content/programovani/3-rocnik/04-zapouzdreni.en.md @@ -0,0 +1,4 @@ +--- +nazev: Encapsulation +popis: Private data and public methods. +--- diff --git a/content/programovani/3-rocnik/04-zapouzdreni.md b/content/programovani/3-rocnik/04-zapouzdreni.md new file mode 100644 index 0000000..27b5b89 --- /dev/null +++ b/content/programovani/3-rocnik/04-zapouzdreni.md @@ -0,0 +1,49 @@ +--- +id: cs-3-zapouzdreni +nazev: Zapouzdření +popis: Soukromá data a veřejné metody. +--- + +# Výklad + +## private +*schované* + +- private pole vidí jen kód té třídy. +- public metoda je dveře, kterými se s objektem mluví. +- Zvenku z.penize nenapíšete. Zavoláte Pridej. + +> Tip: Když je vše public, kdokoli rozbije pravidla objektu. + +## Metoda +*Pridej* + +- Pridej přičte a vrátí nový stav. +- Záporný vklad lze v metodě odmítnout. +- Výpis stavu jde přes Kolik, ne přes pole. + +> Tip: Zkuste Pridej(-10). Stav se nemá zmenšit. + +# Kód + +``` +Pokladna p = new Pokladna(); +p.Pridej(50); +p.Pridej(-10); +Console.WriteLine(p.Kolik()); + +class Pokladna +{ + private int penize; + + public void Pridej(int castka) + { + if (castka > 0) penize = penize + castka; + } + + public int Kolik() + { + return penize; + } +} +``` diff --git a/content/programovani/3-rocnik/05-dedicnost.en.md b/content/programovani/3-rocnik/05-dedicnost.en.md new file mode 100644 index 0000000..8e3d992 --- /dev/null +++ b/content/programovani/3-rocnik/05-dedicnost.en.md @@ -0,0 +1,4 @@ +--- +nazev: Inheritance +popis: A new class takes over a finished one. +--- diff --git a/content/programovani/3-rocnik/05-dedicnost.md b/content/programovani/3-rocnik/05-dedicnost.md new file mode 100644 index 0000000..6b1029a --- /dev/null +++ b/content/programovani/3-rocnik/05-dedicnost.md @@ -0,0 +1,52 @@ +--- +id: cs-3-dedicnost +nazev: Dědičnost +popis: Nová třída převezme hotovou. +--- + +# Výklad + +## base +*: Zak* + +- class Student : Zak znamená, že student je žák a má něco navíc. +- Dostane Jmeno a Vek, aniž by se psaly znovu. +- base(...) zavolá konstruktor předka. + +> Tip: Student umí to, co Zak, a ještě obor. + +## Je +*typ* + +- Student lze uložit do proměnné Zak. Pořád je to student. +- Opačně žák bez oboru studentem není. +- Na specialitu sáhnete, až když máte původní typ. + +> Tip: Vypište i obor. Patří studentovi, ne obecnému žákovi. + +# Kód + +``` +Student s = new Student("Eva", 18, "informatika"); +Console.WriteLine($"{s.Jmeno}, {s.Obor}"); + +class Zak +{ + public string Jmeno { get; set; } + public int Vek { get; set; } + public Zak(string jmeno, int vek) + { + Jmeno = jmeno; + Vek = vek; + } +} + +class Student : Zak +{ + public string Obor { get; set; } + public Student(string jmeno, int vek, string obor) : base(jmeno, vek) + { + Obor = obor; + } +} +``` diff --git a/content/programovani/3-rocnik/06-polymorfismus.en.md b/content/programovani/3-rocnik/06-polymorfismus.en.md new file mode 100644 index 0000000..61e4f62 --- /dev/null +++ b/content/programovani/3-rocnik/06-polymorfismus.en.md @@ -0,0 +1,4 @@ +--- +nazev: Polymorphism +popis: The same call, a different answer from the child. +--- diff --git a/content/programovani/3-rocnik/06-polymorfismus.md b/content/programovani/3-rocnik/06-polymorfismus.md new file mode 100644 index 0000000..f2b4bb7 --- /dev/null +++ b/content/programovani/3-rocnik/06-polymorfismus.md @@ -0,0 +1,50 @@ +--- +id: cs-3-polymorfismus +nazev: Polymorfismus +popis: Stejné volání, jiná odpověď potomka. +--- + +# Výklad + +## virtual a override +*přepsání* + +- virtual u předka dovolí potomkovi metodu vyměnit. +- override u potomka řekne, že jde o tu samou metodu. +- Bez virtual by potomek jen schoval jméno a přes typ předka by se volala stará verze. + +> Tip: Proměnná je Zvire, objekt je Pes. Zavolá se štěk, ne obecné zvíře. + +## Seznam +*víc tvarů* + +- Do pole Zvire lze dát psa i kočku. +- foreach volá Zvuk a každý objekt odpoví po svém. +- Nové zvíře přidáte třídou, ne úpravou smyčky. + +> Tip: Přidejte do pole kočku a třetí řádek výstupu bude mňau. + +# Kód + +``` +Zvire[] zvirata = { new Pes(), new Kocka() }; +foreach (Zvire z in zvirata) +{ + Console.WriteLine(z.Zvuk()); +} + +class Zvire +{ + public virtual string Zvuk() { return "..."; } +} + +class Pes : Zvire +{ + public override string Zvuk() { return "haf"; } +} + +class Kocka : Zvire +{ + public override string Zvuk() { return "mňau"; } +} +``` diff --git a/content/programovani/3-rocnik/07-rozhrani.en.md b/content/programovani/3-rocnik/07-rozhrani.en.md new file mode 100644 index 0000000..1f39bf6 --- /dev/null +++ b/content/programovani/3-rocnik/07-rozhrani.en.md @@ -0,0 +1,4 @@ +--- +nazev: Interface +popis: A contract: which methods a type must have. +--- diff --git a/content/programovani/3-rocnik/07-rozhrani.md b/content/programovani/3-rocnik/07-rozhrani.md new file mode 100644 index 0000000..0caebee --- /dev/null +++ b/content/programovani/3-rocnik/07-rozhrani.md @@ -0,0 +1,50 @@ +--- +id: cs-3-rozhrani +nazev: Rozhraní +popis: Smlouva: které metody typ musí mít. +--- + +# Výklad + +## interface +*I* + +- interface nemá hotový kód, jen hlavičky metod. +- class Pes : IMluvici slíbí, že Mluv napíše. +- Jedna třída může splnit víc rozhraní. Dědit může jen jednu třídu. + +> Tip: Jméno rozhraní se často píše s I na začátku: IMluvici. + +## Společný typ +*seznam* + +- Proměnná typu rozhraní udrží kohokoli, kdo smlouvu splnil. +- Pes a Radio nemají společného předka, ale oba umí Mluv. +- Smyčka jim rozumí přes smlouvu, ne přes původ. + +> Tip: Rozhraní neříká, jak metoda funguje. To napíše až třída. + +# Kód + +``` +IMluvici[] veci = { new Pes(), new Radio() }; +foreach (IMluvici v in veci) +{ + Console.WriteLine(v.Mluv()); +} + +interface IMluvici +{ + string Mluv(); +} + +class Pes : IMluvici +{ + public string Mluv() { return "haf"; } +} + +class Radio : IMluvici +{ + public string Mluv() { return "hudba"; } +} +``` diff --git a/content/programovani/3-rocnik/08-enum.en.md b/content/programovani/3-rocnik/08-enum.en.md new file mode 100644 index 0000000..4fb204b --- /dev/null +++ b/content/programovani/3-rocnik/08-enum.en.md @@ -0,0 +1,4 @@ +--- +nazev: Enum +popis: A fixed list of named values. +--- diff --git a/content/programovani/3-rocnik/08-enum.md b/content/programovani/3-rocnik/08-enum.md new file mode 100644 index 0000000..dec3916 --- /dev/null +++ b/content/programovani/3-rocnik/08-enum.md @@ -0,0 +1,50 @@ +--- +id: cs-3-enum +nazev: Výčet +popis: Pevný seznam pojmenovaných hodnot. +--- + +# Výklad + +## enum +*Den* + +- enum Den { Po, Ut, St } jsou tři pojmenované hodnoty. +- Místo magického čísla 1 píšete Den.Po. +- Překladač nepustí hodnotu, která ve výčtu není. + +> Tip: Čísla 0, 1, 2 by za měsíc nikdo nepřečetl. Jména ano. + +## switch +*přes výčet* + +- switch na enum má case pro každou hodnotu. +- Když přidáte čtvrtek a zapomenete case, překlad na to v novém zápisu upozorní. +- Výčet je typ. Není to string. + +> Tip: Změňte dnes na Den.St. + +# Kód + +``` +Den dnes = Den.Po; +switch (dnes) +{ + case Den.Po: + Console.WriteLine("pondělí"); + break; + case Den.Ut: + Console.WriteLine("úterý"); + break; + default: + Console.WriteLine("jiný den"); + break; +} + +enum Den +{ + Po, + Ut, + St +} +``` diff --git a/content/programovani/4-rocnik/01-vyjimky.en.md b/content/programovani/4-rocnik/01-vyjimky.en.md new file mode 100644 index 0000000..c4750a7 --- /dev/null +++ b/content/programovani/4-rocnik/01-vyjimky.en.md @@ -0,0 +1,4 @@ +--- +nazev: Exceptions +popis: An error you can catch and explain. +--- diff --git a/content/programovani/4-rocnik/01-vyjimky.md b/content/programovani/4-rocnik/01-vyjimky.md new file mode 100644 index 0000000..117182a --- /dev/null +++ b/content/programovani/4-rocnik/01-vyjimky.md @@ -0,0 +1,43 @@ +--- +id: cs-4-vyjimky +nazev: Výjimky +popis: Chyba, kterou lze chytit a vysvětlit. +--- + +# Výklad + +## try +*pokus* + +- try je kus, který může spadnout. +- catch (Exception e) ho chytí, místo aby spadl celý program. +- e.Message je text chyby. + +> Tip: Bez try by Parse na textu abc program ukončil. + +## finally +*vždy* + +- finally se spustí, ať try prošel, nebo ne. +- Hodí se na úklid. V příkladu jen vypíše, že končíme. +- catch může být víc, pro různé typy chyb. + +> Tip: Změňte abc na 5. catch se přeskočí, finally ne. + +# Kód + +``` +try +{ + int n = int.Parse("abc"); + Console.WriteLine(n); +} +catch (Exception e) +{ + Console.WriteLine("Chyba: " + e.Message); +} +finally +{ + Console.WriteLine("konec"); +} +``` diff --git a/content/programovani/4-rocnik/02-list.en.md b/content/programovani/4-rocnik/02-list.en.md new file mode 100644 index 0000000..0c8016d --- /dev/null +++ b/content/programovani/4-rocnik/02-list.en.md @@ -0,0 +1,4 @@ +--- +nazev: List +popis: A list that can grow. +--- diff --git a/content/programovani/4-rocnik/02-list.md b/content/programovani/4-rocnik/02-list.md new file mode 100644 index 0000000..16cbf92 --- /dev/null +++ b/content/programovani/4-rocnik/02-list.md @@ -0,0 +1,39 @@ +--- +id: cs-4-list +nazev: List +popis: Seznam, který umí růst. +--- + +# Výklad + +## Přidávání +*Add* + +- List začne prázdný. Add přidá na konec. +- Count je počet. Indexy jsou zase od nuly. +- Na rozdíl od pole délku dopředu neříkáte. + +> Tip: List potřebuje špičaté závorky s typem prvku. + +## Odebrání +*Remove* + +- Remove("Eva") odebere první Evu. +- RemoveAt(0) odebere podle indexu. +- foreach pak projde, co zůstalo. + +> Tip: Přidejte třetí jméno před odebráním. + +# Kód + +``` +List jmena = new List(); +jmena.Add("Anna"); +jmena.Add("Eva"); +jmena.Remove("Eva"); +foreach (string j in jmena) +{ + Console.WriteLine(j); +} +Console.WriteLine(jmena.Count); +``` diff --git a/content/programovani/4-rocnik/03-slovnik.en.md b/content/programovani/4-rocnik/03-slovnik.en.md new file mode 100644 index 0000000..40451f5 --- /dev/null +++ b/content/programovani/4-rocnik/03-slovnik.en.md @@ -0,0 +1,4 @@ +--- +nazev: Dictionary +popis: A value under a key. +--- diff --git a/content/programovani/4-rocnik/03-slovnik.md b/content/programovani/4-rocnik/03-slovnik.md new file mode 100644 index 0000000..4ff2289 --- /dev/null +++ b/content/programovani/4-rocnik/03-slovnik.md @@ -0,0 +1,39 @@ +--- +id: cs-4-slovnik +nazev: Slovník +popis: Hodnota pod klíčem. +--- + +# Výklad + +## Klíč +*jednou* + +- Dictionary má klíč text a hodnotu číslo. +- znamky["Anna"] = 1 uloží nebo přepíše. +- Stejný klíč je v slovníku jen jednou. + +> Tip: Pole je podle čísla. Slovník je podle jména, které si zvolíte. + +## Čtení +*ContainsKey* + +- Čtení chybějícího klíče spadne. +- ContainsKey napřed zjistí, jestli klíč je. +- foreach projde dvojice Key a Value. + +> Tip: Zeptejte se na jméno, které v slovníku není, až po ContainsKey. + +# Kód + +``` +Dictionary znamky = new Dictionary(); +znamky["Anna"] = 1; +znamky["Eva"] = 2; +znamky["Anna"] = 3; +if (znamky.ContainsKey("Eva")) +{ + Console.WriteLine(znamky["Eva"]); +} +Console.WriteLine(znamky["Anna"]); +``` diff --git a/content/programovani/4-rocnik/04-linq.en.md b/content/programovani/4-rocnik/04-linq.en.md new file mode 100644 index 0000000..0b2ec5a --- /dev/null +++ b/content/programovani/4-rocnik/04-linq.en.md @@ -0,0 +1,4 @@ +--- +nazev: LINQ +popis: Queries over a list. +--- diff --git a/content/programovani/4-rocnik/04-linq.md b/content/programovani/4-rocnik/04-linq.md new file mode 100644 index 0000000..de4a297 --- /dev/null +++ b/content/programovani/4-rocnik/04-linq.md @@ -0,0 +1,36 @@ +--- +id: cs-4-linq +nazev: LINQ +popis: Dotazy nad seznamem. +--- + +# Výklad + +## Where +*filtr* + +- Where nechá jen prvky, které splní podmínku. +- n => n >= 10 je krátká funkce: vezmi n a vrať, jestli je aspoň 10. +- Samotné Where ještě nic neprojde. ToList nebo foreach výsledek vezme. + +> Tip: Šipka => není porovnání. Je to zápis malé funkce. + +## Select +*převod* + +- Select z každého prvku udělá jiný. +- OrderBy seřadí. +- Dotaz lze řetězit: Where a pak Select. + +> Tip: Změňte 10 na 12 a dvojka z výpisu zmizí. + +# Kód + +``` +int[] cisla = { 4, 11, 7, 20 }; +var velka = cisla.Where(n => n >= 10).OrderBy(n => n); +foreach (int n in velka) +{ + Console.WriteLine(n); +} +``` diff --git a/content/programovani/4-rocnik/05-soubory.en.md b/content/programovani/4-rocnik/05-soubory.en.md new file mode 100644 index 0000000..f0b0642 --- /dev/null +++ b/content/programovani/4-rocnik/05-soubory.en.md @@ -0,0 +1,4 @@ +--- +nazev: Files +popis: Writing and reading text. +--- diff --git a/content/programovani/4-rocnik/05-soubory.md b/content/programovani/4-rocnik/05-soubory.md new file mode 100644 index 0000000..bb0133a --- /dev/null +++ b/content/programovani/4-rocnik/05-soubory.md @@ -0,0 +1,33 @@ +--- +id: cs-4-soubory +nazev: Soubory +popis: Zápis a čtení textu. +--- + +# Výklad + +## WriteAllText +*jednou* + +- File.WriteAllText zapíše celý soubor. Když existuje, přepíše ho. +- Cesta pozdrav.txt je vedle programu, v jeho složce. +- ReadAllText celý text vrátí jako string. + +> Tip: Na telefonu se soubor nespustí. Na počítači s .NET ano. + +## Řádky +*Append* + +- AppendAllText přidá na konec a soubor nesmaže. +- ReadAllLines vrátí pole řádků. +- Po skončení programu soubor ve složce laboratoře zůstane. + +> Tip: Spusťte dvakrát. Druhý zápis WriteAllText starý text nahradí. + +# Kód + +``` +File.WriteAllText("pozdrav.txt", "Ahoj ze souboru"); +string zpet = File.ReadAllText("pozdrav.txt"); +Console.WriteLine(zpet); +``` diff --git a/content/programovani/4-rocnik/06-generika.en.md b/content/programovani/4-rocnik/06-generika.en.md new file mode 100644 index 0000000..422ce16 --- /dev/null +++ b/content/programovani/4-rocnik/06-generika.en.md @@ -0,0 +1,4 @@ +--- +nazev: Generics +popis: One method for several types. +--- diff --git a/content/programovani/4-rocnik/06-generika.md b/content/programovani/4-rocnik/06-generika.md new file mode 100644 index 0000000..dfe9d7d --- /dev/null +++ b/content/programovani/4-rocnik/06-generika.md @@ -0,0 +1,37 @@ +--- +id: cs-4-generika +nazev: Generika +popis: Jedna metoda pro víc typů. +--- + +# Výklad + +## T +*typový parametr* + +- static void Vypis(T hodnota) přijme int i string. +- T se doplní při volání podle argumentu. +- Jedna metoda tak nenutí kopírovat kód pro každý typ. + +> Tip: Když metoda s T má umět jen porovnávat, T se omezí. Tady stačí výpis. + +## List +*už ho znáte* + +- List je generická třída. string je dosazený T. +- Stejný List drží čísla a metody jsou stejné. +- Generika hlídá typ. Do List string nedáte. + +> Tip: Zavolejte Vypis s číslem i s textem. + +# Kód + +``` +Vypis(7); +Vypis("ahoj"); + +static void Vypis(T hodnota) +{ + Console.WriteLine(hodnota); +} +``` diff --git a/content/programovani/4-rocnik/07-delegaty.en.md b/content/programovani/4-rocnik/07-delegaty.en.md new file mode 100644 index 0000000..d39c31f --- /dev/null +++ b/content/programovani/4-rocnik/07-delegaty.en.md @@ -0,0 +1,4 @@ +--- +nazev: Delegates +popis: A variable that holds a method. +--- diff --git a/content/programovani/4-rocnik/07-delegaty.md b/content/programovani/4-rocnik/07-delegaty.md new file mode 100644 index 0000000..b5b9652 --- /dev/null +++ b/content/programovani/4-rocnik/07-delegaty.md @@ -0,0 +1,34 @@ +--- +id: cs-4-delegaty +nazev: Delegáty +popis: Proměnná, do které uložíte metodu. +--- + +# Výklad + +## Func +*funkce* + +- Func je metoda, která dostane int a vrátí int. +- x => x * x je taková metoda bez jména. +- Do proměnné ji uložíte a zavoláte později. + +> Tip: Závorky pocet(4) metodu spustí. Bez nich byste ji jen předali dál. + +## Action +*bez výsledku* + +- Action nic nevrací. Hodí se na kroky. +- Metodu můžete předat jiné metodě jako argument. +- Tak se píše i Where z minulé lekce. + +> Tip: Vyměňte x * x za x + 1. + +# Kód + +``` +Func pocet = x => x * x; +Console.WriteLine(pocet(4)); +Action pozdrav = () => Console.WriteLine("ahoj"); +pozdrav(); +``` diff --git a/content/programovani/4-rocnik/08-async.en.md b/content/programovani/4-rocnik/08-async.en.md new file mode 100644 index 0000000..98da3e9 --- /dev/null +++ b/content/programovani/4-rocnik/08-async.en.md @@ -0,0 +1,4 @@ +--- +nazev: Async and await +popis: Work that waits without freezing the way you write it. +--- diff --git a/content/programovani/4-rocnik/08-async.md b/content/programovani/4-rocnik/08-async.md new file mode 100644 index 0000000..9baebaf --- /dev/null +++ b/content/programovani/4-rocnik/08-async.md @@ -0,0 +1,33 @@ +--- +id: cs-4-async +nazev: Async a await +popis: Práce, která počká, aniž by zamkla zbytek zápisu. +--- + +# Výklad + +## await +*počkej* + +- await Task.Delay čeká, ale zápis zůstane řádek za řádkem. +- Metoda s await má v hlavičce async a vrací Task. +- V krátkém programu smí await stát i na nejvyšší úrovni. + +> Tip: Delay(300) čeká 300 milisekund. Výpis hotovo přijde až po něm. + +## Task +*slib* + +- Task je slib, že práce doběhne. +- await slib rozbalí. Bez await byste měli slib, ne výsledek. +- Na síť a soubory se await hodí. Na 2 + 2 ne. + +> Tip: Dejte za první výpis ještě jeden await a druhý řádek. + +# Kód + +``` +Console.WriteLine("začátek"); +await Task.Delay(300); +Console.WriteLine("hotovo"); +``` diff --git a/content/programove-vybaveni/1-rocnik/01-software-a-licence.en.md b/content/programove-vybaveni/1-rocnik/01-software-a-licence.en.md new file mode 100644 index 0000000..77b2182 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/01-software-a-licence.en.md @@ -0,0 +1,6 @@ +--- +nazev: Software and licences +popis: System and application programs, and the terms for using them. +nazev_cviceni: Quiz: software and licences +nadpis_kvizu: Name the kind of program and the kind of licence +--- diff --git a/content/programove-vybaveni/1-rocnik/01-software-a-licence.md b/content/programove-vybaveni/1-rocnik/01-software-a-licence.md new file mode 100644 index 0000000..e40dee4 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/01-software-a-licence.md @@ -0,0 +1,79 @@ +--- +id: sw-1-software-a-licence +nazev: Software a licence +popis: Systémové a aplikační programy a to, za jakých podmínek se smí používat. +nazev_cviceni: Kvíz: software a licence +nadpis_kvizu: Poznejte druh programu a druh licence +--- + +# Výklad + +## Dva druhy programů +*Software* + +- Software je program. Hardware je zařízení. Samotný počítač bez programu nic neudělá. +- Operační systém řídí počítač: Windows, Linux, macOS. Ve škole pracujeme ve Windows. +- Aplikační software řeší jednu práci: Word dokument, Excel tabulku, prohlížeč web. +- Ovladač je malý program k zařízení. Tiskárna bez ovladače netiskne, i když je zapnutá. + +> Tip: Když program spadne a systém běží dál, spadla aplikace. Když nesvítí plocha, spadl systém. + +## Licence +*Podmínky* + +- Licence říká, kdo program smí nainstalovat a na kolik počítačů. +- Komerční program se kupuje, nebo se platí předplatné. Microsoft 365 je předplatné. +- OEM licence je přivázaná k jednomu počítači. Na nový stroj se nepřetahuje. +- Stažený program bez licence není zdarma. Je to kopie, kterou škola nesmí používat. + +> Tip: Školní účet Microsoft 365 nepatří na domácí počítače navždy. Platí, dokud škola předplatné má. + +## Zdarma není jeden pojem +*Freeware a open source* + +- Freeware je zdarma, ale zdrojový kód nedostanete. Smíte ho používat za podmínek autora. +- Open source má otevřený kód. GPL navíc chce, aby odvozené programy zůstaly otevřené. +- Zkušební verze (shareware) po čase skončí, nebo omezí funkce. Není to trvalá licence. +- Na školní počítač patří jen programy, které škola schválila. Vlastní instalace se hlásí učiteli. + +> Tip: Cena nula neznamená, že smíte program rozdávat dál. I freeware má licenci. + +## Zadání: šest programů +*Cvičení* + +- Do tabulky zapište šest programů ze školního počítače: systém, prohlížeč, Word, Excel a dva další. +- U každého napište, jestli je systémový, nebo aplikační. +- U Wordu a Excelu napište, jaká licence ve škole je: předplatné Microsoft 365, ne domácí krabice. +- Soubor pojmenujte licence.txt a uložte ho do Dokumenty. Zatím nic neinstalujte. + +> Tip: Název systému zjistíte příkazem winver. Okno ukáže edici Windows a verzi. + +# Kvíz + +## Kam patří Microsoft Word? +- Mezi operační systémy +- [x] Mezi aplikační software +- Mezi ovladače tiskárny +- Mezi hardware +> Word řeší jednu práci, dokument. Systém pod ním je Windows. + +## Co znamená OEM licence Windows? +- Smí se přenést na každý další počítač +- [x] Je vázaná na jeden počítač +- Je to totéž co GPL +- Platí jen 14 dní +> OEM se prodává s počítačem a na jiný stroj se nepřesouvá. + +## Čím se freeware liší od open source? +- Freeware má otevřený zdrojový kód +- [x] Freeware je zdarma, ale kód obvykle nedostanete +- Open source se nesmí používat ve škole +- Je to totéž +> Zdarma a otevřený kód jsou dvě různé věci. GPL je otevřená licence, ne jen nulová cena. + +## Který program je operační systém? +- Excel +- [x] Windows +- WordPress +- Power BI +> Windows řídí počítač. Ostatní jsou aplikace, které na systému běží. diff --git a/content/programove-vybaveni/1-rocnik/02-operacni-system.en.md b/content/programove-vybaveni/1-rocnik/02-operacni-system.en.md new file mode 100644 index 0000000..572309c --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/02-operacni-system.en.md @@ -0,0 +1,6 @@ +--- +nazev: The operating system +popis: What the system does between the user and the hardware. +nazev_cviceni: Quiz: the operating system +nadpis_kvizu: Name the role of the system +--- diff --git a/content/programove-vybaveni/1-rocnik/02-operacni-system.md b/content/programove-vybaveni/1-rocnik/02-operacni-system.md new file mode 100644 index 0000000..352a114 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/02-operacni-system.md @@ -0,0 +1,79 @@ +--- +id: sw-1-operacni-system +nazev: Operační systém +popis: Co systém dělá mezi uživatelem a hardwarem. +nazev_cviceni: Kvíz: operační systém +nadpis_kvizu: Poznejte roli systému +--- + +# Výklad + +## Mezi člověkem a strojem +*Role* + +- Systém spouští programy, hlídá paměť a přiděluje čas procesoru. +- Souborový systém ukládá data na disk. Ve Windows je to NTFS. +- Ovladač si systém načte, aby mluvil s tiskárnou, diskem a sítí. +- Uživatel vidí plochu a okna. Jádro pod tím nevidí a do něj nesahá. + +> Tip: Když dva programy chtějí tiskárnu najednou, rozhodne systém. Samy se nedomluví. + +## Tři systémy +*Windows, Linux, macOS* + +- Školní učebna je Windows. Příkazy a obrazovky v tomto předmětu jsou z Windows. +- Linux je častý na serverech. Stejný dokument v Excelu tam sám od sebe není. +- macOS běží na počítačích Apple. Zkratka Ctrl je tam často Cmd. +- Soubor .docx je dokument Wordu. Není to program a není to systém. + +> Tip: Přípona souboru říká, čím ho otevřít. Systém podle ní vybere aplikaci. + +## Start počítače +*Až po přihlášení* + +- Po zapnutí naběhne firmware, pak systém, pak přihlašovací obrazovka. +- Dokud se nepřihlásíte, pracujete s účtem nikoho. Soubory uživatele jsou zamčené. +- Každý účet má vlastní plochu a složku C:\Users\jméno. +- Vypnutí přes nabídku Start uloží práci systému. Dlouhý stisk tlačítka je až poslední možnost. + +> Tip: Restart po aktualizaci není trest. Systém si domlouvá soubory, které za běhu nevymění. + +## Zadání: jaký systém to je +*Cvičení* + +- Spusťte winver a opište edici a verzi, třeba Windows 11 Pro, verzi z okna. +- Otevřete C:\Users a napište, které složky uživatelů tam jsou. Do cizích nesaháte. +- Najděte jeden ovladač v názvu: ve Správci zařízení rozbalte Monitory nebo Tiskárny. +- Zápis uložte jako system.txt do Dokumenty. Hesla ani sériová čísla nepište. + +> Tip: Správce zařízení je ve Windows devmgmt.msc. Žlutý vykřičník znamená chybějící nebo vadný ovladač. + +# Kvíz + +## Co dělá operační systém s pamětí? +- Nic, paměť si berou programy samy +- [x] Přiděluje ji programům +- Maže ji při každém uložení dokumentu +- Patří do Excelu +> Program požádá, systém přidělí. Dva programy nesmí psát do stejného místa. + +## Jaký souborový systém používá školní Windows na disku? +- [x] NTFS +- Jen názvy souborů bez systému +- SVG +- MySQL +> NTFS drží soubory, oprávnění i větší disky. Názvy složek jsou až uvnitř něj. + +## Kdy smíte sáhnout do složky jiného uživatele v C:\Users? +- Kdykoli, je to jeden počítač +- [x] Nesmíte, je to jeho účet +- Jen když je složka na ploše +- Když má dokument příponu docx +> Každý účet má vlastní soubory. Cizí složka není společný šuplík. + +## Čím zjistíte edici Windows? +- [x] Příkazem winver +- Otevřením Excelu +- Smazáním koše +- Souborem .bat +> winver otevře okno O systému Windows s edicí a verzí. diff --git a/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.en.md b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.en.md new file mode 100644 index 0000000..be64b98 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Files and folders +popis: A path, an extension and order in Documents. +nazev_cviceni: Quiz: files +nadpis_kvizu: Build the path and the name +--- diff --git a/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.md b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.md new file mode 100644 index 0000000..8989f5a --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/03-soubory-a-slozky.md @@ -0,0 +1,79 @@ +--- +id: sw-1-soubory +nazev: Soubory a složky +popis: Cesta, přípona a pořádek v Dokumentech. +nazev_cviceni: Kvíz: soubory +nadpis_kvizu: Sestavte cestu a název +--- + +# Výklad + +## Cesta +*Kde soubor leží* + +- Cesta začíná diskem. C:\Users\novak\Documents\Skola\1-rocnik je složka, ne soubor. +- Zpětné lomítko odděluje složky. Dopředné lomítko je na webu, ne ve Windows. +- Dokumenty jsou %USERPROFILE%\Documents. Na každém účtu to vede jinam, příkaz je stejný. +- Plocha je taky složka. Soubor jen na ploše se ve školním pořádku ztratí. + +> Tip: Do řádku adresy v Průzkumníku vložte cestu a Enter. Nemusíte proklikávat pět složek. + +## Přípona +*Čím se soubor otevře* + +- prihlaska.docx je Word. znamky.xlsx je Excel. poznamka.txt je prostý text. +- Windows přípony schovává. Zapněte Zobrazit, Zobrazit, Přípony názvů souborů. +- Přejmenovat foto.jpg na foto.docx z něj dokument neudělá. Změní se jen štítek. +- Dvojitá přípona faktura.pdf.exe je program, který se tváří jako dokument. Neotevírejte ho. + +> Tip: Když přípony vidíte, podvodný soubor poznáte dřív, než na něj kliknete. + +## Názvy +*Co je zakázané* + +- V názvu nesmí být znaky \ / : * ? " < > |. +- Mezera smí být, ale v dávkových souborech pak název patří do uvozovek. +- Školní název je věcný: 2026-06-12-zadost.docx. Final-final2.docx není název. +- Datum pište rok-měsíc-den. Složka se seřadí od nejstaršího sama. + +> Tip: Kopírování je Ctrl+C, vložení Ctrl+V, přesun začíná Ctrl+X. Přesun z flashky na disk soubor na flashce smaže. + +## Zadání: strom školy +*Cvičení* + +- V Dokumentech založte Skola, v ní 1-rocnik a v ní slozky word, excel a system. +- Do word uložte prázdný soubor poznamka.txt. Příponu ať je vidět. +- Cesta ať končí na \Skola\1-rocnik\word\poznamka.txt. +- Na ploše nic nenechávejte. Koš nevysypávejte, dokud nevíte, co v něm je. + +> Tip: Nová složka je Ctrl+Shift+N. Když se jmenuje Nová složka, hned ji přejmenujte. + +# Kvíz + +## Která cesta vede k souboru v Dokumentech? +- [x] C:\Users\novak\Documents\Skola\1-rocnik\poznamka.txt +- https://skola/poznamka.txt +- poznamka.txt bez složky, to stačí vždy +- C:/Users/novak s obyčejnými lomítky jako jediná správná forma +> Ve Windows se složky oddělují zpětným lomítkem a cesta začíná diskem. + +## Co se stane, když foto.jpg přejmenujete na foto.docx? +- Stane se z něj upravitelný dokument Word +- [x] Změní se přípona, obsah zůstane obrázek +- Soubor se uloží do koše +- Vznikne tabulka Excel +> Přípona je nápověda pro systém. Bajty obrázku se samy nepřepíšou na dokument. + +## Který název je na školní odevzdání nejlepší? +- final2.docx +- [x] 2026-06-12-zadost.docx +- Nový dokument.docx +- ???.docx +> Datum rok-měsíc-den se seřadí a je z něj poznat verze. Otazník v názvu Windows ani nepustí. + +## Proč zapnout zobrazení přípon? +- Aby šly soubory mazat +- [x] Aby byl vidět skutečný typ, třeba pdf.exe +- Protože bez přípony soubor nejde uložit +- Excel to vyžaduje u každé buňky +> Skrytá přípona schová, že faktura.pdf.exe je program. diff --git a/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.en.md b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.en.md new file mode 100644 index 0000000..2408177 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.en.md @@ -0,0 +1,6 @@ +--- +nazev: Installation and updates +popis: Where a program comes from and why the system is updated. +nazev_cviceni: Quiz: installation +nadpis_kvizu: Pick a safe source +--- diff --git a/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.md b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.md new file mode 100644 index 0000000..4b787c9 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/04-instalace-a-aktualizace.md @@ -0,0 +1,79 @@ +--- +id: sw-1-instalace +nazev: Instalace a aktualizace +popis: Odkud se program bere a proč se systém aktualizuje. +nazev_cviceni: Kvíz: instalace +nadpis_kvizu: Vyberte bezpečný zdroj +--- + +# Výklad + +## Odkud instalovat +*Zdroj* + +- Školní programy berte z Microsoft Store, z balíčku školy, nebo z webu výrobce. +- Odkaz v mailu, tlačítko Stáhnout zdarma na cizím webu a crack nejsou zdroj. +- Instalace chce souhlas správce. Okno Řízení uživatelských účtů se neodklepává poslepu. +- Na plochu nepatří deset instalačních souborů. Po instalaci je smažte, program už je v systému. + +> Tip: Název souboru setup.exe nic neříká. Říká to vydavatel v okně účtů a adresa, odkud soubor přišel. + +## Aktualizace +*Windows Update* + +- Aktualizace zašívá chyby, přes které se do počítače leze. Odkládat ji týdny je riziko. +- Windows Update je v Nastavení, Windows Update. Školní počítač nechte doběhnout. +- Restart po aktualizaci udělejte, když o něj systém požádá. Ne uprostřed testu, ale ten den. +- Word a Excel se v Microsoft 365 aktualizují se sadou Office, ne jen s Windows. + +> Tip: Verze systému z winver se po velké aktualizaci změní. Když se měsíc nemění, aktualizace stojí. + +## Odinstalace +*Aplikace* + +- Program se odebírá v Nastavení, Aplikace, Nainstalované aplikace. Ne smazáním zástupce. +- Zástupce na ploše je jen odkaz. Smazáním ikony program v počítači zůstane. +- Neznámý program, který jste neinstalovali, nejdřív nahlaste. Nemusí to být hra spolužáka. +- Odinstalace nesmaže vaše dokumenty. Smaže program. Dokumenty si před velkým úklidem zkopírujte. + +> Tip: Když v seznamu aplikací Word není a zástupce ano, zástupce vede do prázdna, nebo na jiný disk. + +## Zadání: stav počítače +*Cvičení* + +- Z winver opište verzi. V Windows Update napište, jestli je systém aktuální, nebo něco čeká. +- V nainstalovaných aplikacích najděte Word nebo Microsoft 365 a opište název, jak ho seznam uvádí. +- Najděte jeden program, který nepoznáváte, a zapište ho. Neodinstalovávejte ho bez učitele. +- Uložte stav.txt do Skola\1-rocnik\system. Nic z internetu nestahujte. + +> Tip: Čas poslední aktualizace je ve Windows Update dole v historii. Stačí datum, ne seznam všech záplat. + +# Kvíz + +## Odkud se má školní program instalovat? +- Z odkazu v neznámém mailu +- [x] Z webu výrobce, z Store, nebo z balíčku školy +- Z stránky s nápisem crack +- Z přílohy faktura.pdf.exe +> Výrobce, obchod a škola jsou zdroje. Crack a náhodný mail nejsou. + +## Co udělá smazání zástupce na ploše? +- Odinstaluje program +- [x] Smaže jen odkaz, program zůstane +- Smaže Dokumenty +- Vypne aktualizace +> Zástupce je zkratka. Program se odebírá v Nainstalovaných aplikacích. + +## Proč se Windows Update nenechává měsíc stát? +- Protože jinak nejde otevřít Word +- [x] Záplaty zavírají známé díry +- Protože se jinak smaže plocha +- Aktualizace jen mění tapetu +> Neaktualizovaný systém má chyby, které už jsou veřejně popsané. + +## Kdy odklepnete okno Řízení uživatelských účtů? +- Vždycky, ať instalace pokračuje +- [x] Jen když víte, jaký program a odkud ho spouštíte +- Nikdy, ani u školního balíčku +- Jen v Excelu +> Okno říká, že program chce právo správce. Neznámý vydavatel se neodklepává. diff --git a/content/programove-vybaveni/1-rocnik/05-ucty.en.md b/content/programove-vybaveni/1-rocnik/05-ucty.en.md new file mode 100644 index 0000000..b525df1 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/05-ucty.en.md @@ -0,0 +1,6 @@ +--- +nazev: User accounts +popis: A standard user, an administrator and a password that is not the account name. +nazev_cviceni: Quiz: accounts +nadpis_kvizu: Pick the account type and the password +--- diff --git a/content/programove-vybaveni/1-rocnik/05-ucty.md b/content/programove-vybaveni/1-rocnik/05-ucty.md new file mode 100644 index 0000000..fc41b33 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/05-ucty.md @@ -0,0 +1,79 @@ +--- +id: sw-1-ucty +nazev: Uživatelské účty +popis: Běžný uživatel, správce a heslo, které není jméno účtu. +nazev_cviceni: Kvíz: účty +nadpis_kvizu: Vyberte typ účtu a heslo +--- + +# Výklad + +## Dva typy +*Správce a uživatel* + +- Správce smí instalovat programy a měnit systém. Ve škole je to účet učitele nebo správce učebny. +- Běžný uživatel smí pracovat se svými soubory. Systémové složky mu systém zamkne. +- Denní práce nepatří na účet správce. Virus by dostal stejná práva jako vy. +- Nový školní účet pro cvičení se jmenuje zaci a je běžný uživatel, ne správce. + +> Tip: Když systém požádá o heslo správce, běžný účet na to nestačí. To je záměr, ne porucha. + +## Heslo +*Osm znaků* + +- Heslo má aspoň 8 znaků a není to jméno účtu, ani slovo heslo, ani 12345678. +- Do příkazu se heslo nepíše. Příkaz net user zaci * /add se na heslo zeptá a na obrazovce ho neukáže. +- Heslo na papírek na monitor nepatří. Patří do hlavy, nebo do školního sejfu hesel, když ho učitel určí. +- Cizí heslo se nezkouší. Ani když je účet spolužáka odhlášený a počítač je společný. + +> Tip: Hvězdička v net user znamená dotaz na heslo. Heslo napsané za příkazem zůstane v historii příkazů. + +## Vlastní soubory +*Profil* + +- Profil je složka C:\Users\zaci. Jsou v ní Dokumenty, Plocha a Stažené soubory. +- Soubor uložený do Dokumentů tohoto účtu jiný běžný uživatel neotevře. +- Společná složka C:\Users\Public je pro věci, které mají vidět všichni. Úkoly tam nedávejte. +- Odhlášení účet zavře. Vypnutí počítače taky. Přepnutí uživatele nechá první účet otevřený v paměti. + +> Tip: Na sdíleném počítači se odhlašujte. Přepnutí uživatele nechá otevřené dokumenty v paměti. + +## Zadání: účet na papíře +*Cvičení* + +- Napište, jak by se založil běžný účet zaci: příkaz net user zaci * /add spuštěný jako správce. +- Napište heslo, které pravidlům nevyhovuje, a druhé, které má aspoň 8 znaků a není jméno účtu. Skutečné školní heslo nepište. +- Účet nesmí být ve skupině Administrators. Patří mezi běžné Users. +- Uložte ucet.txt do Skola\1-rocnik\system. Účet v systému zakládejte jen když to učitel výslovně řekne. + +> Tip: Kdo jsem, zjistí příkaz whoami. Když vypíše správce, na denní práci jste na špatném účtu. + +# Kvíz + +## Jaký má být cvičný účet zaci? +- Správce, ať jde všechno +- [x] Běžný uživatel +- Účet bez hesla +- Účet hosta sdílený celou třídou bez hesla +> Denní práce běží jako běžný uživatel. Správce je na instalaci a správu, ne na Word. + +## Jak se heslo předá příkazu net user? +- Napíše se za příkaz, ať je v sešitě vidět +- [x] Hvězdičkou, příkaz se zeptá a heslo neukáže +- Do názvu účtu +- Do souboru na plochu +> net user zaci * /add se zeptá. Heslo v příkazu zůstane v historii. + +## Proč se denně nepracuje jako správce? +- Word jinak nejde otevřít +- [x] Program, který spustíte, dostane právo měnit systém +- Správce nemá Dokumenty +- Excel jinak nepočítá +> Škodlivý program dědí vaše práva. U správce smí instalovat a měnit systém. + +## Kam patří osobní úkol? +- Do C:\Users\Public +- [x] Do Dokumentů vlastního účtu +- Do složky Windows +- Na účet spolužáka +> Public vidí každý účet na počítači. Úkol patří do vlastního profilu. diff --git a/content/programove-vybaveni/1-rocnik/06-spravce-uloh.en.md b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.en.md new file mode 100644 index 0000000..b9e7a54 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.en.md @@ -0,0 +1,6 @@ +--- +nazev: Task Manager +popis: A process, memory and startup programs. The system is not ended. +nazev_cviceni: Quiz: Task Manager +nadpis_kvizu: Pick what you may end +--- diff --git a/content/programove-vybaveni/1-rocnik/06-spravce-uloh.md b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.md new file mode 100644 index 0000000..45405b1 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/06-spravce-uloh.md @@ -0,0 +1,79 @@ +--- +id: sw-1-spravce-uloh +nazev: Správce úloh +popis: Proces, paměť a programy po spuštění. Systém se neukončuje. +nazev_cviceni: Kvíz: Správce úloh +nadpis_kvizu: Vyberte, co smíte ukončit +--- + +# Výklad + +## Okno +*Ctrl+Shift+Esc* + +- Správce úloh se otevře Ctrl+Shift+Esc. Nehledá se přes náhodné ikony na ploše. +- Karta Procesy ukáže aplikace a to, co běží na pozadí. +- Sloupec Procesor je v procentech. Sloupec Paměť je v megabajtech. +- Vysoká paměť u prohlížeče s dvaceti kartami je běžná. Vysoká paměť neznámého procesu se hlásí. + +> Tip: Když plocha nereaguje, Ctrl+Shift+Esc často projde i tehdy, když nabídka Start ne. + +## Ukončit se smí aplikace +*Ne systém* + +- Ukončit úlohu smíte u svého Wordu, který nereaguje. Rozpracovaný dokument si předtím uložte, jinak o něj přijdete. +- Proces Systém, Registr a aplikace systému Windows se neukončují. Počítač pak nemusí doběhnout do restartu. +- Neznámý proces nejdřív vyhledejte podle přesného názvu. Ukončení není první krok. +- Restart počítače je čistší než ukončit deset systémových procesů za sebou. + +> Tip: Word, který nereaguje, ukončete a otevřete znovu. Autorecover často dokument vrátí, ale nespoléhejte na něj. + +## Po spuštění +*Co startuje samo* + +- Karta Aplikace po spuštění ukáže programy, které naběhnou s Windows. +- Každý takový program bere čas startu a paměť. Ve škole jich má být málo. +- Vypnutí Teams nebo prohlížeče po spuštění, když ho učitel nechce, je v pořádku. Vypnutí zabezpečení Windows není. +- Změna se projeví po příštím přihlášení, ne vždy hned. + +> Tip: Povolené a zakázané je v posledním sloupci. Zakázaný program v počítači zůstane, jen se sám nespustí. + +## Zadání: jeden pohled +*Cvičení* + +- Otevřete Správce úloh a zapište tři aplikace, které berou nejvíc paměti. Čísla opište. +- Najděte, jestli s Windows startuje něco, co nepoznáváte. Neznámé nevypínejte, jen ho zapište. +- Systémové procesy neukončujte. Když Word nereaguje, ukončete jen Word. +- Uložte procesy.txt do Skola\1-rocnik\system. + +> Tip: Řazení podle paměti je kliknutí na záhlaví sloupce Paměť. První řádek je pak ten nejtěžší. + +# Kvíz + +## Jakou zkratkou se otevře Správce úloh? +- [x] Ctrl+Shift+Esc +- Ctrl+S +- Win+E je Správce úloh +- Alt+F4 na ploše +> Ctrl+Shift+Esc otevře Správce přímo. Win+E je Průzkumník. + +## Co smíte ukončit, když nereaguje vaše práce? +- Proces Systém +- [x] Svůj Word, který nereaguje +- Registr +- Zabezpečení Windows +> Ukončuje se zaseknutá aplikace. Systémové procesy drží počítač. + +## Co dělá vypnutí programu na kartě Po spuštění? +- Odinstaluje ho +- [x] Příště se sám nespustí s Windows +- Smaže Dokumenty +- Zakáže aktualizace +> Program v počítači zůstane. Nenaběhne jen sám při přihlášení. + +## Vysoká paměť u prohlížeče s mnoha kartami znamená co? +- Že se má hned ukončit Systém +- [x] Že prohlížeč prostě drží otevřené karty +- Že je disk NTFS rozbitý +- Že vypršela licence +> Každá karta bere paměť. Neznámý proces je jiný případ než prohlížeč, který jste sami otevřeli. diff --git a/content/programove-vybaveni/1-rocnik/07-zabezpeceni.en.md b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.en.md new file mode 100644 index 0000000..aaec038 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Securing the computer +popis: Antivirus, updates and a file that pretends to be a document. +nazev_cviceni: Quiz: security +nadpis_kvizu: Pick what stays on and what stays closed +--- diff --git a/content/programove-vybaveni/1-rocnik/07-zabezpeceni.md b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.md new file mode 100644 index 0000000..803fd77 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/07-zabezpeceni.md @@ -0,0 +1,79 @@ +--- +id: sw-1-zabezpeceni +nazev: Zabezpečení počítače +popis: Antivir, aktualizace a soubor, který se tváří jako dokument. +nazev_cviceni: Kvíz: zabezpečení +nadpis_kvizu: Vyberte, co se nezapíná a co se neotvírá +--- + +# Výklad + +## Zabezpečení Windows +*Defender* + +- Školní Windows mají Zabezpečení Windows. Antivir se nevypíná kvůli instalaci hry. +- Ochrana v reálném čase má být zapnutá. Vypnutá ochrana je nález, ne úleva. +- Brána firewall má být zapnutá na školní síti. Vypnutí kvůli jedné hře se nedělá. +- Druhý antivir vedle Defenderu se bez učitele neinstaluje. Dva se perou a oba pak hlídají hůř. + +> Tip: Zabezpečení Windows otevřete z nabídky Start. Zelené fajfky u položek jsou stav, který opisujete do úkolu. + +## Soubory a maily +*Co neotvírat* + +- Příloha faktura.pdf.exe se neotevírá, ani když text mailu zní věcně. +- Odkaz na přihlášení, který nečekáte, se neotvírá. Škola po hesle v mailu nepátrá. +- Flash disk neznámého původu se do školního počítače nestrká. Nejdřív učitel. +- Makro ve Wordu se povoluje, jen když dokument přišel ze školy a učitel řekl proč. + +> Tip: Heslo se neposílá mailem ani do chatu. Kdo ho po vás chce zprávou, nemá ho dostat. + +## Oprávnění k souboru +*NTFS* + +- Pravý klik na složku, Vlastnosti, Zabezpečení ukáže, kdo smí číst a kdo zapisovat. +- Běžný uživatel zapíše do svých Dokumentů. Do C:\Windows nezapíše. +- Odebrat si vlastní právo číst vlastní Dokumenty je chyba. Složka pak nejde otevřít. +- Společná složka Public nemá být místo, kam dáváte test s jménem. + +> Tip: Když soubor nejde uložit, často nemáte právo zápisu. Uložte ho do Dokumentů, ne do Program Files. + +## Zadání: pět kontrol +*Cvičení* + +- Opište, jestli je ochrana v reálném čase zapnutá a jestli firewall hlásí zapnuto. +- Z Windows Update opište, jestli něco čeká na restart. +- Najděte ve stažených souborech, jestli tam není soubor s dvojitou příponou. Neotvírejte ho, zapište název. +- Uložte bezpecnost.txt do Skola\1-rocnik\system. Ochranu nevypínejte. + +> Tip: Když je ochrana vypnutá, nezapínejte ji naslepo třemi kliky v neznámém okně. Řekněte to učiteli, může ji vypínat školní správa. + +# Kvíz + +## Co má být zapnuté v Zabezpečení Windows? +- Nic, antivir zpomaluje Word +- [x] Ochrana v reálném čase a firewall +- Jen spořič obrazovky +- Jen OneDrive +> Antivir i firewall jsou součást školního počítače. Nevypínají se kvůli pohodlí. + +## Který soubor neotevřete? +- poznamka.txt z vlastní složky +- [x] faktura.pdf.exe z mailu +- zadost.docx, kterou jste sami uložili +- znamky.xlsx z hodiny +> Dvojitá přípona exe je program. Mail s fakturou ho nesmí spustit. + +## Kam běžný uživatel smí uložit úkol? +- Do C:\Windows +- [x] Do svých Dokumentů +- Do složky ovladačů +- Přes cizí účet +> Dokumenty vlastního účtu jsou na zápis. Systémové složky ne. + +## Proč se vedle Defenderu bez učitele nedává druhý antivir? +- Windows umí jen jeden a druhý se vždy sám smaže +- [x] Dva antiviry se navzájem ruší +- Defender je freeware bez licence +- Excel pak neumí SUMA +> Dva programy, které hlídají stejné soubory, se blokují. Ve škole stačí Zabezpečení Windows. diff --git a/content/programove-vybaveni/1-rocnik/08-zadani-windows.en.md b/content/programove-vybaveni/1-rocnik/08-zadani-windows.en.md new file mode 100644 index 0000000..847262b --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/08-zadani-windows.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a school account and a tidy disk +popis: Folders, a standard account, updates and protection in one note. +nazev_cviceni: Quiz: the school account +nadpis_kvizu: Check the whole assignment +--- diff --git a/content/programove-vybaveni/1-rocnik/08-zadani-windows.md b/content/programove-vybaveni/1-rocnik/08-zadani-windows.md new file mode 100644 index 0000000..862c127 --- /dev/null +++ b/content/programove-vybaveni/1-rocnik/08-zadani-windows.md @@ -0,0 +1,79 @@ +--- +id: sw-1-zadani-windows +nazev: Zadání: školní účet a pořádek +popis: Složky, běžný účet, aktualizace a ochrana v jednom zápisu. +nazev_cviceni: Kvíz: školní účet +nadpis_kvizu: Zkontrolujte celé zadání +--- + +# Výklad + +## Složky +*Strom* + +- V Dokumentech je Skola\1-rocnik a v ní word, excel a system. +- Na ploše nezůstane pracovní soubor. Plocha není archiv. +- Přípony názvů jsou vidět. poznámka se nejmenuje poznámka.txt.txt. +- Názvy jsou bez znaků \ / : * ? a datum v názvu je rok-měsíc-den. + +> Tip: Cestu opište z řádku adresy. Od oka se lomítka pletou. + +## Účet +*zaci* + +- Cvičný účet zaci je běžný uživatel. Není ve skupině Administrators. +- Heslo má aspoň 8 znaků a nezadává se do příkazu, jen přes hvězdičku v net user zaci * /add. +- Do zápisu nepatří skutečné heslo. Patří tam pravidlo a příkaz bez hesla. +- whoami na denní práci nemá ukázat účet správce. + +> Tip: Účet zakládejte jen pod dohledem. V zápisu stačí postup, když učitel účet na počítači nechce. + +## Stav systému +*Tři řádky* + +- winver: edice a verze. +- Windows Update: aktuální, nebo čeká restart. Datum stačí. +- Zabezpečení Windows: ochrana v reálném čase zapnutá, firewall zapnutý. +- Ze Správce úloh tři nejtěžší aplikace podle paměti. Systémové procesy neukončené. + +> Tip: Když je něco vypnuté, zapište to jako nález. Neopravujte firewall naslepo. + +## Odevzdání +*Jeden soubor* + +- Všechno je v ucto-windows.txt ve složce Skola\1-rocnik\system. +- Jsou tam cesta ke složce word, popis účtu, verze Windows a stav ochrany. +- Není tam heslo, není tam cizí soubor a není tam vypnutý Defender. +- Soubor otevřete znovu a přečtěte ho. Cesta v textu musí po vložení do adresy opravdu vyjít. + +> Tip: Když cesta z textu skončí chybou, složka neexistuje, nebo je v cestě obyčejné lomítko. + +# Kvíz + +## Jaký účet je zaci? +- Správce +- [x] Běžný uživatel mimo skupinu Administrators +- Účet bez hesla v Public +- Sdílený účet celé třídy +> Denní účet není správce. Heslo má a do skupiny Administrators nepatří. + +## Kam se ukládá pracovní soubor? +- Na plochu +- [x] Do Skola\1-rocnik a příslušné složky +- Do C:\Windows +- Do stažených souborů bez názvu +> Plocha se nearchivuje. Strom Skola drží word, excel a system odděleně. + +## Co má být zapnuté? +- Jen spořič +- [x] Ochrana v reálném čase a firewall +- Dva antiviry +- Sdílení celé složky Users +> Defender a firewall zůstávají zapnuté. Do úkolu se opisuje jejich stav. + +## Co v souboru ucto-windows.txt být nesmí? +- Verze z winver +- [x] Skutečné heslo +- Cesta ke složce +- Stav Windows Update +> Heslo se do textového úkolu nepíše. Příkaz ho bere přes hvězdičku, ne do sešitu. diff --git a/content/programove-vybaveni/2-rocnik/01-word-styly.en.md b/content/programove-vybaveni/2-rocnik/01-word-styly.en.md new file mode 100644 index 0000000..92c3344 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/01-word-styly.en.md @@ -0,0 +1,6 @@ +--- +nazev: Word: styles +popis: Heading 1, body text and spacing that is not empty lines. +nazev_cviceni: Quiz: styles +nadpis_kvizu: Pick the style and the shortcut +--- diff --git a/content/programove-vybaveni/2-rocnik/01-word-styly.md b/content/programove-vybaveni/2-rocnik/01-word-styly.md new file mode 100644 index 0000000..b0a2f8d --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/01-word-styly.md @@ -0,0 +1,79 @@ +--- +id: sw-2-word-styly +nazev: Word: styly +popis: Nadpis 1, normální text a mezery, které nejsou prázdné řádky. +nazev_cviceni: Kvíz: styly +nadpis_kvizu: Vyberte styl a zkratku +--- + +# Výklad + +## Styl, ne ruční tučné +*Nadpis* + +- Nadpis 1 je styl. Ctrl+Alt+1 ho zapne. Nadpis 2 je Ctrl+Alt+2. +- Tučné a větší písmo místo stylu vypadá jako nadpis, ale obsah z něj Word nesestaví. +- Normální text je styl Normální. Ctrl+Shift+N ho vrátí, když jste omylem v nadpisu. +- V dokumentu jsou nejvýš tři úrovně nadpisů. Čtvrtá je už nepřehledná. + +> Tip: Podokno Styly otevřete z karty Domů. U odstavce je vidět, jaký styl opravdu má. + +## Odstavec +*Mezery ze stylu* + +- Prázdný Enter mezi odstavci se nedělá. Mezera se nastaví ve stylu, třeba 8 bodů za odstavcem. +- Řádkování školního textu je 1,15 nebo 1,5. Ne dvojité přes celou stránku bez důvodu. +- Zarovnání do bloku u delšího textu, vlevo u nadpisu a u krátkého dopisu. +- Stránka je A4. Okraje 2,5 cm ze všech stran, to je předvolba Normální. + +> Tip: Konec stránky je Ctrl+Enter. Řada Enterů na další stránku se při úpravě rozjede. + +## Soubor +*docx a pdf* + +- Pracovní soubor je .docx. Jdou v něm styly i další úpravy. +- Na odevzdání, které se už nemá měnit, je navíc PDF. +- Jednou vyexportované PDF neupravujte dál jako jediný zdroj. Opravte docx a PDF udělejte znovu. +- Název je věcný: 2026-06-12-protokol.docx. Dokument1.docx se neodevzdává. + +> Tip: Soubor, Export, Vytvořit soubor PDF. Před exportem se podívejte na náhled tisku, jestli nadpis nesedí sám na konci stránky. + +## Zadání: dvoustránkový text +*Cvičení* + +- Dokument A4, okraje 2,5 cm, aspoň tři nadpisy stylem Nadpis 1. +- Pod každým nadpisem aspoň jeden odstavec stylem Normální. Žádné prázdné Entery mezi nimi. +- Konec první stránky je Ctrl+Enter, ne deset prázdných řádků. +- Uložte protokol.docx do Skola\1-rocnik\word a k němu protokol.pdf. + +> Tip: Když v navigačním podokně nadpisy nejsou, jsou to jen tučná písma. Označte je a dejte Ctrl+Alt+1. + +# Kvíz + +## Jak se má udělat nadpis, aby z něj šel obsah? +- Tučně a 16 bodů +- [x] Stylem Nadpis 1, zkratka Ctrl+Alt+1 +- Pěti prázdnými Entery nad ním +- Obrázkem textu +> Obsah čte styly. Ruční tučné písmo v obsahu není. + +## Čím se přejde na další stránku? +- Deseti Entery +- [x] Ctrl+Enter +- Mezerou za odstavcem 80 bodů +- Smazáním okrajů +> Ctrl+Enter je konec stránky. Prázdné řádky se při doplnění textu rozjedou. + +## Jaké jsou školní okraje na A4? +- 0 cm, ať se vejde víc +- [x] 2,5 cm, předvolba Normální +- 5 cm vpravo a 0 vlevo +- Okraje se ve Wordu nenastavují +> Normální okraj je 2,5 cm. Text až do kraje papíru tiskárna často ořízne. + +## Který soubor se dál upravuje? +- PDF +- [x] docx +- Jen vytištěná stránka +- Příloha exe +> docx drží styly. PDF je kopie na odevzdání. diff --git a/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.en.md b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.en.md new file mode 100644 index 0000000..9a94510 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.en.md @@ -0,0 +1,6 @@ +--- +nazev: Word: a table, a header and a table of contents +popis: A 4 by 3 table, a page number and a contents list from the headings. +nazev_cviceni: Quiz: contents +nadpis_kvizu: Pick where the contents come from +--- diff --git a/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.md b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.md new file mode 100644 index 0000000..ce559e7 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/02-word-tabulka-a-obsah.md @@ -0,0 +1,79 @@ +--- +id: sw-2-word-obsah +nazev: Word: tabulka, záhlaví a obsah +popis: Tabulka 4×3, číslo stránky a obsah z nadpisů. +nazev_cviceni: Kvíz: obsah +nadpis_kvizu: Vyberte, odkud se obsah vezme +--- + +# Výklad + +## Tabulka +*4 řádky a 3 sloupce* + +- Tabulka se vkládá z karty Vložení. Kreslení tužkou od ruky se ve školním protokolu nedělá. +- Školní tabulka má 4 řádky a 3 sloupce: záhlaví a tři záznamy. +- Záhlaví tabulky je tučné a řádek se neopakuje jako obyčejný text pod tabulkou. +- Text v buňce se nezalamuje deseti mezerami. Šířka sloupce se táhne myší. + +> Tip: Když potřebujete řádek navíc, klepněte vpravo od řádku a dejte vložit. Enter v buňce jen zalomí text. + +## Záhlaví a zápatí +*Číslo stránky* + +- Záhlaví je nahoře, zápatí dole. Dvojklik v okraji je otevře. +- V záhlaví je název školy a název dokumentu. V zápatí je číslo stránky. +- Číslo stránky se vloží jako pole. Ručně napsaná jednička se na druhé stránce neposune. +- Záhlaví není nadpis dokumentu. Nadpis zůstává v textu jako Nadpis 1. + +> Tip: Pole čísla stránky má po označení šedé pozadí. Když je to obyčejná číslice, na další stránce se nezmění. + +## Obsah +*Z nadpisů* + +- Obsah je na kartě Reference, Obsah. Word ho složí z Nadpis 1 a Nadpis 2. +- Ručně psaný seznam stránek obsah není. Po vložení odstavce se rozejde. +- Po změně textu se obsah aktualizuje pravým klikem, Aktualizovat pole, a volbou celá tabulka. +- Obsah stojí na začátku, za titulní řádek, na vlastní stránce nebo hned pod názvem. Ne na konci. + +> Tip: Když je obsah prázdný, nadpisy nejsou styly. Ctrl+Alt+1 a obsah aktualizujte znovu. + +## Zadání: protokol s tabulkou +*Cvičení* + +- Vezměte protokol.docx. Přidejte tabulku 4×3: sloupec Jméno, Třída, Stav. +- Tři řádky: Anna Nováková, 1.A, odevzdáno. Druhý a třetí řádek doplňte vlastními jmény. +- Záhlaví s názvem školy, zápatí s číslem stránky, obsah z nadpisů na začátku. +- Uložte znovu docx a nové PDF. Staré PDF přepište, ať neexistují dvě verze. + +> Tip: Aktualizace obsahu se ptá, jestli jen čísla, nebo celá tabulka. Po nových nadpisech berte celou tabulku. + +# Kvíz + +## Jak velká je školní tabulka v tomto úkolu? +- [x] 4 řádky a 3 sloupce +- Jedna buňka +- 10 sloupců bez záhlaví +- Tabulka se ve Wordu nedělá +> Záhlaví plus tři záznamy jsou čtyři řádky. Sloupce jsou Jméno, Třída a Stav. + +## Odkud Word vezme obsah? +- Z tučných vět +- [x] Ze stylů Nadpis 1 a Nadpis 2 +- Z zápatí +- Z Excelu automaticky vždy +> Bez stylů nadpisů je obsah prázdný. Tučné písmo nestačí. + +## Jak se má dostat na stránku číslo? +- Napsat 1 do zápatí ručně +- [x] Vložit pole čísla stránky +- Dát ho do názvu souboru +- Nepsat ho, Word ho do PDF doplní sám i bez pole +> Pole se na každé stránce samo zvýší. Ruční jednička zůstane jednička. + +## Kdy se obsah aktualizuje celý, ne jen čísla stránek? +- [x] Když přibyl nadpis +- Když se jen změnila šířka okraje o milimetr a nadpisy zůstaly +- Po každém písmenu +- Obsah se neaktualizuje nikdy +> Nový nadpis do obsahu přibude jen při aktualizaci celé tabulky. diff --git a/content/programove-vybaveni/2-rocnik/03-word-dopis.en.md b/content/programove-vybaveni/2-rocnik/03-word-dopis.en.md new file mode 100644 index 0000000..6a953ff --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/03-word-dopis.en.md @@ -0,0 +1,6 @@ +--- +nazev: Word: a formal letter +popis: Sender, date, subject, greeting, body and signature on one page. +nazev_cviceni: Quiz: the letter +nadpis_kvizu: Put the parts in order +--- diff --git a/content/programove-vybaveni/2-rocnik/03-word-dopis.md b/content/programove-vybaveni/2-rocnik/03-word-dopis.md new file mode 100644 index 0000000..288e40f --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/03-word-dopis.md @@ -0,0 +1,79 @@ +--- +id: sw-2-word-dopis +nazev: Word: úřední dopis +popis: Odesílatel, datum, věc, oslovení, text a podpis na jedné straně. +nazev_cviceni: Kvíz: dopis +nadpis_kvizu: Seřaďte náležitosti +--- + +# Výklad + +## Náležitosti +*Shora dolů* + +- Vlevo nahoře odesílatel: škola, jméno, adresa. Vpravo datum 12. 6. 2026. +- Pod tím adresát. Pak řádek Věc: žádost o uvolnění z výuky. +- Oslovení, text, rozloučení a jméno s podpisem. Podpis je pod jménem, ne místo něj. +- Celý dopis je jedna strana A4. Druhá stránka s jedním řádkem je chyba. + +> Tip: Datum pište 12. 6. 2026, ne 12/6/26. Český úřední dopis má den, měsíc a rok. + +## Vzhled +*Klidný dopis* + +- Písmo jedno, třeba Calibri 11 nebo 12. V dopise není Nadpis 1 přes celou šířku. +- Řádek Věc je tučný nebo jako krátký nadpis, zbytek je Normální. +- Odstavce mají mezeru za sebou. Mezi oslovením a textem není deset Enterů. +- Okraje 2,5 cm. Text do bloku. Bez obrázku, bez tabulky, bez obsahu. + +> Tip: Když se poslední řádek podpisu sleze na druhou stránku, zkraťte mezeru za odstavcem, nepřidávejte menší okraje než 2 cm. + +## Hromadná pošta +*Až když je řádků víc* + +- Jeden dopis se píše přímo. Hromadná korespondence se vyplatí, když je adresátů aspoň deset. +- Zdroj dat je Excel s řádkem záhlaví: Jmeno, Prijmeni, Trida. +- Word na kartě Korespondence vezme tento sešit a pole vloží do oslovení. +- Sloučený výsledek se ukládá jako nový docx. Zdrojový dopis s poli si nechte zvlášť. + +> Tip: Když Excel nemá záhlaví, první žák se stane názvem sloupce a z hromadné pošty vypadne. + +## Zadání: žádost +*Cvičení* + +- Napište žádost o uvolnění z výuky na 12. 6. 2026. Adresát je ředitel školy, odesílatel jste vy. +- V dokumentu je Věc, oslovení, aspoň dvě věty důvodu a podpis. +- Jedna strana, A4, okraje 2,5 cm. Uložte zadost.docx a zadost.pdf do složky word. +- Hromadnou poštu v tomto úkolu nedělejte. Stačí jeden dopis. + +> Tip: Přečtěte dopis nahlas. Když v něm chybí, o co žádáte a který den, věta důvodu nestačí. + +# Kvíz + +## Kam patří datum v tomto dopisu? +- Dospodu místo podpisu +- [x] Dopravna, 12. 6. 2026 +- Do názvu souboru jako jediné místo +- Do zápatí místo čísla, datum v textu být nesmí +> Datum je vpravo nahoře. Podpis dole je jméno, ne datum. + +## Jak se označí, o co v dopise jde? +- [x] Řádkem Věc +- Stylem Nadpis 3 uprostřed stránky +- Obrázkem razítka +- Obsahem na druhé straně +> Věc je krátký řádek nad oslovením, třeba žádost o uvolnění z výuky. + +## Kdy se použije hromadná korespondence? +- Na jeden dopis řediteli +- [x] Když je stejný dopis pro deset a víc adresátů a data jsou v Excelu +- Na každou tabulku +- Místo PDF +> Jeden dopis se píše ručně. Hromadná pošta bere řádky z Excelu se záhlavím. + +## Jak dlouhý je školní dopis z tohoto zadání? +- [x] Jedna strana A4 +- Přesně tři strany +- Půl stránky a zbytek prázdných Enterů +- Deset stran se styly Nadpis 1 +> Žádost se vejde na jednu stranu. Druhá stránka jen s podpisem je špatně zalomená. diff --git a/content/programove-vybaveni/2-rocnik/04-excel-bunky.en.md b/content/programove-vybaveni/2-rocnik/04-excel-bunky.en.md new file mode 100644 index 0000000..17e029c --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/04-excel-bunky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: cells and formulas +popis: The address A1, a formula with an equals sign and a dollar lock. +nazev_cviceni: Quiz: cells +nadpis_kvizu: Complete the formula +--- diff --git a/content/programove-vybaveni/2-rocnik/04-excel-bunky.md b/content/programove-vybaveni/2-rocnik/04-excel-bunky.md new file mode 100644 index 0000000..9ac18a5 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/04-excel-bunky.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-bunky +nazev: Excel: buňky a vzorce +popis: Adresa A1, vzorec se rovnítkem a ukotvení dolarama. +nazev_cviceni: Kvíz: buňky +nadpis_kvizu: Doplňte vzorec +--- + +# Výklad + +## Buňka +*Adresa* + +- Sloupce jsou písmena, řádky čísla. B2 je druhý sloupec a druhý řádek. +- Do buňky patří jedna hodnota. Jméno a třída do jedné buňky nepatří. +- Číslo je číslo, text je text. Jeden apostróf před číslem z něj udělá text a přestane se sčítat. +- Sešit je soubor xlsx. List je záložka dole. Školní sešit má listy Ceník a Součet. + +> Tip: Klik na záhlaví sloupce označí celý sloupec. Na číslo řádku celý řádek. + +## Vzorec +*Rovnítko* + +- Vzorec začíná =. Bez rovnítka je B2*C2 jen text a nic nespočítá. +- V ceníku je v D2 vzorec =B2*C2. B je počet, C je cena, D je částka. +- Český Excel odděluje argumenty funkcí středníkem. Krát je hvězdička, děleno lomítko. +- Vzorec se kopíruje za úchyt v pravém dolním rohu buňky. Neopisuje se do pěti buněk ručně. + +> Tip: Když buňka ukáže vzorec místo výsledku, je sloupec úzký, nebo je buňka text. Zkontrolujte rovnítko. + +## Posun a dolar +*A1 a $A$1* + +- Zkopírování =B2*C2 o řádek níž udělá =B3*C3. Adresy se posunou. +- =$B$2 se neposune ani ve sloupci, ani v řádku. Dolar kotví. +- Sazba DPH v buňce F1 se ve vzorcích bere jako $F$1, ať kopírování neodjede na F2. +- Smazání hodnoty, na kterou vzorec ukazuje, udělá chybu. Vzorec na prázdnou buňku není nula sám od sebe, když čekal číslo a dostal text. + +> Tip: Dolar jen u písmene, $B2, kotví sloupec. Dolar jen u čísla, B$2, kotví řádek. + +## Zadání: ceník +*Cvičení* + +- List Ceník: sloupce Polozka, Pocet, Cena, Castka. Záhlaví v řádku 1, pět položek v řádcích 2 až 6. +- Castka v D2 je =B2*C2 a dolů se táhne. Žádná částka není napsaná ručně. +- Sazba v F1 je 0,21 a v E2 je =D2*$F$1. Po zkopírování dolů pořád ukazuje na F1. +- Uložte cenik.xlsx do Skola\1-rocnik\excel. + +> Tip: Klik na D2 ukáže vzorec v řádku vzorců. Když tam je jen číslo, částka je opsaná a při změně počtu se nehne. + +# Kvíz + +## Čím začíná vzorec? +- Závorkou +- [x] Rovnítkem +- Středníkem +- Dolarama bez rovnítka +> Bez = Excel nic nepočítá. Text B2*C2 zůstane text. + +## Co je v D2, když počet je v B2 a cena v C2? +- [x] =B2*C2 +- B2*C2 bez rovnítka +- =B2+C2 +- Ručně spočítané číslo +> Částka je součin. Sčítání by sečetlo počet s cenou, což není částka. + +## Proč je sazba ve vzorci $F$1? +- [x] Aby se při kopírování dolů adresa neposunula +- Protože dolar je měna tabulky +- Aby vzorec přestal počítat +- $ se v Excelu nesmí +> F1 je jedna buňka pro všechny řádky. Bez dolarů by druhý řádek četl F2. + +## Co udělá zkopírování =B2*C2 o řádek níž? +- Nechá B2 a C2 +- [x] Vznikne =B3*C3 +- Smaže list +- Změní se na text +> Relativní adresa se posune o stejný počet řádků jako kopírování. diff --git a/content/programove-vybaveni/2-rocnik/05-excel-funkce.en.md b/content/programove-vybaveni/2-rocnik/05-excel-funkce.en.md new file mode 100644 index 0000000..06d600d --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/05-excel-funkce.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: functions +popis: SUM, IF and VLOOKUP. The Czech edition separates arguments with a semicolon. +nazev_cviceni: Quiz: functions +nadpis_kvizu: Pick the right formula +--- diff --git a/content/programove-vybaveni/2-rocnik/05-excel-funkce.md b/content/programove-vybaveni/2-rocnik/05-excel-funkce.md new file mode 100644 index 0000000..67c6907 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/05-excel-funkce.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-funkce +nazev: Excel: funkce +popis: SUMA, KDYŽ a SVYHLEDAT. V české verzi se argumenty oddělují středníkem. +nazev_cviceni: Kvíz: funkce +nadpis_kvizu: Vyberte správný zápis +--- + +# Výklad + +## Souhrny +*SUMA a spol.* + +- SUMA(D2:D6) sečte částky. Průměr je PRŮMĚR, nejmenší MIN, největší MAX. +- POČET spočítá čísla. Prázdnou buňku nepočítá. Text nepočítá. +- Rozsah D2:D6 zahrnuje oba konce. D2:D6 jsou řádky 2 až 6, ne jen 2 a 6. +- Souhrn patří pod tabulku, nebo na list Součet. Nepočítá se na papír a neopisuje se. + +> Tip: Anglický Excel má SUM a čárku. Český má SUMA a středník. Ve škole pište česky, jak to má školní Office. + +## KDYŽ +*Známky* + +- České známky: 1 je nejlepší, 5 je neprospěl. Prospěch je známka 1 až 4. +- =KDYŽ(D2<=4;"prospěl";"neprospěl") vrací text podle známky v D2. +- Středníky oddělují podmínku, větev ano a větev ne. Čárka v české verzi vzorec rozbije. +- Podmínka, která čeká známku aspoň 4 jako úspěch, je obráceně. Čtyřka ještě prochází, pětka ne. + +> Tip: Uvozovky kolem textu ve funkci jsou povinné. Bez nich Excel hledá buňku nebo název. + +## SVYHLEDAT +*Ceník* + +- SVYHLEDAT najde hodnotu v levém sloupci oblasti a vrátí jiný sloupec stejného řádku. +- =SVYHLEDAT(A2;Ceník!$A$2:$C$20;3;NEPRAVDA) hledá A2, oblast je ukotvená, vrátí 3. sloupec, shoda je přesná. +- NEPRAVDA znamená přesnou shodu. PRAVDA by směla vzít nejbližší nižší a u textu plete. +- Hledaná hodnota musí být v prvním sloupci oblasti. Jinak funkce nehledá uprostřed tabulky. + +> Tip: Když funkce vrátí #N/A, hodnota v prvním sloupci není. Zkontrolujte mezeru za jménem a stejný typ, text proti číslu. + +## Zadání: známky a ceník +*Cvičení* + +- Na list Znamky dejte pět žáků, sloupec Znamka a sloupec Stav se vzorcem KDYŽ, prospěl při známce 1 až 4. +- Pod známky SUMA nedávejte, součet známek nic neříká. Dejte PRŮMĚR. +- Na listu Ceník doplňte SVYHLEDAT, který podle kódu položky vrátí cenu. Oblast ukotvěte a dejte NEPRAVDA. +- Uložte znamky.xlsx do složky excel. Oba listy ať jsou v jednom sešitu. + +> Tip: Průměr známek 1,0 až 5,0 vychází jako číslo. Když vyjde 0, počítáte prázdné buňky, nebo máte text místo čísel. + +# Kvíz + +## Jak se v českém Excelu zapíše součet D2 až D6? +- =SUM(D2:D6) +- [x] =SUMA(D2:D6) +- =SUMA(D2,D6) +- SUMA(D2:D6) bez rovnítka +> Česká funkce je SUMA a rozsah je D2:D6. SUM je anglická verze. + +## Který vzorec označí známku 5 jako neprospěl? +- [x] =KDYŽ(D2<=4;"prospěl";"neprospěl") +- =KDYŽ(D2>=4;"prospěl";"neprospěl") +- =KDYŽ(D2<=4,"prospěl","neprospěl") +- =D2<=4 +> Prochází 1 až 4, tedy menší nebo rovno 4. V češtině jsou středníky, ne čárky. + +## Co znamená NEPRAVDA na konci SVYHLEDAT? +- Vypne vzorec +- [x] Chce přesnou shodu +- Sečte sloupec +- Vrátí vždy první řádek +> NEPRAVDA je přesně ta hodnota. PRAVDA bere přibližnou shodu a u kódů položek lže. + +## Proč je oblast Ceník!$A$2:$C$20 ukotvená? +- [x] Aby se při kopírování vzorce dolů neposunula +- Protože jinak nejde uložit xlsx +- Dolar sčítá ceny +- SVYHLEDAT ukotvení zakazuje +> Každý řádek hledá v téže tabulce. Bez dolarů by se oblast sunula dolů s vzorcem. diff --git a/content/programove-vybaveni/2-rocnik/06-excel-grafy.en.md b/content/programove-vybaveni/2-rocnik/06-excel-grafy.en.md new file mode 100644 index 0000000..a159302 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/06-excel-grafy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: charts +popis: A column chart to compare, a line chart for time. A pie only for parts of a whole. +nazev_cviceni: Quiz: charts +nadpis_kvizu: Pick the chart type +--- diff --git a/content/programove-vybaveni/2-rocnik/06-excel-grafy.md b/content/programove-vybaveni/2-rocnik/06-excel-grafy.md new file mode 100644 index 0000000..4eda9dd --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/06-excel-grafy.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-grafy +nazev: Excel: grafy +popis: Sloupcový graf pro porovnání, spojnicový pro čas. Koláč jen pro podíl celku. +nazev_cviceni: Kvíz: grafy +nadpis_kvizu: Vyberte typ grafu +--- + +# Výklad + +## Který graf +*Typ* + +- Porovnání tříd nebo položek je sloupcový graf. +- Vývoj po měsících je spojnicový. Osa x je čas, zleva doprava. +- Koláč je jen pro podíly jednoho celku, nejvýš pět výsečí. Dvanáct měsíců v koláči nečtěte. +- Dva grafy přes sebe, které říkají totéž, se nedělají. + +> Tip: Když váháte, sloupec je ve škole skoro vždycky bezpečnější než koláč. + +## Co musí být vidět +*Popisky* + +- Graf má název, třeba Tržba po měsících. Název Graf 1 se neodevzdává. +- Osa hodnot má jednotku, Kč nebo počet. Osa kategorií má názvy, ne 1 až 12 bez legendy měsíce, pokud to z dat není jasné. +- Legenda se dá pryč, když je řada jen jedna. Zabírá místo a nic neříká. +- Data grafu jsou v buňkách. Namalovat sloupec obrázkem nejde aktualizovat. + +> Tip: Klik na graf, Vybrat data. Když oblast nesedí, graf kreslí i záhlaví nebo prázdné řádky. + +## Odkud bere čísla +*Tabulka* + +- Nejdřív čísla, potom graf. Graf bez zdroje je obrázek. +- Nula v buňce je nula a v grafu je vidět. Prázdná buňka čáru přeruší. +- Text v číselném sloupci graf vynechá nebo ho položí jako nulu. Čísla nepište s jednotkou Kč v téže buňce. +- Změna čísla v tabulce změní graf. Když ne, graf je kopie jako obrázek. + +> Tip: Kč patří do záhlaví sloupce nebo do názvu osy, ne za číslo do buňky. Jinak je buňka text. + +## Zadání: jeden graf +*Cvičení* + +- Na novém listu Mesice dejte leden až červen a k nim tržbu v Kč, šest čísel. +- Vložte spojnicový graf s názvem Tržba leden až červen. Jednotka ať je v názvu osy nebo v názvu grafu. +- Koláč nedělejte. Legendu jedné řady vypněte. +- Uložte do znamky.xlsx jako další list, nebo do trzba.xlsx ve složce excel. + +> Tip: Náhled tisku ať graf neroztrhne na dvě stránky. Dejte ho pod tabulku, ne přes ni. + +# Kvíz + +## Jaký graf je na tržbu leden až červen? +- Koláč se šesti výsečemi +- [x] Spojnicový +- Náhodný 3D kužel +- Obrázek sloupců vložený z Wordu +> Čas jde zleva doprava ve spojnicovém grafu. Koláč neukáže pořadí měsíců. + +## Kdy je koláč na místě? +- Na dvanáct měsíců +- [x] Na podíly jednoho celku, jen pár výsečí +- Na každou tabulku +- Když chybí název +> Koláč je celek rozdělený na pár dílů. Časová řada do něj nepatří. + +## Kam nepatří Kč? +- Do názvu osy +- [x] Do buňky za číslo, třeba 120 Kč +- Do názvu grafu +- Do záhlaví sloupce +> 120 Kč je text. Graf i SUMA pak číslo nevidí. + +## Co se stane po změně čísla v tabulce, když je graf v pořádku? +- Graf se musí nakreslit znovu ručně +- [x] Sloupec nebo bod se změní sám +- Excel graf smaže +- Změní se licence +> Graf je navázaný na buňky. Obrázek vložený místo grafu se sám nezmění. diff --git a/content/programove-vybaveni/2-rocnik/07-excel-kontingence.en.md b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.en.md new file mode 100644 index 0000000..bb01bed --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.en.md @@ -0,0 +1,6 @@ +--- +nazev: Excel: a table, a filter and a pivot +popis: A list as a table, a filter and a total by class. +nazev_cviceni: Quiz: the pivot table +nadpis_kvizu: Say what goes in rows and what goes in values +--- diff --git a/content/programove-vybaveni/2-rocnik/07-excel-kontingence.md b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.md new file mode 100644 index 0000000..8f91004 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/07-excel-kontingence.md @@ -0,0 +1,79 @@ +--- +id: sw-2-excel-kontingence +nazev: Excel: tabulka, filtr a kontingenční tabulka +popis: Seznam jako tabulka, filtr a součet podle třídy. +nazev_cviceni: Kvíz: kontingenční tabulka +nadpis_kvizu: Řekněte, co patří do řádků a co do hodnot +--- + +# Výklad + +## Tabulka +*Ctrl+T* + +- Seznam se záhlavím se označí a Ctrl+T z něj udělá tabulku. Filtr v záhlaví vznikne sám. +- Záhlaví je jeden řádek: Polozka, Trida, Castka. Dva řádky nadpisů Ctrl+T rozbije. +- Hned pod tabulku se nepíše další text. Souhrn je v řádku souhrnů, nebo na jiném listu. +- Řazení je šipka v záhlaví. Seřadit jen půlku sloupce mimo tabulku rozhodí řádky. + +> Tip: Tabulka dostane na kartě Návrh název, třeba Prodej. Pak se na ni dá odkazovat jménem. + +## Filtr +*Jen 1.A* + +- Filtr Trida = 1.A ostatní řádky schová. Nesmaže je. +- Zrušení filtru je Vybrat vše, nebo Vymazat filtr na kartě Data. +- Řádek souhrnů tabulky sečte jen to, co filtr nechá vidět. +- Obyčejná SUMA přes stejné buňky sečte i řádky, které filtr schoval. Na filtrovaný součet to není. + +> Tip: Když po filtru zmizí skoro všechno, podívejte se, jestli nemáte mezeru v názvu třídy. 1.A a 1.A s mezerou navíc jsou dvě hodnoty. + +## Kontingenční tabulka +*Součet podle třídy* + +- Kontingenční tabulka je na kartě Vložení. Zdroj je tabulka i se záhlavím. +- Do řádků přijde Trida. Do hodnot Součet z Castka. Sloupce nechte prázdné, dokud není druhý rozměr. +- Známky se v kontingenci nesčítají. U známek dejte průměr, u peněz součet. +- Kontingence patří na nový list. Když ji položíte přes zdroj, zdroj přepíšete. + +> Tip: Po změně dat dejte Obnovit. Kontingenční tabulka se sama nehne. + +## Zadání: prodej po třídách +*Cvičení* + +- List Prodej: aspoň 12 řádků, sloupce Polozka, Trida, Castka. Třídy jen 1.A a 1.B. +- Ctrl+T a název tabulky Prodej. Filtr ať umí ukázat jen 1.A a zase všechno. +- Kontingenční tabulka na novém listu: řádky Trida, hodnoty součet Castka. +- Uložte prodej.xlsx do složky Skola\1-rocnik\excel. Ruční součty vedle kontingence nepište. + +> Tip: Když kontingence ukáže Počet místo Součet, v hodnotách počítá buňky. Přepněte souhrn na Součet. + +# Kvíz + +## Co udělá Ctrl+T na seznamu se záhlavím? +- Smaže ho +- [x] Udělá z něj tabulku s filtrem +- Převede ho na PDF +- Uzamkne list heslem +> Tabulka má filtr v záhlaví a jde pojmenovat. Obyčejná oblast to nemá. + +## Co sečte jen řádky, které filtr nechá vidět? +- Obyčejná SUMA přes celou oblast +- [x] Řádek souhrnů tabulky +- Nadpis grafu +- Ručně opsaný součet +> SUMA schované řádky filtru pořád počítá. Řádek souhrnů tabulky bere jen viditelné. + +## Kam v kontingenční tabulce patří Trida a kam Castka? +- Trida do hodnot jako součet, Castka do řádků +- [x] Trida do řádků, součet Castka do hodnot +- Obojí do filtrů a nikam jinam +- Trida do názvu souboru +> Řádky jsou třídy. Hodnoty jsou součet peněz. Součet názvů tříd nedává smysl. + +## Kdy se u známek v kontingenci použije průměr? +- Vždy se známky sčítají +- [x] Když má vyjít průměrná známka, ne součet známek +- Průměr Excel neumí +- Jen u textu +> Součet známek nic neříká. Peníze se sčítají, známky se průměrují. diff --git a/content/programove-vybaveni/2-rocnik/08-zadani-office.en.md b/content/programove-vybaveni/2-rocnik/08-zadani-office.en.md new file mode 100644 index 0000000..04488f3 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/08-zadani-office.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a report and a sheet +popis: A Word letter and an Excel grade sheet in one folder. +nazev_cviceni: Quiz: the Office hand-in +nadpis_kvizu: Check both files +--- diff --git a/content/programove-vybaveni/2-rocnik/08-zadani-office.md b/content/programove-vybaveni/2-rocnik/08-zadani-office.md new file mode 100644 index 0000000..4c2a906 --- /dev/null +++ b/content/programove-vybaveni/2-rocnik/08-zadani-office.md @@ -0,0 +1,79 @@ +--- +id: sw-2-zadani-office +nazev: Zadání: protokol a tabulka +popis: Dopis ve Wordu a známky v Excelu v jedné složce. +nazev_cviceni: Kvíz: odevzdání Office +nadpis_kvizu: Zkontrolujte oba soubory +--- + +# Výklad + +## Word +*Žádost* + +- zadost.docx je jedna strana A4, okraje 2,5 cm, datum 12. 6. 2026 vpravo nahoře. +- Je v ní řádek Věc, oslovení, aspoň dvě věty a podpis. Není v ní obsah ani tabulka. +- K protokolu z dřívějška, pokud ho máte, patří styly Nadpis 1 a PDF. +- PDF se jmenuje stejně jako docx. Dvě verze final a final2 se neodevzdávají. + +> Tip: Náhled tisku má ukázat jednu stránku. Podpis na druhé stránce znamená moc velké mezery. + +## Excel +*Známky* + +- znamky.xlsx má list Znamky: jméno, známka, stav. +- Stav je vzorec =KDYŽ(D2<=4;"prospěl";"neprospěl") nebo se sloupcem známky, který opravdu máte. Známka 5 je neprospěl. +- Pod tabulkou je PRŮMĚR známek, ne jejich součet. +- Čísla jsou čísla. Text 1 nebo 120 Kč ve stejné buňce se nepočítá. + +> Tip: Klik na buňku stavu musí v řádku vzorců ukázat KDYŽ. Opsané slovo prospěl se při změně známky nehne. + +## Kontrola vzorců +*Ne opis* + +- V ceníku, pokud ho přikládáte, je částka =B2*C2 a sazba ukotvená jako $F$1. +- SVYHLEDAT, pokud ho máte, končí na NEPRAVDA a oblast je ukotvená. +- Graf tržby je spojnicový, má název a není to koláč dvanácti měsíců. +- Kontingenční tabulka, pokud je v sešitu, stojí na vlastním listu a sčítá částky po třídách. + +> Tip: Stačí žádost a známky. Ceník, graf a kontingence jsou navíc, ale když je přiložíte, musí sedět. + +## Složka +*Dva hlavní soubory* + +- Ve složce Skola\1-rocnik\word je zadost.docx a zadost.pdf. +- Ve složce Skola\1-rocnik\excel je znamky.xlsx. +- Na ploše tyto soubory nejsou. Přípony jsou vidět. +- Otevřete oba znovu. Datum, věc a aspoň jedna pětka se stavem neprospěl mají být vidět. + +> Tip: Když PDF nejde otevřít, export nedoběhl. Udělejte ho z docx znovu. + +# Kvíz + +## Jaký je školní dopis z tohoto zadání? +- Tři strany se styly a obsahem +- [x] Jedna strana A4 s řádkem Věc a datem 12. 6. 2026 +- Jen tabulka 4×3 +- Prezentace +> Žádost je jeden list. Obsah a Nadpis 1 patří protokolu, ne tomuto dopisu. + +## Který vzorec označí pětku jako neprospěl? +- [x] =KDYŽ(D2<=4;"prospěl";"neprospěl") +- =KDYŽ(D2>=4;"prospěl";"neprospěl") +- napsané slovo prospěl +- =SUMA(D2) +> Prochází známka 1 až 4. Pětka padne do druhé větve. V češtině jsou středníky. + +## Co patří pod sloupec známek? +- Součet známek +- [x] Průměr +- Kontingenční součet peněz +- Nic, známky se mažou +> Součet známek nemá význam. PRŮMĚR ano. + +## Které soubory se odevzdávají jako základ? +- Jen PDF bez docx +- [x] zadost.docx, zadost.pdf a znamky.xlsx +- Jen xlsx na ploše +- prezentace a video +> Word se odevzdává i jako zdroj, i jako PDF. Excel je xlsx, protože v PDF by nebyly vzorce. diff --git a/content/programove-vybaveni/3-rocnik/01-databaze.en.md b/content/programove-vybaveni/3-rocnik/01-databaze.en.md new file mode 100644 index 0000000..e466ba7 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/01-databaze.en.md @@ -0,0 +1,6 @@ +--- +nazev: A database +popis: A table, a record, a key. When a sheet is no longer enough. +nazev_cviceni: Quiz: databases +nadpis_kvizu: Name the key and the reason for a database +--- diff --git a/content/programove-vybaveni/3-rocnik/01-databaze.md b/content/programove-vybaveni/3-rocnik/01-databaze.md new file mode 100644 index 0000000..bcda465 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/01-databaze.md @@ -0,0 +1,79 @@ +--- +id: sw-3-databaze +nazev: Databáze +popis: Tabulka, záznam, klíč. Kdy už nestačí Excel. +nazev_cviceni: Kvíz: databáze +nadpis_kvizu: Poznejte klíč a důvod databáze +--- + +# Výklad + +## Excel nestačí +*Vztahy* + +- Excel je výborný na jeden seznam. Jakmile se třída opakuje na každém řádku žáka, začne se název třídy rozcházet. +- Databáze drží věci v samostatných tabulkách a spojuje je klíčem. +- Tabulka tridy má název třídy jednou. Tabulka zaci se na ni jen odkáže. +- Access i MySQL umí totéž v principu. Access je soubor accdb, MySQL je server a příkazy SQL. + +> Tip: Když opravujete 1.A na 1. A v padesáti řádcích Excelu, má to být tabulka tříd, ne opis. + +## Řádek a sloupec +*Záznam a pole* + +- Řádek je záznam, třeba jeden žák. Sloupec je pole, třeba prijmeni. +- Každá hodnota je atomická. Jméno a příjmení nepatří do jedné buňky. +- Primární klíč je hodnota, která se v tabulce neopakuje. U žáka je to id, ne příjmení. +- Cizí klíč je pole, které ukazuje na primární klíč jiné tabulky. trida_id v tabulce zaci ukazuje na id v tridy. + +> Tip: Dva Nováci se smějí jmenovat stejně. Jejich id je různé. Proto příjmení klíč není. + +## Typy +*Číslo, text, datum* + +- id je číslo. Jméno je krátký text. Datum narození je datum, ne text 12.6. +- Ano/ne je samostatný typ, ne text ano. +- Do číselného pole nepatří jednotka. Počet žáků je 28, ne 28 žáků. +- Prázdné pole a nula nejsou totéž. Nula je známá hodnota, prázdno je nevíme. + +> Tip: Text v číselném poli se nepodaří uložit, nebo se sečte špatně. Typ zvolte při založení sloupce. + +## Zadání: návrh na papír +*Cvičení* + +- Navrhněte dvě tabulky: tridy (id, nazev) a zaci (id, jmeno, prijmeni, trida_id). +- U každého pole napište typ. id v obou tabulkách je primární klíč. +- Napište dva řádky tříd, 1.A a 1.B, a tři žáky, z toho Annu Novákovou ve třídě 1.A. +- Soubor navrh.txt uložte k databázím. Access ani MySQL ještě neotevírejte, dokud návrh nesedí. + +> Tip: Šipka vede od trida_id k id tabulky tridy. Opačně by žák byl klíčem třídy, což není. + +# Kvíz + +## Co je primární klíč žáka? +- Příjmení +- [x] id, které se neopakuje +- Název třídy +- První sloupec, ať je v něm cokoli +> Příjmení se opakuje. id jednoho žáka ne. + +## Co je trida_id v tabulce zaci? +- Primární klíč třídy v téže tabulce +- [x] Cizí klíč na tabulku tridy +- Text 1.A opsaný do každého řádku +- Heslo +> Cizí klíč drží jen číslo třídy. Název třídy žije v tabulce tridy jednou. + +## Kdy už je Excel na seznam žáků těsný? +- Jakmile má víc než jeden sloupec +- [x] Když se údaj, třeba název třídy, opakuje a musí zůstat stejný +- Excel nestačí nikdy +- Jakmile je soubor xlsx +> Opakovaný název se rozjede. Databáze ho má v jedné tabulce a ostatní se na ni odkážou. + +## Čím se liší nula a prázdné pole? +- Ničím +- [x] Nula je hodnota, prázdné pole znamená, že hodnota chybí +- Prázdné pole je vždy nula +- Nula je text +> Průměr nulu počítá. Prázdnou buňku často ne. Typ i význam se liší. diff --git a/content/programove-vybaveni/3-rocnik/02-access-tabulky.en.md b/content/programove-vybaveni/3-rocnik/02-access-tabulky.en.md new file mode 100644 index 0000000..3ee054f --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/02-access-tabulky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Access: tables and a relationship +popis: An AutoNumber, a foreign key and a one-to-many link without a cascade. +nazev_cviceni: Quiz: Access +nadpis_kvizu: Pick the key type and the cascade +--- diff --git a/content/programove-vybaveni/3-rocnik/02-access-tabulky.md b/content/programove-vybaveni/3-rocnik/02-access-tabulky.md new file mode 100644 index 0000000..c92654e --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/02-access-tabulky.md @@ -0,0 +1,79 @@ +--- +id: sw-3-access-tabulky +nazev: Access: tabulky a vztah +popis: Automatické číslo, cizí klíč a vztah 1:N bez kaskády. +nazev_cviceni: Kvíz: Access +nadpis_kvizu: Vyberte typ klíče a kaskádu +--- + +# Výklad + +## Soubor +*accdb* + +- Databáze Accessu je jeden soubor accdb. Jmenuje se evidence.accdb. +- Tabulku založíte v návrhovém zobrazení, ne jen v datovém listu. V návrhu je vidět typ. +- Tabulka tridy má pole id a nazev. Tabulka zaci má id, jmeno, prijmeni a trida_id. +- Název pole je bez mezery a bez diakritiky: trida_id. Popisek pro lidi se dá nastavit zvlášť. + +> Tip: Datový list je na zapisování řádků. Typ pole se mění v návrhu. Když už v tabulce data jsou, změna typu může selhat. + +## Klíče +*Automatické číslo* + +- id je typ Automatické číslo a je to primární klíč. Access ho doplňuje sám: 1, 2, 3. +- trida_id je typ Číslo, dlouhé celé číslo. Není to automatické číslo. Píšete ho vy, nebo ho vybere vztah. +- nazev, jmeno a prijmeni jsou Krátký text. Délka 50 stačí. +- Dva primární klíče v jedné tabulce školního cvičení nejsou. Stačí id. + +> Tip: Když trida_id necháte jako text, vztah na číselné id třídy Access odmítne. Typy obou konců musí sedět. + +## Vztah 1:N +*Bez kaskády* + +- Jedna třída má víc žáků. Žák má jednu třídu. To je vztah 1:N. +- Ve vztazích přetáhnete tridy.id na zaci.trida_id. +- Zaškrtněte vynucení referenční integrity. Žák se nesmí odkázat na třídu, která neexistuje. +- Kaskádové odstranění nechte vypnuté. Smazání třídy by jinak smazalo i žáky. + +> Tip: Na straně tridy je jednička, na straně zaci je znak nekonečna. Když je to obráceně, táhli jste vztah špatným směrem. + +## Zadání: evidence.accdb +*Cvičení* + +- Založte evidence.accdb s tabulkami tridy a zaci podle návrhu. +- Vložte třídy 1.A a 1.B. Vložte Annu Novákovou s trida_id třídy 1.A a ještě dva žáky. +- Vztah 1:N s referenční integritou, bez kaskádového odstranění. +- Zkuste uložit žáka s trida_id 99. Access ho má odmítnout. Pak to číslo vraťte na platnou třídu. + +> Tip: Když jde uložit trida_id 99, integrita není zapnutá. Otevřete vztahy a vztah vytvořte znovu. + +# Kvíz + +## Jaký typ má id žáka? +- Krátký text, který píšete ručně +- [x] Automatické číslo a primární klíč +- Ano/ne +- Dlouhý text s celým jménem třídy +> Automatické číslo žák nedoplňuje. Access ho dá samo a v tabulce se neopakuje. + +## Jaký typ má trida_id? +- Automatické číslo +- [x] Číslo, dlouhé celé číslo +- Datum +- Ano/ne +> Je to cizí klíč na id třídy, které je taky číslo. Automatické číslo by si vymýšlelo vlastní hodnoty. + +## Co dělá referenční integrita? +- Smaže žáky při smazání třídy +- [x] Nedovolí žáka s třídou, která neexistuje +- Schová tabulku +- Převede accdb na xlsx +> Odkaz musí vést na skutečný řádek. Třída 99 bez řádku v tridy projít nemá. + +## Proč nechat kaskádové odstranění vypnuté? +- Access ho neumí +- [x] Smazání třídy by smazalo i její žáky +- Jinak nejde uložit accdb +- Kvůli Excelu +> Třída a žáci jsou různá data. Smazat třídu omylem a přijít o žáky je špatná pojistka pro školní cvičení. diff --git a/content/programove-vybaveni/3-rocnik/03-access-dotazy.en.md b/content/programove-vybaveni/3-rocnik/03-access-dotazy.en.md new file mode 100644 index 0000000..721c282 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/03-access-dotazy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Access: queries +popis: A query that shows only class 1.A, and a criterion in quotes. +nazev_cviceni: Quiz: queries +nadpis_kvizu: Fill in the criterion +--- diff --git a/content/programove-vybaveni/3-rocnik/03-access-dotazy.md b/content/programove-vybaveni/3-rocnik/03-access-dotazy.md new file mode 100644 index 0000000..679b605 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/03-access-dotazy.md @@ -0,0 +1,79 @@ +--- +id: sw-3-access-dotazy +nazev: Access: dotazy +popis: Dotaz, který ukáže jen třídu 1.A, a kritérium v uvozovkách. +nazev_cviceni: Kvíz: dotazy +nadpis_kvizu: Doplňte kritérium +--- + +# Výklad + +## Dotaz není kopie +*Pohled* + +- Dotaz data nekopíruje do nové tabulky. Přečte je a ukáže výsledek. +- Když ve třídě přibude žák, stejný dotaz ho příště ukáže taky. +- Dotaz se dělá v návrhovém zobrazení. Tabulky se přidají a pole se přetáhnou dolů. +- Uložený dotaz se jmenuje zaci_1A. Dotaz1 se neodevzdává. + +> Tip: Když dotaz otevřete a data změníte, měníte zdrojovou tabulku. Na prohlížení to vadit nemusí, na omyl ano. + +## Kritérium +*1.A* + +- Pod pole nazev z tabulky tridy patří kritérium "1.A" včetně uvozovek. +- Bez uvozovek Access text nepozná a dotaz selže, nebo se zeptá na parametr. +- Číselné kritérium uvozovky nemá. id 1 je prostě 1. +- Chcete-li žáky obou tříd, kritérium 1.A tam nedávejte. Filtr, který tam zbyl z minula, výsledek ořeže. + +> Tip: Řádek Kritéria je filtr. Řádek Zobrazit schová sloupec, který v dotazu být musí, ale ve výsledku ho nechcete. + +## Spojení v dotazu +*Obě tabulky* + +- Do dotazu přidejte tridy i zaci. Access použije vztah, který už máte. +- Ven dejte prijmeni, jmeno a nazev třídy. id ven dávat nemusíte. +- Řazení podle prijmeni je vzestupně. V návrhové mřížce je na to řádek Řadit. +- Žák bez třídy se při vnitřním spojení neukáže. Díky integritě takový řádek stejně nemá vzniknout. + +> Tip: Když se každý žák ukáže dvakrát, tabulky nejsou spojené a Access udělal všechny kombinace. Vraťte vztah. + +## Zadání: jen 1.A +*Cvičení* + +- Dotaz zaci_1A ukáže jméno, příjmení a název třídy, jen pro 1.A, seřazené podle příjmení. +- Anna Nováková v něm je. Žák z 1.B v něm není. +- Uložte dotaz do evidence.accdb. Výsledek nekopírujte do Excelu jako jediný výstup. +- Změňte v tabulce příjmení Anny na Novák a dotaz otevřete znovu. Má ukázat Novák. + +> Tip: Když se zeptá na parametr 1.A, kritérium nemá uvozovky a Access ho bere jako otázku. + +# Kvíz + +## Kam patří podmínka "1.A"? +- Do názvu souboru +- [x] Do řádku Kritéria pod polem nazev +- Do primárního klíče místo id +- Do kaskádového odstranění +> Kritéria filtrují řádky. Pole nazev drží text 1.A. + +## Proč jsou u 1.A uvozovky? +- [x] Access jinak text pochopí jako parametr nebo chybu +- Protože 1.A je číslo +- Uvozovky se v Accessu nepoužívají +- Aby se zapnula kaskáda +> Textové kritérium se píše v uvozovkách. Číslo ne. + +## Co se stane s dotazem, když do třídy 1.A přidáte žáka? +- Dotaz je stará kopie a žáka neukáže +- [x] Po novém otevření ho ukáže +- Access databázi zavře +- Musíte ho opsat do Excelu +> Dotaz čte tabulky. Není to zmrazený export. + +## Proč se žák v dotazu objeví dvakrát u každé třídy? +- Má dva primární klíče +- [x] Tabulky v dotazu nejsou spojené vztahem +- Je seřazený podle příjmení +- Filtr je vypnutý +> Bez spojení Access zkombinuje každý řádek s každým. Vztah tomu zabrání. diff --git a/content/programove-vybaveni/3-rocnik/04-access-formulare.en.md b/content/programove-vybaveni/3-rocnik/04-access-formulare.en.md new file mode 100644 index 0000000..b3cd488 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/04-access-formulare.en.md @@ -0,0 +1,6 @@ +--- +nazev: Access: a form and a report +popis: Entry through a form and printing through a report. The datasheet is not for users. +nazev_cviceni: Quiz: forms +nadpis_kvizu: Pick what is for entry and what is for print +--- diff --git a/content/programove-vybaveni/3-rocnik/04-access-formulare.md b/content/programove-vybaveni/3-rocnik/04-access-formulare.md new file mode 100644 index 0000000..37f9b48 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/04-access-formulare.md @@ -0,0 +1,79 @@ +--- +id: sw-3-access-formulare +nazev: Access: formulář a sestava +popis: Zápis přes formulář a tisk přes sestavu. Datový list není pro uživatele. +nazev_cviceni: Kvíz: formulář +nadpis_kvizu: Vyberte, čím se data zadávají a čím tisknou +--- + +# Výklad + +## Formulář +*Jeden žák* + +- Formulář ukáže jeden záznam jako políčka. Datový list ukáže mřížku a tam se běžný uživatel snadno trefí do špatného řádku. +- Formulář Zaci se založí z tabulky zaci. Pole id ať je vidět, ale zamčené. Automatické číslo se nepíše. +- trida_id je lépe rozbalovací seznam, který ukazuje název třídy a ukládá id. +- Formulář se jmenuje frmZaci. Výchozí Formulář1 se neodevzdává. + +> Tip: Rozbalovací seznam bere řádky z tabulky tridy. Viditelný sloupec je nazev, vázaný sloupec je id. + +## Sestava +*Tisk* + +- Sestava je na tisk a na PDF. Formulář je na zápis. Neslouží k tomu totéž. +- Sestava žáků je seskupená podle třídy a v rámci třídy seřazená podle příjmení. +- Záhlaví skupiny ukáže 1.A jednou, ne na každém řádku jako jediný nadpis celé sestavy. +- Do sestavy nepatří tlačítka formuláře. Patří do ní nadpis Evidence žáků a číslo stránky. + +> Tip: Náhled sestavy je před tiskem povinný. Prázdná druhá stránka znamená velkou zápatí nebo prázdný oddíl. + +## Kdo kam smí +*Ne do návrhu* + +- Uživatel, který jen zapisuje žáky, dostane formulář. Návrhové zobrazení ne. +- Mazání záznamu na formuláři je vypnuté, dokud učitel neřekne jinak. +- Sestava se needituje. Když v ní přepíšete jméno, často přepisujete data, a to z sestavy dělat nechcete. +- Záloha je kopie accdb před mazáním. Jediný soubor na ploše záloha není. + +> Tip: Soubor, Uložit jako, a datum v názvu: evidence-2026-06-12.accdb. Pak teprve mažete zkušební řádky. + +## Zadání: formulář a sestava +*Cvičení* + +- frmZaci zapíše jméno, příjmení a třídu. id nejde přepsat. +- Sestava seskupí žáky podle třídy. Anna Nováková je pod 1.A. +- Z formuláře přidejte jednoho žáka do 1.B a v sestavě ho najděte. +- Uložte vše do evidence.accdb. PDF sestavy je evidence-zaci.pdf vedle souboru. + +> Tip: Když v rozbalovacím seznamu vidíte čísla místo 1.A, zobrazujete id. Přepněte šířku sloupců, ať je vidět nazev. + +# Kvíz + +## Čím se ve škole zadává nový žák? +- Přímo v návrhovém zobrazení tabulky +- [x] Formulářem frmZaci +- Přepsáním sestavy +- Excelem místo Accessu bez uložení accdb +> Formulář ukáže jeden záznam a umí seznam tříd. Návrh tabulky je pro autora databáze. + +## K čemu je sestava? +- K mazání sloupců +- [x] K tisku a PDF, seskupenému podle třídy +- K nastavení automatického čísla +- K instalaci Accessu +> Sestava formátuje výstup. Zápis zůstává formuláři. + +## Proč je id na formuláři zamčené? +- [x] Automatické číslo se nepíše ručně +- Access id neumí ukázat +- Jinak nejde sestava +- Protože je to text +> Číslo přidělí Access. Ruční přepsání by rozbilo klíč. + +## Co uděláte před mazáním zkušebních řádků? +- Nic, accdb se sám verzuje +- [x] Kopie souboru s datem v názvu +- Smažete vztah +- Export jen do JPG +> Jedna kopie accdb je záloha. Mazání bez ní nejde vzít zpět tak snadno jako Ctrl+Z ve Wordu. diff --git a/content/programove-vybaveni/3-rocnik/05-mysql-select.en.md b/content/programove-vybaveni/3-rocnik/05-mysql-select.en.md new file mode 100644 index 0000000..4e04663 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/05-mysql-select.en.md @@ -0,0 +1,6 @@ +--- +nazev: MySQL: SELECT +popis: Choosing columns, a WHERE condition and a sort. Text sits in single quotes. +nazev_cviceni: Quiz: SELECT +nadpis_kvizu: Pick the right statement +--- diff --git a/content/programove-vybaveni/3-rocnik/05-mysql-select.md b/content/programove-vybaveni/3-rocnik/05-mysql-select.md new file mode 100644 index 0000000..2595d1b --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/05-mysql-select.md @@ -0,0 +1,79 @@ +--- +id: sw-3-mysql-select +nazev: MySQL: SELECT +popis: Výběr sloupců, podmínka WHERE a řazení. Text je v jednoduchých uvozovkách. +nazev_cviceni: Kvíz: SELECT +nadpis_kvizu: Vyberte správný příkaz +--- + +# Výklad + +## Databáze skola +*Dvě tabulky* + +- Na serveru je databáze skola. Tabulka tridy má id a nazev. Tabulka zaci má id, jmeno, prijmeni a trida_id. +- Příkazy se píšou v MySQL, ne do Excelu. Středník příkaz ukončí. +- Nejdřív USE skola; ať další příkazy nemíří do jiné databáze. +- Heslo k serveru do sešitu nepatří. Patří učiteli a do klienta, který ho neukazuje v příkazu. + +> Tip: Když klient hlásí, že tabulka neexistuje, jste v jiné databázi, nebo je v názvu překlep. SHOW TABLES; ukáže, co tam je. + +## SELECT +*Jen to, co potřebujete* + +- SELECT prijmeni, jmeno FROM zaci; vrátí dva sloupce všech žáků. +- Hvězdička SELECT * FROM zaci; je na průzkum. Do odevzdaného dotazu vyberte sloupce jménem. +- WHERE trida_id = 1 nechá jen jednu třídu. Číslo je bez uvozovek. +- Text se píše v jednoduchých uvozovkách: WHERE nazev = '1.A'. + +> Tip: Dvojité uvozovky kolem 1.A záleží na režimu serveru. Ve škole pište jednoduché, ty platí spolehlivě. + +## Pořadí +*ORDER BY* + +- ORDER BY prijmeni; seřadí vzestupně, tedy A až Ž. +- ORDER BY prijmeni DESC; je sestupně. +- WHERE patří před ORDER BY. Obrácené pořadí příkazů server odmítne. +- LIMIT 5 vrátí nejvýš pět řádků. Na kontrolu ano, na úplný seznam třídy ne. + +> Tip: Nejdřív FROM, pak WHERE, pak ORDER BY. SELECT je na začátku, i když se sloupce vybírají jako první v hlavě. + +## Zadání: tři dotazy +*Cvičení* + +- Do souboru dotazy.sql napište USE skola; a tři SELECT. +- První: příjmení a jméno všech žáků podle příjmení. +- Druhý: jen žáci s trida_id = 1. +- Třetí: název třídy 1.A z tabulky tridy. Text '1.A' je v jednoduchých uvozovkách. + +> Tip: Každý příkaz ukončete středníkem. Bez něj klient čeká na pokračování. + +# Kvíz + +## Který příkaz vrátí příjmení žáků třídy s id 1? +- [x] SELECT prijmeni FROM zaci WHERE trida_id = 1; +- SELECT prijmeni WHERE trida_id = 1; +- SELECT * FROM tridy WHERE nazev = 1.A; +- WHERE trida_id = 1; +> SELECT říká sloupce, FROM tabulku, WHERE podmínku. Číslo je bez uvozovek. + +## Jak se v podmínce zapíše název třídy? +- nazev = 1.A +- [x] nazev = '1.A' +- nazev = "bez uvozovek a bez čísla" +- nazev := 1.A +> Text patří do jednoduchých uvozovek. Bez nich to není řetězec. + +## Kam patří ORDER BY? +- Před SELECT +- [x] Za WHERE +- Místo FROM +- Do názvu tabulky +> Pořadí je WHERE, potom ORDER BY. SELECT zůstává první. + +## K čemu je ve škole SELECT * ? +- Na odevzdaný tisk všech osobních údajů +- [x] Na rychlý pohled, co v tabulce je +- Místo primárního klíče +- Na smazání řádků +> Hvězdička je průzkum. Odevzdaný dotaz jmenuje sloupce, které mají být vidět. diff --git a/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.en.md b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.en.md new file mode 100644 index 0000000..a00bb9c --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.en.md @@ -0,0 +1,6 @@ +--- +nazev: MySQL: INSERT, UPDATE and DELETE +popis: A new row, a change and a delete. An UPDATE or DELETE without WHERE is not written in class. +nazev_cviceni: Quiz: changing data +nadpis_kvizu: Recognise the safe statement +--- diff --git a/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.md b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.md new file mode 100644 index 0000000..63fc270 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/06-mysql-zmeny.md @@ -0,0 +1,79 @@ +--- +id: sw-3-mysql-zmeny +nazev: MySQL: INSERT, UPDATE a DELETE +popis: Nový řádek, oprava a smazání. UPDATE a DELETE bez WHERE se ve škole nepíšou. +nazev_cviceni: Kvíz: změny dat +nadpis_kvizu: Poznejte bezpečný příkaz +--- + +# Výklad + +## INSERT +*Nový žák* + +- INSERT INTO zaci (jmeno, prijmeni, trida_id) VALUES ('Anna', 'Nováková', 1); přidá jeden řádek. +- Sloupce v závorce a hodnoty za VALUES musí být ve stejném pořadí a stejném počtu. +- id s automatickým číslem do výčtu nedávejte, server ho doplní. +- Jeden INSERT, jeden žák. Balit pět žáků do nepřehledného příkazu ve cvičení nemusíte. + +> Tip: Když server hlásí neznámý sloupec, název nesedí s tabulkou. SHOW COLUMNS FROM zaci; ukáže pravdu. + +## UPDATE +*Jen jeden řádek* + +- UPDATE zaci SET trida_id = 2 WHERE id = 15; přepíše třídu jen žáku 15. +- Bez WHERE by stejný příkaz přepsat třídu všem. Ve škole se neodevzdává. +- Nejdřív SELECT s stejnou podmínkou WHERE. Až když vidíte jeden správný řádek, pusťte UPDATE. +- SET mění hodnoty. Není to SELECT a nic nevrací kromě počtu ovlivněných řádků. + +> Tip: Klient ukáže, kolik řádků se změnilo. Když vidíte desítky a čekali jste jeden, zastavte se a volejte učitele. Záloha se hodí předtím, ne potom. + +## DELETE +*Také s WHERE* + +- DELETE FROM zaci WHERE id = 15; smaže jeden řádek. +- DELETE FROM zaci; bez WHERE smaže všechny žáky. To není čištění tabulky, to je ztráta dat. +- Smazaný řádek Ctrl+Z v MySQL nevrátí. Vrátí ho záloha, nebo ho vložíte znovu, když znáte hodnoty. +- Mazat třídu, na kterou ještě ukazují žáci, referenční pravidlo nemá dovolit. Nejdřív žáci, nebo třídu nemazat. + +> Tip: Před DELETE udělejte SELECT se stejnou podmínkou. Počet řádků musí být ten, který čekáte, obvykle jeden. + +## Zadání: tři změny na kopii +*Cvičení* + +- Na cvičných datech vložte žáka Petr Svoboda do třídy 1, tedy trida_id 1. +- UPDATE mu změňte třídu na 2, ale jen přes jeho id, které jste předtím přečetli SELECTem. +- Téhož žáka DELETE smažte zase jen podle id. Ostatní žáci v tabulce zůstanou. +- Tři příkazy připojte do dotazy.sql pod komentář, který říká, že UPDATE a DELETE mají WHERE. + +> Tip: Komentář v SQL začíná dvěma pomlčkami. Do příkazu se nepočítá, příštímu čtenáři řekne, proč tam WHERE je. + +# Kvíz + +## Který příkaz přidá Annu do třídy 1? +- [x] INSERT INTO zaci (jmeno, prijmeni, trida_id) VALUES ('Anna', 'Nováková', 1); +- SELECT Anna INTO zaci; +- UPDATE zaci SET Anna; +- DELETE FROM zaci WHERE jmeno = 'Anna'; +> INSERT vkládá řádek. Sloupce a hodnoty drží pořadí. + +## Jak se změní třída jen žáku s id 15? +- UPDATE zaci SET trida_id = 2; +- [x] UPDATE zaci SET trida_id = 2 WHERE id = 15; +- SELECT trida_id = 2; +- INSERT INTO zaci VALUES (15); +> Bez WHERE by se třída přepsala všem. WHERE id = 15 drží jeden řádek. + +## Co udělá DELETE FROM zaci; bez WHERE? +- Nesmaže nic, WHERE je volitelné a server se zeptá +- [x] Smaže všechny řádky v zaci +- Smaže jen poslední řádek +- Jen vypíše žáky +> Bez podmínky je cílem celá tabulka. Ve školním cvičení takový příkaz nepište. + +## Co uděláte těsně před UPDATE? +- DELETE bez WHERE +- [x] SELECT se stejnou podmínkou, ať vidíte ty řádky +- DROP tabulky +- Vypnete integritu +> SELECT ukáže, na koho UPDATE dopadne. Až pak má smysl stejné WHERE použít ke změně. diff --git a/content/programove-vybaveni/3-rocnik/07-mysql-join.en.md b/content/programove-vybaveni/3-rocnik/07-mysql-join.en.md new file mode 100644 index 0000000..b97432f --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/07-mysql-join.en.md @@ -0,0 +1,6 @@ +--- +nazev: MySQL: joining tables +popis: An INNER JOIN on trida_id, and why the class name is not repeated on every row. +nazev_cviceni: Quiz: JOIN +nadpis_kvizu: Complete the join +--- diff --git a/content/programove-vybaveni/3-rocnik/07-mysql-join.md b/content/programove-vybaveni/3-rocnik/07-mysql-join.md new file mode 100644 index 0000000..62e5a1b --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/07-mysql-join.md @@ -0,0 +1,79 @@ +--- +id: sw-3-mysql-join +nazev: MySQL: spojení tabulek +popis: INNER JOIN přes trida_id a proč se název třídy neopakuje v každém řádku. +nazev_cviceni: Kvíz: JOIN +nadpis_kvizu: Doplňte spojení +--- + +# Výklad + +## Jedna pravda +*Název jen jednou* + +- Název 1.A je v tabulce tridy jednou. V zaci je jen trida_id. +- Když by název ležel i u žáka, oprava překlepu znamená projít všechny žáky. +- To je důvod dvou tabulek. Excel to umí jen ručně, databáze vztahem. +- JOIN tabulky spojí ve výsledku dotazu. Nesloučí je do jedné natrvalo. + +> Tip: Po JOIN pořád existují dvě tabulky. Výsledek dotazu je jen pohled pro tento příkaz. + +## INNER JOIN +*Shoda klíčů* + +- INNER JOIN vrátí řádky, kde se klíče sejdou. Žák a jeho třída. +- ON z.trida_id = t.id je podmínka spojení. Není to totéž co WHERE na název. +- Bez ON server buď příkaz odmítne, nebo spojí každý řádek s každým. +- Přezdívky z a t jsou v dotazu proto, aby bylo jasné, která tabulka je která. + +> Tip: WHERE t.nazev = '1.A' přijde až po spojení. Nejdřív se tabulky potkají, pak se filtruje. + +## Celý příkaz +*Jak má vypadat* + +- SELECT z.prijmeni, z.jmeno, t.nazev FROM zaci AS z INNER JOIN tridy AS t ON z.trida_id = t.id; +- WHERE t.nazev = '1.A' z toho udělá jen jednu třídu. +- ORDER BY z.prijmeni výsledek seřadí. +- Sloupce pište s přezdívkou tabulky. Samotné id by bylo nejasné, mají ho obě tabulky. + +> Tip: Když příkaz hlásí nejednoznačný sloupec id, napište z.id nebo t.id. Server nehádá. + +## Zadání: jeden JOIN +*Cvičení* + +- Do dotazy.sql přidejte dotaz, který vypíše příjmení, jméno a název třídy. +- Jen třída 1.A, seřazené podle příjmení. +- Anna Nováková v něm je včetně textu 1.A, ne jen čísla 1. +- Příkaz spusťte dvakrát. Výsledek má být stejný, JOIN data nemění. + +> Tip: Když vidíte číslo místo 1.A, vybíráte trida_id a ne t.nazev. Doplňte sloupec z tabulky tridy. + +# Kvíz + +## Které spojení ukáže žáka i název jeho třídy? +- SELECT * FROM zaci; +- [x] SELECT z.prijmeni, t.nazev FROM zaci AS z INNER JOIN tridy AS t ON z.trida_id = t.id; +- DELETE FROM tridy; +- INSERT INTO tridy VALUES ('1.A'); +> JOIN vezme příjmení z žáků a název z tříd tam, kde se id shoduje. + +## Co patří do ON? +- [x] z.trida_id = t.id +- t.nazev = '1.A' +- ORDER BY z.prijmeni +- USE skola +> ON je shoda klíčů. Filtr na název 1.A je WHERE, ne spojení. + +## Proč není název třídy uložený u každého žáka? +- MySQL to neumí +- [x] Aby se název měnil na jednom místě +- Protože text v MySQL nejde uložit +- Aby nešel JOIN +> Jedna pravda v tabulce tridy. Žáci drží jen číslo. + +## Co JOIN udělá s uloženými tabulkami? +- Sloučí je natrvalo do jedné +- [x] Jen složí výsledek dotazu, tabulky zůstanou dvě +- Smaže cizí klíče +- Převede je na Excel +> Výsledek je odpověď na příkaz. tridy a zaci na serveru zůstávají. diff --git a/content/programove-vybaveni/3-rocnik/08-zadani-databaze.en.md b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.en.md new file mode 100644 index 0000000..b69e293 --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a student register +popis: Classes, students, a relationship and one query that shows 1.A by name. +nazev_cviceni: Quiz: the register +nadpis_kvizu: Walk the keys and the query +--- diff --git a/content/programove-vybaveni/3-rocnik/08-zadani-databaze.md b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.md new file mode 100644 index 0000000..d3a812b --- /dev/null +++ b/content/programove-vybaveni/3-rocnik/08-zadani-databaze.md @@ -0,0 +1,79 @@ +--- +id: sw-3-zadani-databaze +nazev: Zadání: evidence žáků +popis: Třídy, žáci, vztah a jeden dotaz, který ukáže 1.A jménem. +nazev_cviceni: Kvíz: evidence +nadpis_kvizu: Projděte klíče i dotaz +--- + +# Výklad + +## Struktura +*Dvě tabulky* + +- tridy: id jako klíč, nazev. Řádky 1.A a 1.B. +- zaci: id jako klíč, jmeno, prijmeni, trida_id jako cizí klíč. +- Anna Nováková je ve třídě 1.A. Ve třídě 1.B je aspoň jeden jiný žák. +- Název 1.A není opsaný v tabulce žáků. Je jen v tridy. + +> Tip: Ať děláte Access, nebo MySQL, tahle dvě pole a vztah jsou povinné. Nástroj je obal. + +## Access +*Když odevzdáváte accdb* + +- evidence.accdb má vztah 1:N, referenční integritu a vypnutou kaskádu. +- Dotaz zaci_1A a formulář frmZaci. Sestava je seskupená podle třídy. +- Žák s neexistující třídou nejde uložit. Vyzkoušeno na trida_id 99. +- PDF sestavy leží vedle accdb. + +> Tip: Když učitel otevře jen accdb, formulář a dotaz v něm musí být. Samotné PDF bez souboru nestačí. + +## MySQL +*Když odevzdáváte SQL* + +- dotazy.sql obsahuje SELECT se WHERE, INSERT, UPDATE i DELETE, oba měnící příkazy s WHERE. +- JOIN vypíše příjmení a název 1.A, seřazené podle příjmení. +- V souboru není heslo k serveru a není DELETE bez WHERE. +- Příkazy jdou spustit znovu jen tam, kde to dává smysl. DELETE testovacího žáka v souboru má konkrétní id a komentář. + +> Tip: Odevzdává se text příkazů, ne snímek obrazovky klienta. Snímek smí být navíc. + +## Jedno odevzdání +*Ne obě poloviční* + +- Stačí jeden nástroj, Access nebo MySQL, ale celý: struktura, data, dotaz na 1.A. +- V dotazu je Anna vidět jako 1.A, ne jako holé číslo, pokud je součástí této části zadání JOIN nebo dotaz s názvem. +- Záloha souboru má datum v názvu, třeba evidence-2026-06-12.accdb. +- Do textu úkolu nepište heslo a nepište, že jste kaskádu zapnuli, pokud jste ji zapnout neměli. + +> Tip: Když umíte obojí, odevzdejte jedno pořádně. Dvě rozdělané databáze jsou horší než jedna hotová. + +# Kvíz + +## Kde leží text 1.A? +- V každém řádku žáka jako jediná pravda +- [x] V tabulce tridy, v poli nazev +- V názvu souboru jako jediném místě +- V hesle +> Žák má jen trida_id. Název je v tabulce tříd jednou. + +## Jaký vztah mají třídy a žáci? +- [x] 1:N, jedna třída a víc žáků +- N:N bez třetí tabulky uprostřed školního cvičení +- Žádný, stačí Excel bez klíče +- 1:1, každý žák vlastní tabulku třídy +> Jedna třída, mnoho žáků. Na straně žáků je cizí klíč. + +## Co musí mít UPDATE a DELETE v odevzdaném SQL? +- Žádnou podmínku, ať je příkaz krátký +- [x] WHERE, které trefí jen zamýšlené řádky +- Hvězdičku místo sloupců +- Kaskádu v příkazu SELECT +> Bez WHERE se mění nebo maže celá tabulka. Ve cvičení se to neodevzdává. + +## Co Access odmítne, když je integrita zapnutá? +- Žáka se třídou 1.A +- [x] Žáka s trida_id 99, které v tridy není +- Formulář +- Sestavu podle příjmení +> Cizí klíč musí ukazovat na existující třídu. 99 v tabulce tříd není. diff --git a/content/programove-vybaveni/4-rocnik/01-wordpress.en.md b/content/programove-vybaveni/4-rocnik/01-wordpress.en.md new file mode 100644 index 0000000..d8adbc0 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/01-wordpress.en.md @@ -0,0 +1,6 @@ +--- +nazev: WordPress: pages and posts +popis: A school site, a static page and a dated news item. The system is not hand-written HTML for every sentence. +nazev_cviceni: Quiz: WordPress +nadpis_kvizu: Tell a page from a post +--- diff --git a/content/programove-vybaveni/4-rocnik/01-wordpress.md b/content/programove-vybaveni/4-rocnik/01-wordpress.md new file mode 100644 index 0000000..de527c6 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/01-wordpress.md @@ -0,0 +1,79 @@ +--- +id: sw-4-wordpress +nazev: WordPress: stránky a příspěvky +popis: Web školy, statická stránka a datovaná zpráva. Systém není ručně psané HTML každé věty. +nazev_cviceni: Kvíz: WordPress +nadpis_kvizu: Rozlište stránku a příspěvek +--- + +# Výklad + +## Co to je +*Správa obsahu* + +- WordPress je systém pro web. Text se píše v administraci, ne do každé stránky jako do samostatného souboru ve Wordu. +- Školní web se jmenuje Školní den. Název se nastaví v Nastavení, Obecné. +- Administrace je cesta /wp-admin/ na adrese webu. Bez účtu tam nejste. +- Veřejná stránka a administrace jsou dva pohledy. Návštěvník administraci nevidí. + +> Tip: Když se po uložení na webu nic nezměnilo, často jste jen uložili koncept. Zveřejnit je samostatný krok. + +## Stránka a příspěvek +*Dva druhy* + +- Stránka je trvalá: O škole, Kontakt. Nemá datum jako hlavní údaj. +- Příspěvek je zpráva: co se stalo 12. 6. 2026. Řadí se od nejnovějšího. +- Školní den má aspoň jednu stránku O škole a jeden příspěvek o dni otevřených dveří. +- Příspěvek není stránka s jiným názvem. V menu se s nimi pracuje jinak. + +> Tip: V administraci jsou Příspěvky a Stránky dvě samostatné položky. Když novinku přidáte jako stránku, na výpisu zpráv nebude. + +## Adresa +*Trvalý odkaz* + +- Trvalý odkaz v Nastavení, Trvalé odkazy, ať je název příspěvku, ne ?p=123. +- Adresa pak nese slova, třeba /den-otevrenych-dveri/. +- Jednou zveřejněnou adresu neměňte jen kvůli stylu. Odkaz, který už někdo má, by se rozbil. +- Název webu a adresa příspěvku jsou dvě věci. Název je Školní den, adresa je konkrétní článek. + +> Tip: Když škola už odkazy rozeslala, změnu trvalých odkazů nechte na učiteli. Cvičný web si klidně nastavte hned na začátku. + +## Zadání: dvě věci na webu +*Cvičení* + +- Na cvičném webu nastavte název Školní den. +- Zveřejněte stránku O škole, aspoň dvě věty, bez data jako hlavní osy. +- Zveřejněte příspěvek Den otevřených dveří s datem 12. 6. 2026. +- Do souboru web.txt napište adresu stránky a adresu příspěvku. Heslo do souboru nepište. + +> Tip: Koncept se na veřejné stránce neukáže. U obou má být stav Publikováno. + +# Kvíz + +## Čím se liší stránka od příspěvku? +- Ničím, je to stejné tlačítko +- [x] Stránka je trvalá, příspěvek je datovaná zpráva +- Příspěvek nejde zveřejnit +- Stránka musí být v Excelu +> O škole je stránka. Novinka s datem je příspěvek. + +## Kam se nastaví název webu Školní den? +- Do názvu počítače +- [x] Do Nastavení, Obecné +- Do buňky A1 +- Do příkazu DELETE +> Název webu je nastavení WordPressu, ne název souboru Windows. + +## Jaký trvalý odkaz ve cvičení chcete? +- ?p=123 natrvalo +- [x] Název příspěvku ve adrese +- Jen číslo id žáka +- Adresu z Accessu +> Adresa se slovy se čte a pamatuje. Číselný parametr ne. + +## Co napsat do web.txt? +- Heslo správce +- [x] Adresy zveřejněné stránky a příspěvku +- Celou databázi MySQL +- Soubor accdb +> Úkol dokládá, že stránky jsou venku. Heslo do textového souboru nepatří. diff --git a/content/programove-vybaveni/4-rocnik/02-wordpress-role.en.md b/content/programove-vybaveni/4-rocnik/02-wordpress-role.en.md new file mode 100644 index 0000000..bee86a9 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/02-wordpress-role.en.md @@ -0,0 +1,6 @@ +--- +nazev: WordPress: users and appearance +popis: An author writes their own news, an editor also edits others. There is one administrator. +nazev_cviceni: Quiz: roles +nadpis_kvizu: Match the role +--- diff --git a/content/programove-vybaveni/4-rocnik/02-wordpress-role.md b/content/programove-vybaveni/4-rocnik/02-wordpress-role.md new file mode 100644 index 0000000..a4d5f49 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/02-wordpress-role.md @@ -0,0 +1,79 @@ +--- +id: sw-4-wordpress-role +nazev: WordPress: uživatelé a vzhled +popis: Autor píše své zprávy, šéfredaktor i cizí. Správce je jeden. +nazev_cviceni: Kvíz: role +nadpis_kvizu: Přiřaďte roli +--- + +# Výklad + +## Role +*Nejmíň práv* + +- Správce instaluje doplňky, mění šablonu a zakládá uživatele. Ve třídě je jeden, účet učitele. +- Šéfredaktor zveřejňuje a upravuje i cizí příspěvky a stránky. Správce to být nemusí. +- Autor zveřejňuje vlastní příspěvky. Cizí ne. Stránky O škole nezakládá. +- Spolužák, který jen přidává novinky, dostane roli Autor. Ne správce. + +> Tip: Přihlášení každého člověka je vlastní účet. Heslo se nepůjčuje, ani když jde o školní cvičení. + +## Šablona a doplňky +*Málo* + +- Šablona je vzhled. Ve cvičení stačí jedna, třeba výchozí, kterou učitel určí. +- Doplněk přidává funkci. Každý doplněk je další věc, která se musí aktualizovat. +- Doplněk z neznámého webu se neinstaluje. Stačí jeden překlep a web běží s cizím kódem. +- Aktualizace WordPressu, šablony a doplňků se dělají v klidný den a po záloze, ne minutu před dnem otevřených dveří. + +> Tip: Neaktivní doplněk, který nepoužíváte, smažte. Vypnutý pořád leží na disku a pořád se může hodit útočníkovi, když v něm je chyba. + +## Menu +*Co návštěvník vidí* + +- Menu má položku O škole na stránku, ne na koncept. +- Příspěvky se často vypisují samy na úvodu. Do menu se nemusí přidávat každý. +- Zkouškový příspěvek s textem test test do veřejného menu nepatří. Smažte ho, nebo ho nechte jako koncept. +- Název v menu a nadpis stránky ať říkají totéž. O škole a O nas jsou dvě stránky, ne jedna. + +> Tip: Náhled v anonymním okně ukáže web bez vašeho přihlášení. To je pohled návštěvníka. + +## Zadání: účet autora +*Cvičení* + +- Založte uživatele redakce s rolí Autor. Heslo mu nastavte mimo sešit, do web.txt ho nepište. +- Ověřte, že tento účet neumí instalovat doplňky. Když umí, je to správce a roli změňte. +- Šablonu neměňte na náhodnou z internetu. Nechte tu, kterou web už má, nebo tu od učitele. +- Do web.txt dopište přihlašovací jméno autora a že role je Autor. Ne heslo. + +> Tip: Uživatelé, Všichni uživatelé, sloupec Role. Autor tam musí být napsaný, nestačí, že to tak bylo v úmyslu. + +# Kvíz + +## Jakou roli dostane spolužák, který jen píše novinky? +- Správce +- [x] Autor +- Jediný účet s vaším heslem +- Roli MySQL +> Autor zveřejní vlastní příspěvek a nesahá na doplňky ani na cizí texty. + +## Kdo smí instalovat doplňky? +- Každý návštěvník +- [x] Správce +- Autor +- Kdokoli s odkazem na článek +> Doplňky mění celý web. To je práce správce, ne autora zprávy. + +## Proč se doplněk nestahuje z náhodného webu? +- WordPress doplňky neumí +- [x] Je to cizí kód, který běží na školním webu +- Protože pak nejde napsat stránku +- Doplňky jsou vždycky v Excelu +> Doplněk je program. Neznámý zdroj do školního webu nepatří. + +## Co do web.txt o účtu redakce nepatří? +- Přihlašovací jméno +- Role Autor +- [x] Heslo +- To, že účet nezvládne instalovat doplňky +> Heslo do textového úkolu nepište. Jméno a role ano. diff --git a/content/programove-vybaveni/4-rocnik/03-power-bi.en.md b/content/programove-vybaveni/4-rocnik/03-power-bi.en.md new file mode 100644 index 0000000..fb41b75 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/03-power-bi.en.md @@ -0,0 +1,6 @@ +--- +nazev: Power BI: data from Excel +popis: One workbook, one table, a chart by class. The same data is not loaded twice. +nazev_cviceni: Quiz: Power BI +nadpis_kvizu: Pick the source and the visual +--- diff --git a/content/programove-vybaveni/4-rocnik/03-power-bi.md b/content/programove-vybaveni/4-rocnik/03-power-bi.md new file mode 100644 index 0000000..e3c9d21 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/03-power-bi.md @@ -0,0 +1,79 @@ +--- +id: sw-4-power-bi +nazev: Power BI: data z Excelu +popis: Jeden sešit, jedna tabulka, graf podle třídy. Stejná data se nenačítají dvakrát. +nazev_cviceni: Kvíz: Power BI +nadpis_kvizu: Vyberte zdroj a vizuál +--- + +# Výklad + +## Soubor +*pbix* + +- Power BI Desktop ukládá práci do pbix. Není to xlsx a není to obrázek grafu. +- Na odevzdání vedle pbix patří PDF stránky sestavy, ať ji otevře i člověk bez Power BI. +- Zdroj je prodej.xlsx, tabulka Prodej se sloupci Polozka, Trida, Castka. +- Načtení je Načíst data, Excel, a vybere se tabulka Prodej, ne celý sešit naslepo dvakrát. + +> Tip: Když v modelu vidíte Prodej a Prodej (2), tabulka se načetla dvakrát. Jednu odeberte, než postavíte graf. + +## Model +*Jedna řádka, jedna skutečnost* + +- Každý řádek je jeden prodej: položka, třída, částka. Součet v tomtéž sloupci navíc řádek souhrnu být nemá. +- Řádek souhrnů z Excelu do Power BI nepatří. Součet si spočítá vizuál. +- Trida je text 1.A a 1.B. Castka je číslo bez Kč v buňce. +- Když Excel opravíte, v Power BI dejte Obnovit. Sám se o uložení xlsx nedozví. + +> Tip: Sloupec, který Power BI bere jako text, se nesčítá. Často je v něm mezera nebo Kč. V Excelu ho opravte a obnovte. + +## Vizuál +*Sloupec a karta* + +- Sloupcový graf: osa Trida, hodnota součet Castka. Porovnání dvou tříd. +- Karta ukáže jedno číslo, celkový součet Castka. +- Koláč pro dvě třídy je zbytečný. Pro dvanáct měsíců byl špatně už v Excelu. +- Průřez, anglicky slicer, na pole Trida nechte na příští lekci. Teď ať graf ukáže obě třídy. + +> Tip: Když graf ukáže počet řádků místo součtu korun, v hodnotách je Počet. Přepněte souhrn na Součet. + +## Zadání: první pbix +*Cvičení* + +- prodej.pbix načte tabulku Prodej jednou. +- Na stránce jsou sloupcový graf podle třídy a karta s celkovou částkou. +- Obnovte data potom, co v Excelu změníte jednu částku. Karta se musí pohnout. +- Uložte pbix do složky Skola\1-rocnik\excel a vedle něj prodej.pdf. + +> Tip: PDF je snímek sestavy. pbix je práce, ve které jde vizuál opravit. Odevzdávají se oba. + +# Kvíz + +## Jaký soubor je pracovní v Power BI? +- xlsx jako jediný +- [x] pbix +- pdf jako jediný +- accdb +> pbix drží model a vizuály. Excel je zdroj. PDF je náhled na odevzdání. + +## Jak se mají načíst data prodeje? +- [x] Tabulka Prodej jednou +- Tentýž list dvakrát +- Jako obrázek grafu z Excelu +- Přepisem do Wordu +> Jedna tabulka v modelu. Druhá kopie téhož zdroje mate součty. + +## Jaký vizuál porovná částku tříd 1.A a 1.B? +- Koláč s dvanácti díly +- [x] Sloupcový graf, osa Trida, součet Castka +- Karta bez popisku a bez čísla +- Průřez místo grafu jako jediný prvek +> Dvě kategorie a jejich součet jsou sloupce. Karta je jen jedno číslo celkem. + +## Co uděláte v Power BI potom, co změníte číslo v Excelu? +- Nic, pbix se mění sám v tu vteřinu +- [x] Obnovit +- Smazat pbix a začít ve Wordu +- Převést Castka na text +> Power BI si data při načtení vezme. Další změna Excelu se projeví až po obnovení. diff --git a/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.en.md b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.en.md new file mode 100644 index 0000000..29027c6 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.en.md @@ -0,0 +1,6 @@ +--- +nazev: Power BI: a slicer and a report +popis: A class slicer filters the whole page. A filter on one visual leaves the others. +nazev_cviceni: Quiz: the slicer +nadpis_kvizu: Say what the slicer affects +--- diff --git a/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.md b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.md new file mode 100644 index 0000000..c2e7f08 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/04-power-bi-prurez.md @@ -0,0 +1,79 @@ +--- +id: sw-4-power-bi-prurez +nazev: Power BI: průřez a sestava +popis: Průřez třídy přefiltruje celou stránku. Filtr jednoho vizuálu ostatní nechá. +nazev_cviceni: Kvíz: průřez +nadpis_kvizu: Řekněte, co průřez ovlivní +--- + +# Výklad + +## Průřez +*Celá stránka* + +- Průřez na pole Trida je ovládací prvek na plátně. Čtenář klikne na 1.A. +- Sloupcový graf i karta se mají změnit. Celková částka pak není celá škola, ale jen 1.A. +- Když se karta nehne, průřez na ni není napojený, nebo karta bere jinou tabulku. +- Výběr se zruší gumou na průřezu, nebo volbou všech tříd. Data se tím nesmažou. + +> Tip: Průřez filtruje. Není to nový součet uložený do Excelu. + +## Filtr vizuálu +*Jen jeden graf* + +- Filtr v podokně konkrétního vizuálu ovlivní jen ten vizuál. +- Školní sestava pro porovnání tříd filtr vizuálu nepotřebuje. Má ukázat obě, dokud čtenář nezvolí průřez. +- Filtr stránky schová třídu všem vizuálům na stránce, i bez klikání. Na odevzdání ho nenechávejte zapnutý omylem. +- Než sestavu vyexportujete, zrušte výběr průřezu, pokud má PDF ukázat celou školu. + +> Tip: PDF zachytí průřez tak, jak je zrovna vybraný. Vybraná 1.A v PDF už druhou třídu neukáže. + +## Stránka +*Jedna otázka* + +- Stránka se jmenuje Prodej. Název Stránka 1 se neodevzdává. +- Nadpis na plátně říká Prodej po třídách. Graf má popisky tříd. +- Na jedné stránce jsou nejvýš průřez, jeden graf a jedna karta. Druhý graf se stejným obsahem je navíc. +- Barvy nechte klidné. Červená a zelená těsně vedle sebe se špatně čte. + +> Tip: Zobrazte sestavu na projektoru, nebo aspoň v celém okně. Drobný popisek, který na vašem monitoru ujde, vzadu ve třídě zanikne. + +## Zadání: stránka s průřezem +*Cvičení* + +- Na stránku Prodej přidejte průřez Trida. +- Klik na 1.A změní graf i kartu. Po zrušení výběru je zase vidět celý součet. +- PDF vyexportujte bez vybrané třídy, ať ukazuje 1.A i 1.B. +- pbix uložte znovu. V modelu je tabulka Prodej pořád jen jednou. + +> Tip: Když průřez nabízí prázdný řádek, v Excelu je prázdný řádek tabulky. Smažte ho ve zdroji a obnovte. + +# Kvíz + +## Co má průřez na Trida změnit? +- Jen barvu pozadí +- [x] Graf i kartu na stránce +- Soubor xlsx na disku +- Heslo k WordPressu +> Průřez filtruje vizuály na stránce. Karta s celkovou částkou se musí změnit taky. + +## Čím se liší filtr jednoho vizuálu od průřezu? +- Ničím +- [x] Filtr vizuálu platí jen pro něj, průřez je ovládání pro stránku +- Filtr vizuálu smaže Excel +- Průřez nejde zrušit +> Průřez je na plátně pro čtenáře. Filtr v podokně vizuálu ostatní grafy nechá. + +## Jaké PDF se odevzdává, když mají být vidět obě třídy? +- S vybranou jen 1.A +- [x] Bez výběru v průřezu +- Prázdná stránka +- Jen karta bez grafu a bez tříd +> PDF drží aktuální výběr. Než ho uděláte, výběr zrušte. + +## Jak se má stránka jmenovat? +- Stránka 1 +- [x] Prodej +- pbix +- DELETE +> Název říká, co na stránce je. Výchozí Stránka 1 se neodevzdává. diff --git a/content/programove-vybaveni/4-rocnik/05-batch.en.md b/content/programove-vybaveni/4-rocnik/05-batch.en.md new file mode 100644 index 0000000..bbfeb4d --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/05-batch.en.md @@ -0,0 +1,6 @@ +--- +nazev: Batch files +popis: A bat file that creates the school folders and deletes nothing. +nazev_cviceni: Quiz: batch +nadpis_kvizu: Pick the safe command +--- diff --git a/content/programove-vybaveni/4-rocnik/05-batch.md b/content/programove-vybaveni/4-rocnik/05-batch.md new file mode 100644 index 0000000..b110c71 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/05-batch.md @@ -0,0 +1,79 @@ +--- +id: sw-4-batch +nazev: Dávkové soubory +popis: Soubor bat, který založí složky školy a nic nemaže. +nazev_cviceni: Kvíz: batch +nadpis_kvizu: Vyberte bezpečný příkaz +--- + +# Výklad + +## Soubor bat +*Příkazy za sebou* + +- Dávkový soubor je text s příponou bat. Příkazový řádek ho spustí řádek po řádku. +- První řádek cvičení je @echo off, ať se příkazy nevypisují podruhé do okna. +- rem je komentář. Do příkazu se nepočítá. +- Soubor se jmenuje slozky.bat a leží v Dokumentech, ne na cizím flash disku, kterému nerozumíte. + +> Tip: Dvojklik spustí bat v té složce, kde soubor je. Neznámý bat z mailu se nespouští, stejně jako pdf.exe. + +## Složky +*mkdir* + +- mkdir založí složku. Když už existuje, příkaz ohlásí chybu a pokračuje se dál, složku nesmaže. +- set "ROOT=%USERPROFILE%\Documents\Skola" uloží cestu. %USERPROFILE% je složka účtu, u každého jiná. +- mkdir "%ROOT%\1-rocnik\word" založí i mezistupně, když je napíšete takto v nových Windows. Když ne, založte nejdřív 1-rocnik a pak word. +- Uvozovky kolem cesty jsou povinné v okamžiku, kdy v cestě je mezera. V Documents mezera není, v názvu účtu být může. + +> Tip: echo Hotovo vypíše větu. pause nechá okno otevřené, ať výsledek přečtete. Bez pause dvojklik okno hned zavře. + +## Co se nepouští +*del* + +- del smaže soubor. Ve školním bat na založení složek žádný del není. +- Příkaz, který maže podle masky celý disk, se nepíše ani z legrace. Obnova není Ctrl+Z. +- cd /d přejde na jiný disk a složku. Než něco mažete, echo vypíše, kde jste. V tomto cvičení nemažete. +- Bat, kterému nerozumíte řádek po řádku, nespouštějte. Krátký soubor se čte celý. + +> Tip: Když chcete zkusit del, zkuste ho na jeden soubor test.txt, který jste sami právě vytvořili. Ne na složku Windows. + +## Zadání: slozky.bat +*Cvičení* + +- slozky.bat nastaví ROOT na %USERPROFILE%\Documents\Skola a založí 1-rocnik\word, 1-rocnik\excel a 1-rocnik\system. +- Na konci je echo s cestou a pause. Není tam del. +- Spusťte ho jednou. Složky v Průzkumníku opravdu jsou. +- Spusťte ho podruhé. Smí ohlásit, že složka už existuje. Nemá nic smazat. + +> Tip: Úprava bat je v Poznámkovém bloku, ne ve Wordu. Word do něj doplní formátování a příkazový řádek pak řádek nepochopí. + +# Kvíz + +## Co má být první řádek školního bat? +- del C:\ +- [x] @echo off +- formát disku +- heslo účtu +> @echo off jen ztiší opisování příkazů. Nic nemaže. + +## Co dělá mkdir? +- Smaže složku +- [x] Založí složku +- Otevře Word +- Změní heslo +> mkdir je make directory. Mazání je jiný příkaz a v tomto cvičení není. + +## Kam ukazuje %USERPROFILE%? +- Na stejnou složku na všech účtech počítače +- [x] Na profil přihlášeného účtu +- Na web WordPressu +- Na tabulku tridy +> Každý účet má vlastní profil. Stejný bat tedy založí Skola v Dokumentech toho, kdo ho spustil. + +## Který bat ve cvičení nespouštíte? +- Ten, který jen zakládá složky a na konci čeká +- [x] Neznámý soubor z mailu +- slozky.bat, který jste sami přečetli +- Soubor, který ohlásí, že složka už existuje +> Dávka je program. Z mailu se nespouští, stejně jako soubor s příponou exe. diff --git a/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.en.md b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.en.md new file mode 100644 index 0000000..840ca87 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.en.md @@ -0,0 +1,6 @@ +--- +nazev: Users and groups in Windows +popis: The zaci account is in Users. It is not in Administrators. +nazev_cviceni: Quiz: groups +nadpis_kvizu: Put the account in the right group +--- diff --git a/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.md b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.md new file mode 100644 index 0000000..75a3c74 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/06-uzivatele-a-skupiny.md @@ -0,0 +1,79 @@ +--- +id: sw-4-uzivatele +nazev: Uživatelé a skupiny ve Windows +popis: Účet zaci je ve Users. Ve skupině Administrators není. +nazev_cviceni: Kvíz: skupiny +nadpis_kvizu: Zařaďte účet do skupiny +--- + +# Výklad + +## Skupina +*Práva najednou* + +- Skupina sdružuje účty, které mají mít stejná práva. Neměníte právo každému účtu zvlášť po jednom souboru. +- Users jsou běžní uživatelé. Administrators smí měnit systém a instalovat. +- Účet zaci patří do Users. Do Administrators ne. +- Nový účet je často v Users sám. Práce navíc je nestrkat ho mezi správce, ne dopisovat Users. + +> Tip: Kdo je správce, vypíše net localgroup Administrators. Jméno zaci v tom seznamu být nemá. + +## Příkazy +*Bez hesla v řádku* + +- net user zaci * /add účet založí a na heslo se zeptá. Hvězdička znamená dotaz, ne heslo zapsané do bat. +- net user zaci vypíše účet. Je tam vidět, jestli je aktivní. +- net localgroup Users zaci /add ho přidá do Users, kdyby tam nebyl. +- net localgroup Administrators zaci /delete ho ze správců odebere, pokud tam omylem je. Příkaz chce spustit správce. + +> Tip: lusrmgr.msc otevře stejné účty v okně, ale jen ve Windows Pro, Education a Enterprise. V edici Home toto okno není a zůstává net user. + +## Kdo příkaz spouští +*Správce* + +- Běžný uživatel účet nezaloží. Okno se musí spustit jako správce, nebo systém požádá o souhlas. +- Souhlas správce není důvod založit účet rovnou jako správce. zaci zůstává běžný. +- whoami na účtu zaci nesmí skončit na Administrators. +- Cvičný účet se po hodině dá odebrat: net user zaci /delete. Nejdřív si ověřte, že nemaže účet učitele. + +> Tip: Jméno účtu zaci je cvičné. Účet, který se jmenuje jako učitel nebo správce, se nedeaktivuje a nemaže. + +## Zadání: zápis, ne cizí účty +*Cvičení* + +- Do ucty.txt napište příkaz, kterým se zaci založí, včetně hvězdičky místo hesla. +- Napište, v které skupině má být a ve které být nemá. +- Pokud učitel dovolí, účet opravdu založte a net localgroup Administrators opište. zaci v seznamu není. +- Heslo do ucty.txt nepište. Cizí účty na počítači neměňte. + +> Tip: Když lusrmgr.msc hlásí, že neexistuje, máte edici Home. Použijte net user a do zápisu napište, že grafické okno v této edici není. + +# Kvíz + +## Do které skupiny patří zaci? +- Administrators +- [x] Users +- Do žádné, účet bez skupiny smí instalovat +- Do skupiny hesel +> Běžná práce je skupina Users. Správci jsou Administrators. + +## Jak se účet založí, aniž by heslo zůstalo v příkazu? +- [x] net user zaci * /add +- net user zaci heslo123 /add +- mkdir zaci +- DELETE FROM zaci +> Hvězdička se zeptá. Heslo napsané v příkazu zůstane v historii. + +## Kde lusrmgr.msc není? +- Ve Windows Pro +- [x] Ve Windows Home +- Ve Windows Education +- Ve Windows Enterprise +> Grafická správa účtů je v Pro, Education a Enterprise. Home ji nemá. + +## Co whoami nesmí ukázat při denní práci na účtu zaci? +- Že jde o běžného uživatele +- [x] Že účet je ve skupině Administrators +- Název počítače +- To, že účet existuje +> Denní účet není správce. whoami to prozradí dřív, než něco nainstalujete. diff --git a/content/programove-vybaveni/4-rocnik/07-zasady.en.md b/content/programove-vybaveni/4-rocnik/07-zasady.en.md new file mode 100644 index 0000000..a14b512 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/07-zasady.en.md @@ -0,0 +1,6 @@ +--- +nazev: Local policy +popis: A minimum password of 8 characters and a lock after 300 seconds. A school domain overrides the local policy. +nazev_cviceni: Quiz: policy +nadpis_kvizu: Fill in the value and the command +--- diff --git a/content/programove-vybaveni/4-rocnik/07-zasady.md b/content/programove-vybaveni/4-rocnik/07-zasady.md new file mode 100644 index 0000000..f850445 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/07-zasady.md @@ -0,0 +1,79 @@ +--- +id: sw-4-zasady +nazev: Místní zásady +popis: Minimální heslo 8 znaků a zámek po 300 sekundách. Doména školy místní zásadu přebije. +nazev_cviceni: Kvíz: zásady +nadpis_kvizu: Doplňte hodnotu a příkaz +--- + +# Výklad + +## Kde se zásady berou +*gpedit* + +- Místní zásady otevře gpedit.msc. Je ve Windows Pro, Education a Enterprise, v edici Home není. +- Konfigurace počítače platí pro stroj. Konfigurace uživatele platí pro účty, které se přihlásí. +- Školní cvičení mění dvě věci: délku hesla a zámek při nečinnosti. Ostatní zásady nechte být. +- Když je počítač v doméně školy, doménová zásada vyhraje nad místní. Místní změna se pak jakoby ztratí a není to chyba klikání. + +> Tip: Než něco změníte, napište si původní hodnotu. Vrátit Neznámé číslo zpět nejde, když jste si ho nezapsali. + +## Heslo +*Osm znaků* + +- Cesta vede přes Konfigurace počítače, Nastavení systému Windows, Nastavení zabezpečení, Zásady účtů, Zásady hesel. +- Minimální délka hesla se nastaví na 8. +- Nula znamená, že heslo smí být prázdné. To školní účet nechce. +- Zásada neopraví heslo, které už je krátké, dokud ho uživatel příště nemění. Platí pro nová hesla. + +> Tip: Heslo v zásadě nenastavujete. Nastavujete pravidlo. Samotné heslo účtu zaci se zadává při net user, ne tady. + +## Zámek +*300 sekund* + +- Nečinnost: Konfigurace počítače, Nastavení systému Windows, Nastavení zabezpečení, Místní zásady, Možnosti zabezpečení. +- Položka Interaktivní přihlášení: limit nečinnosti počítače se nastaví na 300 sekund, tedy 5 minut. +- Nula nebo nenastaveno znamená, že tato zásada zámek nevynucuje. +- Po pěti minutách bez myši a klávesnice se má počítač zamknout, ne vypnout a ne smazat Dokumenty. + +> Tip: Zámek je Win+L hned. Zásada totéž udělá sama, když odejdete od stroje a zapomenete. + +## Zadání: dvě hodnoty +*Cvičení* + +- Pokud gpedit.msc máte, nastavte minimální délku hesla na 8 a limit nečinnosti na 300 sekund. Původní hodnoty si zapište. +- gpupdate /force zásady načte hned, bez čekání na restart. Příkaz chce okno spuštěné jako správce. +- Do zasady.txt napište obě hodnoty a jestli je počítač v doméně. Heslo nepište. +- V edici Home napište, že gpedit.msc v ní není, a hodnoty jen popište. Nevymýšlejte, že jste je nastavili. + +> Tip: Když po gpupdate hodnota v gpedit zůstane a systém se nechová podle ní, počítač je v doméně a doména místní číslo přebíjí. + +# Kvíz + +## Jaká je školní minimální délka hesla? +- 0 +- [x] 8 +- 300 +- Prázdné heslo +> Osm znaků je pravidlo z první hodiny o účtech. Nula by prázdné heslo dovolila. + +## Na kolik sekund se nastaví limit nečinnosti? +- 5 +- [x] 300 +- 8 +- Na nulu, ať se počítač nezamyká +> 300 sekund je pět minut. Číslo 5 by bylo pět sekund, to v této položce není. + +## Čím se místní zásady načtou hned? +- Smazáním účtu zaci +- [x] gpupdate /force +- mkdir +- SELECT * FROM zaci +> gpupdate zásady znovu načte. Bez něj si počítač může počkat na své vlastní obnovení. + +## Co platí, když je počítač v doméně školy a místní heslo nastavíte jinak? +- Vždy vyhraje místní gpedit +- [x] Vyhraje doménová zásada +- Hesla se vypnou +- WordPress přestane běžet +> Doména je nad místním počítačem. Místní změna, která jí odporuje, se neuplatní. diff --git a/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.en.md b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.en.md new file mode 100644 index 0000000..fdca7fe --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exam assignment +popis: Folders, a letter, grades, a register, a site, a report and an account with a password rule. +nazev_cviceni: Quiz: the whole assignment +nadpis_kvizu: Walk the hand-in +--- diff --git a/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.md b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.md new file mode 100644 index 0000000..ef404a3 --- /dev/null +++ b/content/programove-vybaveni/4-rocnik/08-maturitni-zadani.md @@ -0,0 +1,79 @@ +--- +id: sw-4-maturita +nazev: Maturitní zadání +popis: Složky, dopis, známky, evidence, web, sestava a účet s zásadou hesla. +nazev_cviceni: Kvíz: celé zadání +nadpis_kvizu: Projdi odevzdání +--- + +# Výklad + +## Systém +*Složky a účet* + +- slozky.bat založí Skola\1-rocnik\word, excel a system v Dokumentech toho účtu, který ho spustí. +- V bat není del a není v něm heslo. Je v něm @echo off, mkdir a pause. +- Účet zaci je běžný uživatel ve skupině Users, není v Administrators. +- Minimální délka hesla je 8 znaků. Zámek po nečinnosti je 300 sekund, pokud edice Windows zásady má. + +> Tip: Do textového souboru patří příkazy a hodnoty. Skutečné heslo ne. + +## Office +*Dopis a známky* + +- zadost.docx je jedna strana, datum 12. 6. 2026, řádek Věc, k tomu PDF. +- znamky.xlsx počítá stav vzorcem KDYŽ. Známka 5 je neprospěl, 1 až 4 prospěl. +- Pod známkami je průměr, ne součet. +- prodej.xlsx a prodej.pbix: tabulka načtená jednou, průřez třídy, PDF bez výběru, ať jsou vidět obě třídy. + +> Tip: pbix bez xlsx zdroje učitel neobnoví. Xlsx vedle pbix nechte. + +## Data a web +*Evidence a Školní den* + +- Access nebo MySQL: tabulky tridy a zaci, vztah 1:N, Anna Nováková ve třídě 1.A. +- Dotaz ukáže název 1.A, ne jen číslo. UPDATE a DELETE, pokud jsou v SQL, mají WHERE. +- WordPress: název Školní den, stránka O škole, příspěvek s datem, účet autora. Správce je jeden. +- V textech o webu a účtech není heslo. + +> Tip: Stačí jeden databázový nástroj, ale celý. Access s integritou, nebo SQL s JOINem. + +## Složka odevzdání +*Pořádek* + +- Všechno leží pod Skola, ne na ploše a ne v Stažených. +- Přípony jsou vidět. Názvy nemají final2. +- Na obhajobu otevřete nejdřív bat a složky, pak Excel a Power BI, pak databázi a nakonec web. +- U každé části umíte říct, kdo smí heslo, kdo smí instalovat a proč pětka není prospěl. + +> Tip: Když se zeptají, proč zaci není správce, odpověď je: denní účet nemá právo měnit systém. + +# Kvíz + +## Co slozky.bat nesmí obsahovat? +- mkdir +- @echo off +- [x] del přes celý disk nebo cizí složky +- pause +> Cvičení složky zakládá. Mazání do něj nepatří a neznámé mazání už vůbec ne. + +## Jaký je stav známky 5? +- prospěl +- [x] neprospěl +- průměr +- 1.A +> Prochází 1 až 4. Pětka je neprospěl. Rozhoduje vzorec KDYŽ, ne rukou psaný text. + +## Jakou roli má účet, který ve WordPressu jen píše novinky? +- Správce +- [x] Autor +- Jediný sdílený účet učitele +- Administrators ve Windows +> Autor zveřejní vlastní příspěvek. Doplňky a uživatele spravuje jeden správce. + +## Jaké jsou dvě hodnoty zásad? +- Heslo 0 znaků a zámek 8 sekund +- [x] Heslo aspoň 8 znaků a zámek po 300 sekundách +- Heslo v souboru bat a zámek vypnutý +- 300 znaků hesla a 8 sekund +> Osm znaků je délka hesla. 300 sekund je pět minut nečinnosti. diff --git a/content/technicka-grafika/1-rocnik/01-format-a-meritko.en.md b/content/technicka-grafika/1-rocnik/01-format-a-meritko.en.md new file mode 100644 index 0000000..2de20d7 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/01-format-a-meritko.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sheet size, scale and the title block +popis: A sizes, drawing scale and the title block. +nazev_cviceni: Quiz: sheet and scale +nadpis_kvizu: Check the sheet, the scale and the title block +--- diff --git a/content/technicka-grafika/1-rocnik/01-format-a-meritko.md b/content/technicka-grafika/1-rocnik/01-format-a-meritko.md new file mode 100644 index 0000000..2ef74ba --- /dev/null +++ b/content/technicka-grafika/1-rocnik/01-format-a-meritko.md @@ -0,0 +1,80 @@ +--- +id: tg-1-format-a-meritko +nazev: Formát, měřítko a razítko +popis: Řada A, měřítko výkresu a popisové pole. +nazev_cviceni: Kvíz: formát a měřítko +nadpis_kvizu: Ověřte si formát, měřítko a razítko +--- + +# Výklad + +## Řada formátů A +*Papír* + +- Základní formát A4 má 210 × 297 mm. Delší strana je na výšku. +- A3 vznikne složením dvou A4: 297 × 420 mm. +- A2 je 420 × 594 mm. Každý větší formát má dvojnásobnou plochu. +- Na škole se součást kreslí nejčastěji na A4 nebo A3. + +> Tip: kratší strana většího formátu je delší stranou menšího. A3 je na šířku, A4 na výšku. + +## Co znamená měřítko +*Měřítko* + +- Měřítko 1:1 znamená, že výkres je ve skutečné velikosti. +- Měřítko 1:2 zmenšuje kresbu na polovinu. Měřítko 2:1 ji zvětšuje na dvojnásobek. +- Kóta se vždy píše ve skutečném rozměru, ne v rozměru nakreslené čáry. +- Do razítka se měřítko zapíše tak, jak se kreslilo, třeba 1:2. + +> Tip: díra o průměru 20 mm má na výkresu 1:2 čáru dlouhou 10 mm, ale kóta říká 20. + +## Popisové pole +*Razítko* + +- Razítko je v pravém dolním rohu. Čte se zprava, když je výkres ve správné poloze. +- Patří do něj název, měřítko, materiál, datum a jméno. +- Číslo výkresu a název součásti se nesmí vyměnit za ozdobný text. +- Rámeček je od okraje papíru odsazený, na školním A4 obvykle o 5 až 10 mm. + +> Tip: než začnete kreslit součást, vyhraďte si hladinu pro rámeček a razítko. + +## Zadání: rámeček A4 +*Cvičení* + +- Nakreslete obdélník 210 × 297 a dovnitř rámeček odsazený o 10 mm od každého okraje. +- Vpravo dole vyhraďte razítko 180 × 40 mm, které lícuje s vnitřním rámečkem. +- Do razítka vepište název Destička, měřítko 1:1 a své jméno. +- Soubor uložte jako A4-ramecek.dwg, jednotky milimetry. + +> Tip: vnitřní rámeček má 190 × 277 mm. Levý dolní roh dejte do bodu 0,0. + + +# Kvíz + +## Jaké rozměry má formát A4? +- 297 × 420 mm +- [x] 210 × 297 mm +- 420 × 594 mm +- 148 × 210 mm +> A3 je 297 × 420 mm. A4 je polovina této plochy, 210 × 297 mm. + +## Co se stane s kótou na výkresu v měřítku 1:2? +- Píše se polovina skutečného rozměru +- [x] Píše se skutečný rozměr +- Kóty se vynechají +- Měřítko se do razítka nepíše +> Kresba je poloviční, číslo na kótě zůstává skutečné. Měřítko 1:2 je v razítku. + +## Kam se na výkresu umísťuje razítko? +- Doprostřed plochy +- [x] Do pravého dolního rohu +- Nad součást, vlevo +- Mimo rámeček +> Razítko lícuje s vnitřním rámečkem vpravo dole, aby šlo výkres složit a razítko zůstalo navrchu. + +## Jak je velký vnitřní rámeček A4 při odsazení 10 mm? +- 210 × 297 mm +- [x] 190 × 277 mm +- 200 × 287 mm +- 180 × 40 mm +> Od 210 i od 297 se odečte 10 mm z každé strany, tedy 20 mm. 190 × 277 mm. diff --git a/content/technicka-grafika/1-rocnik/02-druhy-car.en.md b/content/technicka-grafika/1-rocnik/02-druhy-car.en.md new file mode 100644 index 0000000..afc6ade --- /dev/null +++ b/content/technicka-grafika/1-rocnik/02-druhy-car.en.md @@ -0,0 +1,6 @@ +--- +nazev: Line types and line weights +popis: Outlines, hidden edges, centrelines and dimensions. +nazev_cviceni: Quiz: lines +nadpis_kvizu: Name the line +--- diff --git a/content/technicka-grafika/1-rocnik/02-druhy-car.md b/content/technicka-grafika/1-rocnik/02-druhy-car.md new file mode 100644 index 0000000..542e176 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/02-druhy-car.md @@ -0,0 +1,80 @@ +--- +id: tg-1-druhy-car +nazev: Druhy a tloušťky čar +popis: Obrys, skryté hrany, osy a kóty podle normy. +nazev_cviceni: Kvíz: čáry +nadpis_kvizu: Poznejte druh čáry +--- + +# Výklad + +## Tlustá plná čára +*Obrys* + +- Viditelné hrany a obrysy se kreslí souvislou tlustou čarou. +- V AutoCADu jí patří vlastní hladina, třeba OBRYS, s tloušťkou 0,5 mm. +- Tloušťka se nastavuje na hladině, ne u každé čáry zvlášť. +- Obrys nesmí být stejně tenký jako kóta, jinak se výkres nedá číst. + +> Tip: hladinu OBRYS nastavte jako aktuální dřív, než začnete kreslit tvar. + +## Tenké čáry +*Kóty a hrany* + +- Kóty, odkazy, šrafy a pomocné čáry jsou souvislé a tenké, kolem 0,25 mm. +- Skryté hrany, které z daného pohledu nejsou vidět, jsou čárkované a tenké. +- Osa díry nebo válce je čerchovaná: dlouhá čárka, tečka, dlouhá čárka. +- Osa přesahuje obrys jen o kousek, nepřetahuje se přes celý výkres. + +> Tip: čárkovaná čára je skrytá hrana. Čerchovaná je osa. Nejsou totéž. + +## Jak to nastavit v AutoCADu +*Hladiny* + +- Příkaz `_LAYER` otevře správce hladin. Založte OBRYS, SKRYTE, OSA a KOTA. +- U SKRYTE zvolte typ čáry DASHED, u OSA typ CENTER. +- Typ čáry se načítá příkazem `_LINETYPE`. Bez načtení ho hladina nenabídne. +- Měřítko typu čáry `_LTSCALE` zvětší nebo zmenší mezery v čárkování, ať je čára čitelná. + +> Tip: když čárkování vypadá jako plná čára, je LTSCALE moc velké nebo moc malé. + +## Zadání: tři čáry jedné díry +*Cvičení* + +- Nakreslete obdélník 60 × 40 tlustou plnou čarou. +- Svislou osu veďte středem a nechte ji přesáhnout obrys o 3 mm na každou stranu. +- Jednu zadní hranu, která z pohledu není vidět, nakreslete tenkou čárkovanou čarou. +- Každý druh čáry musí ležet na své hladině. + +> Tip: osa dlouhá 46 mm při výšce tělesa 40 mm přesahuje o 3 mm nahoře i dole. + + +# Kvíz + +## Jakou čarou se kreslí viditelný obrys? +- Tenkou čárkovanou +- [x] Tlustou plnou +- Tenkou čerchovanou +- Tlustou čerchovanou +> Viditelné hrany jsou souvislé a tlustší než kóty, osy i skryté hrany. + +## Co znamená tenká čárkovaná čára? +- Osu díry +- [x] Skrytou hranu +- Obrys +- Šrafu +> Čárkovaná čára je hrana, kterou v daném pohledu nevidíme. Osa je čerchovaná. + +## Jakou čarou se kreslí osa válce? +- Tlustou plnou +- Tenkou čárkovanou +- [x] Tenkou čerchovanou +- Vlnovkou +> Osa je dlouhá čárka, tečka, dlouhá čára. V AutoCADu je to typ CENTER. + +## Proč má mít obrys vlastní hladinu? +- Aby šel smazat jen razítko +- [x] Aby měla tloušťka a typ čáry jedno místo +- Hladiny v AutoCADu nejsou +- Obrys se kreslí jen od ruky +> Tloušťka i typ se nastaví na hladině. Všechny obrysy je pak mají stejné. diff --git a/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.en.md b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.en.md new file mode 100644 index 0000000..2d39a07 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.en.md @@ -0,0 +1,6 @@ +--- +nazev: The AutoCAD workspace +popis: Model space, the command line, units and limits. +nazev_cviceni: Quiz: the workspace +nadpis_kvizu: Check the first drawing setup +--- diff --git a/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.md b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.md new file mode 100644 index 0000000..7a16b82 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/03-prostredi-autocadu.md @@ -0,0 +1,80 @@ +--- +id: tg-1-prostredi +nazev: Prostředí AutoCADu +popis: Model, příkazový řádek, jednotky a meze. +nazev_cviceni: Kvíz: prostředí +nadpis_kvizu: Ověřte si první nastavení výkresu +--- + +# Výklad + +## Kam se kreslí +*Model* + +- Součást se kreslí v modelu. Papír, rámeček a výřezy patří až do rozvržení. +- Příkazový řádek je dole. Čte se pořád: AutoCAD se ptá na další bod. +- Stisk Enter nebo mezerník zopakuje poslední příkaz. +- Esc příkaz zruší. Neukládejte rozpracovaný příkaz klávesou Esc uprostřed bodu. + +> Tip: když AutoCAD „nic nedělá“, podívejte se, na co se příkazový řádek ptá. + +## Příkaz, který funguje vždy +*Podtržítko* + +- Český AutoCAD má přeložené příkazy. Úsečka se jmenuje ÚSEČKA. +- Mezinárodní jméno se píše s podtržítkem: `_LINE`. Funguje v každé jazykové verzi. +- Na pásu karet je stejný příkaz pod ikonou. Ikona i text dělají totéž. +- V těchto lekcích je u každého kroku mezinárodní příkaz. Naučte se ho psát. + +> Tip: `_LINE` a ÚSEČKA spustí stejné kreslení. Podtržítko se hodí, když čeština na klávesnici zlobí. + +## Jednotky a meze +*Milimetry* + +- `_UNITS` nastavte na desetinné milimetry. Úhly nechte ve stupních. +- Přesnost 0 stačí na celé milimetry, 0,0 když budete potřebovat desetinu. +- `_LIMITS` určí levý dolní a pravý horní roh kreslicí plochy, třeba 0,0 a 420,297. +- `_ZOOM` a volba All ukáže celé meze. Kolečko myši přibližuje ke kurzoru. + +> Tip: nový výkres začněte jednotkami. Přepočet z palců na milimetry uprostřed práce nic nespraví. + +## Zadání: prázdný výkres A3 +*Cvičení* + +- Založte soubor v milimetrech a nastavte meze 0,0 až 420,297. +- Příkazem `_RECTANG` nakreslete obrys papíru z bodu 0,0 do bodu 420,297. +- Přibližte vše příkazem `_ZOOM` a volbou Extents. +- Uložte soubor jako A3-prazdny.dwg. + +> Tip: Extents přiblíží to, co je nakreslené. All přiblíží meze, i když jsou prázdné. + + +# Kvíz + +## Kde se kreslí tvar součásti? +- V rozvržení, přes razítko +- [x] V modelu +- Jen na pásu karet +- V příkazu PLOT +> Model je prostor součásti. Rozvržení je papír, na který se model později vloží výřezem. + +## Co udělá příkaz `_LINE` v českém AutoCADu? +- Nastaví jednotky +- [x] Spustí kreslení úsečky +- Smaže poslední čáru +- Otevře hladiny +> Podtržítko volá mezinárodní příkaz. `_LINE` je úsečka, ať je AutoCAD v jakémkoli jazyce. + +## Jakou jednotku má školní výkres? +- Palec +- [x] Milimetr +- Metr +- Pixel +> `_UNITS` nastavte na desetinné milimetry dřív, než zadáte první rozměr. + +## Co ukáže `_ZOOM` s volbou Extents? +- Celé nastavené meze, i prázdné +- [x] Všechno, co je nakreslené +- Jen razítko +- Poslední příkaz +> Extents orámuje nakreslené objekty. All ukáže meze výkresu. diff --git a/content/technicka-grafika/1-rocnik/04-souradnice.en.md b/content/technicka-grafika/1-rocnik/04-souradnice.en.md new file mode 100644 index 0000000..95648e1 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/04-souradnice.en.md @@ -0,0 +1,6 @@ +--- +nazev: Coordinates +popis: Absolute points, relative moves and polar entry. +nazev_cviceni: Quiz: coordinates +nadpis_kvizu: Find the next point +--- diff --git a/content/technicka-grafika/1-rocnik/04-souradnice.md b/content/technicka-grafika/1-rocnik/04-souradnice.md new file mode 100644 index 0000000..626ba0c --- /dev/null +++ b/content/technicka-grafika/1-rocnik/04-souradnice.md @@ -0,0 +1,80 @@ +--- +id: tg-1-souradnice +nazev: Souřadnice +popis: Absolutní bod, relativní posun a polární zápis. +nazev_cviceni: Kvíz: souřadnice +nadpis_kvizu: Dopočítejte další bod +--- + +# Výklad + +## Absolutní souřadnice +*Od počátku* + +- Bod 40,25 leží 40 mm vpravo a 25 mm nahoru od počátku 0,0. +- Čárka odděluje X a Y. Desetinná čárka v čísle se v AutoCADu píše tečkou: 10.5 +- Osa X míří vpravo, osa Y nahoru. Tak je natočený model při startu. +- Absolutní bod nezávisí na tom, kde jste skončili minule. + +> Tip: když se výkres „rozjede“ mimo papír, někdo zadal absolutní bod místo posunu. + +## Relativní souřadnice +*Zavináč* + +- `@30,0` znamená 30 mm vpravo od posledního bodu, výška se nemění. +- `@0,40` je 40 mm nahoru. `@-20,0` je 20 mm doleva. +- Zavináč se píše před čísla. Bez něj AutoCAD čte absolutní bod. +- Při kreslení `_LINE` se tak skládá tvar bod za bodem. + +> Tip: obdélník 70 × 40 z bodu 10,10: `@70,0` pak `@0,40` pak `@-70,0` a zavřít volbou Close. + +## Polární souřadnice +*Délka a úhel* + +- `@50<90` je úsečka dlouhá 50 mm směrem 90 stupňů, tedy kolmo nahoru. +- Úhel se měří od kladné osy X proti směru hodinových ručiček. +- `@40<0` míří vpravo, `@40<180` vlevo, `@40<270` dolů. +- Špičatá závorka se píše bez mezery. Mezera by příkaz ukončila. + +> Tip: úhel 45 stupňů je šikmo vpravo nahoru. Délka je vždy skutečná, ne průmět. + +## Zadání: obdélník ze souřadnic +*Cvičení* + +- Spusťte `_LINE` v bodě 20,20. +- Pokračujte `@80,0`, potom `@0,50`, potom `@-80,0`. +- Útvar zavřete volbou Close, ne ručním bodem. +- Změřte `_DIST` mezi levým dolním a pravým horním rohem. Má vyjít 94,3 mm, tedy přepona 80 a 50. + +> Tip: Close spojí poslední bod s prvním. Ruční bod 20,20 nemusí trefit počátek a obrys zůstane otevřený. + + +# Kvíz + +## Co znamená zápis `@0,40`? +- Bod 0,40 od počátku výkresu +- [x] 40 mm nahoru od posledního bodu +- 40 mm vpravo od počátku +- Úhel 40 stupňů +> Zavináč čte posun od posledního bodu. Nula v X a 40 v Y je čistě nahoru. + +## Kam míří `@60<180`? +- 60 mm nahoru +- [x] 60 mm doleva +- 180 mm vpravo +- Do počátku +> Úhel 180 stupňů je záporná osa X, tedy doleva. 60 je délka úsečky. + +## Jak AutoCAD pozná desetinné číslo? +- Čárkou: 10,5,0 +- [x] Tečkou: 10.5 +- Zlomkem 21/2 +- Mezerou 10 5 +> Čárka odděluje X a Y. Desetina se píše tečkou, třeba 10.5,20. + +## Proč útvar zavřít volbou Close? +- Aby se smazala první úsečka +- [x] Aby se poslední bod trefil do prvního +- Close nastaví měřítko +- Close je totéž co Esc +> Ručně opsaný první bod často mine o zlomek milimetru a obrys nejde vyšrafovat. diff --git a/content/technicka-grafika/1-rocnik/05-zakladni-entity.en.md b/content/technicka-grafika/1-rocnik/05-zakladni-entity.en.md new file mode 100644 index 0000000..2ca03d0 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/05-zakladni-entity.en.md @@ -0,0 +1,6 @@ +--- +nazev: Lines, circles, arcs and rectangles +popis: The four commands every part starts with. +nazev_cviceni: Quiz: basic objects +nadpis_kvizu: Pick the right command +--- diff --git a/content/technicka-grafika/1-rocnik/05-zakladni-entity.md b/content/technicka-grafika/1-rocnik/05-zakladni-entity.md new file mode 100644 index 0000000..e41ea6b --- /dev/null +++ b/content/technicka-grafika/1-rocnik/05-zakladni-entity.md @@ -0,0 +1,80 @@ +--- +id: tg-1-zakladni-entity +nazev: Úsečka, kružnice, oblouk a obdélník +popis: Čtyři příkazy, kterými začíná každá součást. +nazev_cviceni: Kvíz: základní entity +nadpis_kvizu: Vyberte správný příkaz +--- + +# Výklad + +## Úsečka a obdélník +*Hrany* + +- `_LINE` chce první a další bod. Končí se Enterem, zavření obrysu volbou Close. +- `_RECTANG` chce dva protilehlé rohy. Z bodu 0,0 a `@90,40` vznikne obdélník 90 × 40. +- Obdélník je jedna uzavřená křivka. Úsečky jsou samostatné objekty. +- Uzavřená křivka se později líp šrafuje a vysouvá do 3D. + +> Tip: když budete tvar vysouvat, kreslete ho jako `_RECTANG` nebo uzavřenou `_PLINE`, ne jako čtyři volné úsečky. + +## Kružnice +*Díry* + +- `_CIRCLE` se nejčastěji zadá středem a poloměrem. +- Volba D přepne na průměr. U díry Ø12 je průměr 12 a poloměr 6. +- Střed díry se kótuje, ne její okraj. Okraj je důsledek průměru. +- Víc stejných děr se nekreslí znovu: první se zkopíruje nebo rozloží do pole. + +> Tip: v zadání Ø znamená průměr. Do příkazu s poloměrem dáte polovinu. + +## Oblouk +*Zaoblení z příkazu* + +- `_ARC` umí oblouk třemi body nebo středem, počátkem a úhlem. +- Zaoblení rohu ale nedělejte obloukem od ruky. Na to je později `_FILLET`. +- Oblouk má směr. Špatné pořadí bodů ho vyklenu na druhou stranu. +- U drážky se oblouk často nahradí `_PLINE` s volbou Arc uprostřed úseček. + +> Tip: tři body oblouku zadávejte v pořadí, v jakém oblouk plyne. Prostřední bod je na oblouku, ne střed kružnice. + +## Zadání: destička s dírou +*Cvičení* + +- `_RECTANG` z 0,0 do `@90,50`. +- Střed destičky je 45,25. Tam `_CIRCLE`, volba D, průměr 16. +- Osu svislou i vodorovnou nechte přesáhnout obrys o 3 mm. Patří na hladinu OSA. +- Uložte jako desticka-90x50.dwg. + +> Tip: polovina z 90 je 45 a polovina z 50 je 25. Střed neměřte od oka. + + +# Kvíz + +## Čím nakreslíte obdélník 90 × 40 jedním objektem? +- Čtyřmi úsečkami `_LINE` +- [x] `_RECTANG` a druhým rohem `@90,40` +- `_CIRCLE` s poloměrem 90 +- `_ARC` o úhlu 90 +> `_RECTANG` chce dva rohy. Relativní `@90,40` je šířka a výška od prvního rohu. + +## Zadání říká díra Ø12. Jaký poloměr má kružnice? +- 12 +- [x] 6 +- 24 +- 3 +> Průměr je 12, poloměr je polovina. V `_CIRCLE` buď poloměr 6, nebo volba D a průměr 12. + +## Kde leží střed destičky 90 × 50, která začíná v 0,0? +- 90,50 +- [x] 45,25 +- 0,0 +- 25,45 +> Střed je v polovině šířky a polovině výšky. + +## Kdy dát přednost uzavřené křivce před čtyřmi úsečkami? +- Nikdy, úsečka je přesnější +- [x] Když se obrys bude šrafovat nebo vysouvat +- Jen u razítka +- Když je tvar otevřený +> Šrafa i vysunutí potřebují uzavřenou oblast. Čtyři úsečky, které se netrefí, oblast neutvoří. diff --git a/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.en.md b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.en.md new file mode 100644 index 0000000..08596b5 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.en.md @@ -0,0 +1,6 @@ +--- +nazev: Snaps, ortho and polar tracking +popis: How to hit an endpoint and draw square. +nazev_cviceni: Quiz: precise drawing +nadpis_kvizu: Pick the snap and the toggle +--- diff --git a/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.md b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.md new file mode 100644 index 0000000..c9f7f0e --- /dev/null +++ b/content/technicka-grafika/1-rocnik/06-uchopeni-a-orto.md @@ -0,0 +1,80 @@ +--- +id: tg-1-uchopeni +nazev: Úchop, orto a polární trasování +popis: Jak trefit konec čáry a kreslit kolmo. +nazev_cviceni: Kvíz: přesné kreslení +nadpis_kvizu: Vyberte úchop a přepínač +--- + +# Výklad + +## Úchopy +*Koncový bod* + +- Úchop přitáhne kurzor ke geometrii. Bez něj minuta minete konec čáry. +- Zapíná se klávesou F3 nebo v stavovém řádku. Nastavení je `_OSNAP`. +- Na začátek stačí koncový bod, střed a střed kružnice. +- Kolmice a tečna se hodí, až budete napojovat čáru na kružnici. + +> Tip: úchop Střed u úsečky je její polovina. Střed u kružnice je její osa. Jsou to dva různé úchopy. + +## Orto a polární úhel +*F8 a F10* + +- Orto, klávesa F8, pustí kurzor jen vodorovně a svisle. +- Polární trasování F10 drží i šikmé úhly, obvykle po 45 nebo po 15 stupních. +- Orto a volné šikmé kreslení nejdou dohromady. Na šikmou čáru orto vypněte. +- Délku při zapnutém ortu napište jen číslem a Enter. Směr určuje myš. + +> Tip: vodorovná čára dlouhá 40 mm je F8, kurzor doprava, napsat 40 a Enter. Nemusíte počítat druhý bod. + +## Proč se výkres rozpadne +*Minutí* + +- Dvě čáry, které se „téměř“ dotýkají, nejdou oříznout ani vyšrafovat. +- Mezera vznikne, když kliknete vedle koncového bodu a úchop je vypnutý. +- `_ZOOM` na roh ukáže mezeru, která na celém výkresu není vidět. +- Opravený roh se napojí úchopem, ne posunem od oka. + +> Tip: než odevzdáte výkres, přibližte každý roh. Mezera menší než milimetr na tisku vynikne. + +## Zadání: kříž přesně ve středu +*Cvičení* + +- Nakreslete obdélník 80 × 60 z bodu 0,0. +- Svislou osu veďte z úchopu Střed dolní hrany do úchopu Střed horní hrany. +- Vodorovnou osu veďte ze Středu levé hrany do Středu pravé. +- Osy se mají protnout v bodě 40,30. Ověřte to `_ID`. + +> Tip: `_ID` vypíše souřadnice úchopu. Když průsečík není 40,30, jedna osa minula střed hrany. + + +# Kvíz + +## Která klávesa zapíná úchopy? +- F8 +- [x] F3 +- F10 +- Esc +> F3 jsou úchopy, F8 je orto, F10 je polární trasování. + +## Co dělá orto? +- Chytá konce čar +- [x] Drží kreslení ve vodorovném a svislém směru +- Nastaví milimetry +- Maže poslední úsečku +> Se zapnutým ortem kurzor neuhne do šikma. Délku pak stačí napsat číslem. + +## Proč dvě čáry, které se na obrazovce dotýkají, nejdou vyšrafovat? +- Šrafa vyžaduje kóty +- [x] Mezi nimi je mezera, konec minul úchop +- Hladina OSA to zakazuje +- Orto je zapnuté +> Od oka minutý konec nechá škvíru. Přiblížení a úchop Koncový bod ji ukážou. + +## Který úchop najde polovinu úsečky? +- Střed kružnice +- [x] Střed +- Koncový bod +- Kolmice +> Úchop Střed na úsečce je její polovina. Na kružnici je to jiný úchop, střed kružnice. diff --git a/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.en.md b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.en.md new file mode 100644 index 0000000..0409703 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Move, copy, rotate and mirror +popis: Change a finished shape instead of drawing it again. +nazev_cviceni: Quiz: editing +nadpis_kvizu: Match the command to the task +--- diff --git a/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.md b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.md new file mode 100644 index 0000000..611ca1a --- /dev/null +++ b/content/technicka-grafika/1-rocnik/07-posun-kopie-otoceni.md @@ -0,0 +1,80 @@ +--- +id: tg-1-posun-kopie +nazev: Posun, kopie, otočení a zrcadlení +popis: Úprava hotového tvaru místo kreslení znovu. +nazev_cviceni: Kvíz: úpravy +nadpis_kvizu: Vyberte příkaz podle zadání +--- + +# Výklad + +## Posun a kopie +*Z místa na místo* + +- `_MOVE` objekt odnese. Na původním místě nic nezůstane. +- `_COPY` nechá vzor a přidá další. Ptá se na základní bod a pak na nové body. +- Základní bod vyberte úchopem, třeba roh nebo střed. Od oka se kopie rozjede. +- Víc kopií za sebou vznikne opakovaným zadáním dalšího bodu, bez nového příkazu. + +> Tip: posun o 30 mm doprava je základní bod kdekoli, druhý bod `@30,0`. + +## Otočení +*Kolem bodu* + +- `_ROTATE` chce základní bod a úhel. Kladný úhel je proti směru hodin. +- 90 otočí doleva, -90 nebo 270 doprava. +- Volba Reference vezme současný úhel hrany a řekne, jaký má být nový. +- Otáčí se kolem vybraného bodu, ne kolem středu obrazovky. + +> Tip: díru v rohu otočíte kolem středu destičky, ne kolem středu díry, jinak odjede z rozteče. + +## Zrcadlení +*Souměrnost* + +- `_MIRROR` chce dva body osy zrcadlení. Osa nemusí být nakreslená předem. +- Svislá osa jsou dva body se stejným X. Vodorovná dva body se stejným Y. +- Na otázku, zda smazat zdroj, dejte Ne, když má zůstat obě poloviny. +- Text se při zrcadlení může otočit naruby. Proměnná MIRRTEXT s hodnotou 0 nechá text čitelný. + +> Tip: souměrnou součást nakreslete z poloviny a zrcadlete. Druhou polovinu neobkreslujte. + +## Zadání: druhá díra zrcadlem +*Cvičení* + +- Destička 100 × 40, levý dolní roh v 0,0. +- Díru Ø10 dejte středem do bodu 20,20. +- Zrcadlete ji přes svislou osu destičky. Osa prochází body 50,0 a 50,40. +- Zdroj nemažte. Druhá díra má střed v 80,20. + +> Tip: 100 minus 20 je 80. Zrcadlo kolem x = 50 převede 20 na 80 a výšku 20 nechá. + + +# Kvíz + +## Čím odnesete objekt, aby na původním místě nezůstal? +- `_COPY` +- [x] `_MOVE` +- `_MIRROR` +- `_ROTATE` +> Kopie zdroj nechá. Posun ho přestěhuje. + +## Jaký úhel otočí tvar o 90 stupňů ve směru hodin? +- 90 +- [x] -90 +- 180 +- 45 +> Kladný úhel jde proti hodinám. Po směru hodin je záporný, tedy -90. + +## Přes co se zrcadlí díra se středem 20,20 na destičce široké 100? +- Přes bod 0,0 +- [x] Přes svislou osu x = 50 +- Přes vodorovnou osu y = 20 +- Přes hranu x = 100 +> Osa souměrnosti destičky je uprostřed šířky. 20 a 80 jsou od ní stejně daleko. + +## Co má být základní bod posunu? +- Libovolné místo vedle objektu, od oka +- [x] Úchop, třeba roh nebo střed +- Vždy počátek 0,0 +- Střed obrazovky +> Úchop podrží rozměr. Kliknutí vedle rohu posune tvar o neznámý kousek navíc. diff --git a/content/technicka-grafika/1-rocnik/08-zadani-desticka.en.md b/content/technicka-grafika/1-rocnik/08-zadani-desticka.en.md new file mode 100644 index 0000000..1920b3e --- /dev/null +++ b/content/technicka-grafika/1-rocnik/08-zadani-desticka.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a plate +popis: The whole first-year drawing on one part. +nazev_cviceni: Quiz: the plate +nadpis_kvizu: Check the finished drawing +--- diff --git a/content/technicka-grafika/1-rocnik/08-zadani-desticka.md b/content/technicka-grafika/1-rocnik/08-zadani-desticka.md new file mode 100644 index 0000000..08464d1 --- /dev/null +++ b/content/technicka-grafika/1-rocnik/08-zadani-desticka.md @@ -0,0 +1,80 @@ +--- +id: tg-1-zadani-desticka +nazev: Zadání: destička +popis: Celý výkres z prvního ročníku na jedné součásti. +nazev_cviceni: Kvíz: destička +nadpis_kvizu: Zkontrolujte hotový výkres +--- + +# Výklad + +## Co se odevzdává +*Zadání* + +- Destička 120 × 70 mm, tloušťka se nekreslí, jde o jeden pohled. +- Čtyři díry Ø8. Středy jsou 15 mm od levého i pravého kraje a 15 mm od dolního i horního kraje. +- Uprostřed je podélná drážka 40 × 16 se zaoblenými konci. Zatím stačí obdélník, zaoblení přijde ve druháku. +- Výkres je na A4, měřítko 1:1, rámeček a razítko z první lekce. + +> Tip: středy děr jsou body 15,15, pak 105,15, pak 15,55 a 105,55. 120 minus 15 je 105, 70 minus 15 je 55. + +## Hladiny a čáry +*Pořádek* + +- OBRYS je tlustá plná čára. OSA je tenká čerchovaná. KOTA zatím nechte prázdnou. +- Každá díra má svislou i vodorovnou osu, která přesahuje obrys o 3 mm. +- Destička má jednu vodorovnou a jednu svislou osu souměrnosti přes celý tvar. +- Skryté hrany tu nejsou. Pohled je zepředu a díry jsou průchozí, jejich okraj je vidět. + +> Tip: průchozí díra v tomto pohledu není čárkovaná. Čárkovaná by byla hrana schovaná za materiálem. + +## Postup +*Po řadě* + +- Nejdřív rámeček a razítko, potom obrys `_RECTANG` 120 × 70. +- Jednu díru `_CIRCLE` o průměru 8 a další tři `_COPY` nebo dvě `_MIRROR`. +- Drážku `_RECTANG` 40 × 16 se středem ve středu destičky. Její levý dolní roh je 40,27. +- Osy dokreslete úchopem Střed. `_DIST` mezi protilehlými dírami ve vodorovném směru má být 90. + +> Tip: střed destičky je 60,35. Drážka sahá 20 mm doleva a doprava a 8 mm nahoru a dolů. + +## Kontrola před odevzdáním +*Co musí sedět* + +- V razítku je název Destička, měřítko 1:1 a materiál, třeba ocel. +- Žádná čára obrysu neleží na hladině OSA a žádná osa není plná. +- Přiblížené rohy nemají mezeru. Kružnice se dotýká svých os ve středu. +- Soubor se jmenuje desticka-120x70.dwg a je v milimetrech. + +> Tip: vypněte hladinu OBRYS. Zůstat mají jen osy a rámeček. Zapněte ji zpět a totéž udělejte s OSA. + + +# Kvíz + +## Kde leží střed pravé horní díry? +- 15,15 +- [x] 105,55 +- 120,70 +- 60,35 +> Od pravého kraje 15 mm je x = 105. Od horního kraje 15 mm je y = 55. + +## Jaká je vodorovná rozteč levé a pravé díry? +- 120 +- 15 +- [x] 90 +- 105 +> 105 minus 15 je 90. Tolik má ukázat `_DIST` mezi středy. + +## Kam patří obrys destičky? +- Na hladinu OSA +- [x] Na hladinu OBRYS, tlustou plnou čarou +- Čárkovaně, protože díry jsou průchozí +- Do razítka +> Viditelný obrys je tlustá plná čára. Osa je samostatná hladina. + +## Proč se průchozí díra v tomto pohledu nekreslí čárkovaně? +- Čárkovaná čára je zakázaná +- [x] Její okraj je z pohledu vidět +- Díra se kreslí jen osou +- Měřítko je 1:2 +> Čárkovaně se kreslí hrana, kterou materiál zakrývá. Okraj díry v čelním pohledu vidíme. diff --git a/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.en.md b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.en.md new file mode 100644 index 0000000..0b5fb23 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.en.md @@ -0,0 +1,6 @@ +--- +nazev: Trim, extend and offset +popis: Fix lines that overshoot, and keep a constant gap. +nazev_cviceni: Quiz: trim and offset +nadpis_kvizu: Pick the command and the distance +--- diff --git a/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.md b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.md new file mode 100644 index 0000000..d132ae3 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/01-orez-a-ekvidistanta.md @@ -0,0 +1,80 @@ +--- +id: tg-2-orez-ekvidistanta +nazev: Ořez, prodloužení a ekvidistanta +popis: Úprava čar, které přesahují nebo mají stálý odstup. +nazev_cviceni: Kvíz: ořez a odstup +nadpis_kvizu: Vyberte příkaz a vzdálenost +--- + +# Výklad + +## Ořez a prodloužení +*Na hranu* + +- `_TRIM` uřízne kus objektu po hraně, kterou vyberete jako řeznou. +- V novějším AutoCADu stačí Enter bez výběru hran a řeže se o všechno, co čára protíná. +- `_EXTEND` dělá opak: natáhne čáru až k vybrané hraně. +- Maže se ta část, na kterou kliknete. Klik na špatnou stranu uřízne to, co mělo zůstat. + +> Tip: když ořez „nebere“, čáry se ve skutečnosti neprotínají. Přibližte roh a napojte ho úchopem. + +## Ekvidistanta +*Stálý odstup* + +- `_OFFSET` vytvoří rovnoběžnou křivku v zadané vzdálenosti. +- Hodí se na tloušťku stěny, rámeček i osazení. Zadáte vzdálenost a stranu. +- U kružnice vznikne soustředná kružnice. Průměr se změní o dvojnásobek odstupu. +- Ekvidistanta obdélníku udělá menší nebo větší obdélník se stejnými rohy, ne se zaoblením. + +> Tip: stěna tlustá 8 mm je ekvidistanta obrysu o 8 mm dovnitř. U kružnice Ø40 tak vznikne Ø24. + +## Co neřeže +*Otevřený roh* + +- Ořez potřebuje průsečík. Dvě čáry, které se jen míjejí, se neoříznou. +- Nejdřív je prodlužte nebo je nakreslete znovu s úchopem, teprve pak řežte. +- Ekvidistanta otevřené křivky zůstane otevřená. Rohy se samy nedovřou. +- Po ořezu zkontrolujte, že nezůstaly pahýly kratší než milimetr. Ty `_ERASE` smaže. + +> Tip: pahýl po ořezu je pořád objekt. Na tisku udělá tečku, která do výkresu nepatří. + +## Zadání: rámeček stěny +*Cvičení* + +- Vnější obdélník 100 × 60. Ekvidistantou o 8 mm dovnitř udělejte vnitřní obrys. +- Vnitřní obdélník má tedy 84 × 44. +- Čáry, které po dalších úpravách přesáhnou vnější roh, ořízněte. +- Oba obrysy nechte na hladině OBRYS. Kóty zatím nepřidávejte. + +> Tip: 100 minus dvakrát 8 je 84. 60 minus 16 je 44. Když vnitřek vyjde jinak, odstup šel ven, ne dovnitř. + + +# Kvíz + +## Který příkaz uřízne přesahující kus čáry? +- `_OFFSET` +- [x] `_TRIM` +- `_EXTEND` +- `_MOVE` +> Ořez bere kus za řeznou hranou. Prodloužení čáru naopak natahuje. + +## Ekvidistanta kružnice Ø40 o 8 mm dovnitř má průměr? +- 32 +- [x] 24 +- 48 +- 8 +> Poloměr klesne o 8, průměr o 16. 40 minus 16 je 24. + +## Proč ořez na dvou čarách nic neudělá? +- Orto je zapnuté +- [x] Čáry se neprotínají +- Chybí razítko +- Objekt je kružnice +> Bez průsečíku není kde řezat. Nejdřív čáry napojte, potom řežte. + +## Vnitřní obrys obdélníku 100 × 60 po ekvidistantě 8 mm dovnitř je? +- 100 × 60 +- 92 × 52 +- [x] 84 × 44 +- 108 × 68 +> Z šířky i výšky se ubere 8 mm z každé strany, tedy 16 mm. 84 × 44. diff --git a/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.en.md b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.en.md new file mode 100644 index 0000000..30afc38 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Fillets and chamfers +popis: A corner by a radius or by two distances. +nazev_cviceni: Quiz: corners +nadpis_kvizu: Work out the corner +--- diff --git a/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.md b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.md new file mode 100644 index 0000000..17e40f1 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/02-zaobleni-a-zkoseni.md @@ -0,0 +1,80 @@ +--- +id: tg-2-zaobleni-zkoseni +nazev: Zaoblení a zkosení +popis: Roh poloměrem nebo dvěma vzdálenostmi. +nazev_cviceni: Kvíz: rohy +nadpis_kvizu: Dopočítejte roh +--- + +# Výklad + +## Zaoblení +*Poloměr* + +- `_FILLET` spojí dvě úsečky obloukem. Volba R nastaví poloměr. +- Poloměr 8 znamená oblouk o poloměru 8 mm, ne průměr. +- R0 ostrý roh jen napojí, oblouk nevznikne. Hodí se na nedotažené čáry. +- Příkaz si poloměr pamatuje. Než zaoblíte jiný roh, R zkontrolujte. + +> Tip: u drážky se zaobleným koncem je poloměr polovina šířky drážky. Drážka 16 mm má R8. + +## Zkosení +*Dvě vzdálenosti* + +- `_CHAMFER` srazí roh úsečkou. Volba D zadá dvě vzdálenosti od vrcholu. +- Stejné vzdálenosti 2 a 2 srazí roh pod 45 stupňů, pokud jsou hrany kolmé. +- Různé vzdálenosti, třeba 2 a 4, dají šikminu, která není 45 stupňů. +- Zkosení se kótuje délkou a úhlem, nebo oběma délkami. Ne poloměrem. + +> Tip: na výkresu je zkosení 2 × 45°. V příkazu jsou to vzdálenosti 2 a 2, ne úhel napsaný místo D. + +## Kdy který roh +*Dílec* + +- Zaoblení bývá tam, kde se obrábí frézou nebo kde ostrá hrana vadí. +- Zkosení bývá na hraně díry a na konci hřídele, aby díl navedl protikus. +- Na jednom rohu nedávejte zaoblení i zkosení. Zadání říká jen jedno. +- Po zaoblení se změní délka rovné hrany. Kóta rovné části už není celá šířka. + +> Tip: hrana dlouhá 40 mm se dvěma zaobleními R5 na koncích má rovný úsek 30 mm. + +## Zadání: drážka a sražení +*Cvičení* + +- Obdélník 80 × 30. Na obou kratších koncích `_FILLET` s R8, na obou dlouhých hranách. +- Vznikne oválná drážka: rovný střed je dlouhý 64 mm, šířka zůstane 30? Pozor, R8 na výšce 30 nejde, šířka drážky musí být 16. +- Překreslete tvar jako drážku 64 × 16 a konce zaoblete R8. +- Jeden vnější roh destičky 80 × 50 mimo drážku srazte `_CHAMFER` 2 a 2. + +> Tip: poloměr zaoblení konce drážky se rovná polovině její šířky. 16 / 2 = 8. + + +# Kvíz + +## Co nastaví `_FILLET` volbou R8? +- Zkosení 8 × 8 +- [x] Oblouk o poloměru 8 mm +- Průměr 8 +- Osazení o 8 mm +> R je poloměr oblouku. Průměr oblouku by byl 16. + +## Jak v příkazu zadáte zkosení 2 × 45° na kolmém rohu? +- R2 +- [x] Vzdálenosti 2 a 2 +- Úhel 45 místo obou vzdáleností, bez délek +- Ekvidistantu 2 +> Stejné vzdálenosti na kolmých hranách dají 45 stupňů. Do příkazu patří délky. + +## Jaký poloměr má konec drážky široké 16 mm? +- 16 +- [x] 8 +- 4 +- 32 +> Konec je půlkruh. Jeho poloměr je polovina šířky. + +## Co udělá zaoblení R0? +- Smaže roh +- [x] Napojí čáry ostrým rohem +- Udělá kružnici +- Nastaví měřítko +> Nulový poloměr je způsob, jak dvě minuté čáry spojit do ostrého rohu. diff --git a/content/technicka-grafika/2-rocnik/03-hladiny.en.md b/content/technicka-grafika/2-rocnik/03-hladiny.en.md new file mode 100644 index 0000000..ecdd7b2 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/03-hladiny.en.md @@ -0,0 +1,6 @@ +--- +nazev: Layers +popis: Colour, linetype and weight in one place. +nazev_cviceni: Quiz: layers +nadpis_kvizu: Decide which layer an object belongs on +--- diff --git a/content/technicka-grafika/2-rocnik/03-hladiny.md b/content/technicka-grafika/2-rocnik/03-hladiny.md new file mode 100644 index 0000000..6a93333 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/03-hladiny.md @@ -0,0 +1,80 @@ +--- +id: tg-2-hladiny +nazev: Hladiny +popis: Barva, typ čáry a tloušťka na jednom místě. +nazev_cviceni: Kvíz: hladiny +nadpis_kvizu: Rozhodněte, co patří na kterou hladinu +--- + +# Výklad + +## Proč hladiny +*Pořádek* + +- Hladina nese barvu, typ čáry i tloušťku. Objekt, který na ní vznikne, je zdědí. +- Obrys, osa, skrytá hrana, šrafa, kóta a rámeček jsou samostatné hladiny. +- Vypnutá hladina na obrazovce zmizí, ale tisknout se pořád může. Zmrazená se netiskne a nepřepočítává. +- Zamčená hladina je vidět, ale nejde omylem posunout. + +> Tip: kóty nikdy nekreslete na OBRYS. Až budete obrys měnit, kóty by odjely s ním. + +## Aktuální hladina +*Než kliknete* + +- Nový objekt padne na aktuální hladinu. Je to ta, která je ve správci vybraná jako současná. +- `_LAYER` založí hladinu. Poklepáním ji nastavíte jako aktuální. +- Objekt na špatné hladině přesuňte ve vlastnostech, příkaz `_PROPERTIES` nebo horní lišta. +- Barvu a typ čáry nechte ByLayer. Barva nastavená na objektu se se hladinou přestane řídit. + +> Tip: ByLayer znamená „ber to, co říká hladina“. Je to výchozí a na školním výkresu má tak zůstat. + +## Názvy, které obstojí +*Úmluva* + +- OBRYS, OSA, SKRYTE, SRAFA, KOTA, RAM, TEXT. Krátce a bez mezer. +- OBRYS je bílá nebo černá, tloušťka 0,50. Ostatní tenké, 0,25, každá svou barvou. +- SKRYTE má typ DASHED, OSA typ CENTER. Ostatní Continuous. +- Hladinu 0 nemažte a nekreslete na ni součást. Je systémová. + +> Tip: hladina 0 je schovaná v blocích. Součást na ní se později špatně barví. + +## Zadání: šest hladin +*Cvičení* + +- V souboru destičky založte OBRYS, OSA, SKRYTE, SRAFA, KOTA a RAM. +- Obrys přesuňte na OBRYS, osy na OSA, rámeček na RAM. +- OSA musí mít typ CENTER a po `_LTSCALE` 0,5 nebo 1 musí být čerchování vidět. +- Vypněte OBRYS. Na obrazovce smí zůstat osy a rámeček, nic z tvaru. + +> Tip: když osa vypadá plně, typ čáry není načtený, nebo je LTSCALE mimo rozumné měřítko. + + +# Kvíz + +## Kam patří kóta? +- Na hladinu OBRYS +- [x] Na vlastní hladinu KOTA +- Na hladinu 0 +- Kamkoli, hladina je jen barva pozadí +> Kóta má vlastní tenkou čáru a při úpravě obrysu nesmí odjet s ním. + +## Co znamená ByLayer? +- Objekt barvu hladiny ignoruje +- [x] Objekt si bere barvu a typ čáry z hladiny +- Hladina je zamčená +- Objekt se netiskne +> ByLayer je správné nastavení. Barva „natvrdo“ na objektu se hladinou už neřídí. + +## Čím se liší vypnutá a zmrazená hladina? +- Ničím +- [x] Zmrazená se navíc netiskne a výkres ji nepočítá +- Vypnutá se nedá znovu zapnout +- Zmrazená je vidět červeně +> Vypnutí je jen obrazovka. Zmrazení hladinu vyřadí i z tisku a z přepočtu. + +## Proč nekreslit součást na hladinu 0? +- Hladina 0 nejde použít +- [x] Je systémová a v blocích se chová jinak +- Nepouští tlusté čáry +- Smí na ni jen razítko +> Hladina 0 má zvláštní roli uvnitř bloků. Součást patří na pojmenované hladiny. diff --git a/content/technicka-grafika/2-rocnik/04-srafy.en.md b/content/technicka-grafika/2-rocnik/04-srafy.en.md new file mode 100644 index 0000000..7619115 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/04-srafy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Hatches +popis: Sectioned material, hatch scale and a closed boundary. +nazev_cviceni: Quiz: hatches +nadpis_kvizu: Decide what gets hatched +--- diff --git a/content/technicka-grafika/2-rocnik/04-srafy.md b/content/technicka-grafika/2-rocnik/04-srafy.md new file mode 100644 index 0000000..300909f --- /dev/null +++ b/content/technicka-grafika/2-rocnik/04-srafy.md @@ -0,0 +1,80 @@ +--- +id: tg-2-srafy +nazev: Šrafy +popis: Řez materiálem, měřítko šrafy a uzavřená oblast. +nazev_cviceni: Kvíz: šrafy +nadpis_kvizu: Rozhodněte, co se šrafuje +--- + +# Výklad + +## Co šrafa říká +*Materiál* + +- Šrafa v řezu znamená materiál, který řezná rovina proťala. +- Ocel se na školním výkresu šrafuje tenkými čarami pod 45 stupňů. +- Dva sousední díly mají šrafu na opačnou stranu, nebo jinou rozteč. +- Díra, drážka a prázdno se nešrafují. Není tam materiál. + +> Tip: šrafa pod 45 stupni nesmí být rovnoběžná s obrysem. Když je obrys šikmý, úhel šrafy změňte. + +## Příkaz +*Uzavřená oblast* + +- `_HATCH` a vzor ANSI31. Klikněte dovnitř uzavřené oblasti. +- Oblast musí být dovřená. Mezera v rohu šrafu zastaví nebo ji vylije ven. +- Měřítko vzoru zvětší rozteč čar. Moc hustá šrafa na tisku zčerná. +- Šrafa patří na hladinu SRAFA, tenkou čarou. Ne na OBRYS. + +> Tip: asociativní šrafa se přizpůsobí, když obrys později natáhnete. Na školní práci ji nechte zapnutou. + +## Co se v podélném řezu nešrafuje +*Výjimky* + +- Hřídel, žebro, šroub a pero v podélném řezu zůstávají bez šrafy. +- V příčném řezu se tatáž hřídel šrafuje. Rozhoduje směr řezu, ne součást. +- Tenká stěna řezaná podélně se šrafuje. Žebro ne, aby nepůsobilo jako plný kus. +- Obrys šrafované plochy je pořád tlustá čára. Šrafa ho nenahrazuje. + +> Tip: podélný řez hřídele ukáže osazení obrysem. Šrafa by z hřídele udělala zdánlivě plný hranol. + +## Zadání: řez destičkou +*Cvičení* + +- Destičku 80 × 20 v řezu A-A vyšrafujte ANSI31, měřítko tak, aby rozteč byla zhruba 2 až 3 mm. +- Díru Ø10 uprostřed nešrafujte. Šrafa končí na její hraně. +- Šrafu dejte na hladinu SRAFA. Vypnutím této hladiny musí zmizet jen šrafa, obrys zůstane. +- Když šrafa vyteče ven, najděte mezeru v obrysu a roh napojte. + +> Tip: jedna oblast vlevo od díry a jedna vpravo. Obě mohou být jedna šrafa, pokud příkaz umí víc oblastí. + + +# Kvíz + +## Co znamená šrafa v řezu? +- Skrytou hranu +- [x] Materiál proťatý řeznou rovinou +- Závit +- Měřítko 1:2 +> Šrafuje se jen to, co řez skutečně prochází. Díra je prázdná. + +## Jaký vzor se na škole používá pro ocel? +- Plná výplň +- [x] ANSI31, tenké čáry pod 45 stupňů +- Tečkování +- Čerchovaná osa +> ANSI31 je běžný strojařský vzor. Úhel změňte, jen když by splynul s obrysem. + +## Proč šrafa vyteče mimo součást? +- Je zapnuté orto +- [x] Obrys není uzavřený +- Chybí kóta +- Hladina je ByLayer +> I malá mezera v rohu oblast otevře. Napojte roh a šrafu opakujte. + +## Šrafuje se hřídel v podélném řezu? +- Ano, vždy +- [x] Ne +- Jen když má drážku +- Jen ve 3D +> Hřídel, žebro a šroub se v podélném řezu nechávají prázdné. V příčném řezu se hřídel šrafuje. diff --git a/content/technicka-grafika/2-rocnik/05-koty-linearni.en.md b/content/technicka-grafika/2-rocnik/05-koty-linearni.en.md new file mode 100644 index 0000000..46918a3 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/05-koty-linearni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Linear dimensions +popis: The real size, extension lines and a dimension style. +nazev_cviceni: Quiz: linear dimensions +nadpis_kvizu: Read the dimension correctly +--- diff --git a/content/technicka-grafika/2-rocnik/05-koty-linearni.md b/content/technicka-grafika/2-rocnik/05-koty-linearni.md new file mode 100644 index 0000000..20556d0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/05-koty-linearni.md @@ -0,0 +1,80 @@ +--- +id: tg-2-koty-linearni +nazev: Lineární kóty +popis: Skutečný rozměr, pomocné čáry a styl kót. +nazev_cviceni: Kvíz: lineární kóty +nadpis_kvizu: Přečtěte kótu správně +--- + +# Výklad + +## Co kóta říká +*Skutečný rozměr* + +- Kóta je skutečný rozměr součásti, ne délka čáry na papíře. +- Na výkresu 1:2 je čára poloviční a číslo zůstává celé. +- `_DIMLINEAR` chce počátek první pomocné čáry, druhé a polohu kótovací čáry. +- Body chytejte úchopem. Kóta z bodu vedle rohu je o milimetr špatně a tváří se přesně. + +> Tip: číslo na kótě nepřepisujte ručně. Když nesedí, je špatně geometrie, ne text. + +## Vzhled +*Čitelné číslo* + +- Pomocné čáry odstupují od obrysu asi o 1 mm a přesahují kótovací čáru. +- Sousední kóty mají stejný odstup, třeba 10 mm od obrysu a 7 mm mezi sebou. +- Číslo se píše nad kótovací čáru, ne přes obrys. Kóta neprotíná šrafu, když to jde jinak. +- `_DIMSTYLE` nastaví šipky, písmo a jednotky jednou pro celý výkres. + +> Tip: výška písma na A4 bývá 3,5 mm. Menší text se na tisku nedá číst. + +## Řetězec a základna +*Víc rozměrů* + +- Řetězec klade kóty za sebou. Součet dílčích kót má dát celkový rozměr. +- Kóta od základny měří všechno od jedné hrany. Hodí se na rozteče od dorazu. +- Celkový rozměr a součet dílčích se na jedné hraně neopakují bez důvodu. +- Kóta patří tam, odkud se rozměr bude vyrábět, ne doprostřed šrafy. + +> Tip: 20 + 45 + 15 = 80. Když součet nesedí s celkovou kótou, jedna pomocná čára minula roh. + +## Zadání: destička 120 × 70 +*Cvičení* + +- Okótujte šířku 120 a výšku 70. +- Vodorovně přidejte 15 od levého kraje ke středu díry a 90 mezi středy děr. +- Svisle 15 od dolní hrany ke středu díry. +- Všechny kóty na hladinu KOTA. `_DIST` mezi rohy musí ukázat stejná čísla jako kóty. + +> Tip: kótujte středy děr, ne jejich okraje. Okraj určí až kóta průměru. + + +# Kvíz + +## Co se píše do lineární kóty na výkresu 1:2? +- Polovina skutečného rozměru +- [x] Skutečný rozměr +- Dvojnásobek +- Měřítko 1:2 místo čísla +> Měřítko mění délku čáry na papíře. Číslo kóty je rozměr součásti. + +## Proč nepřepisovat text kóty ručně? +- AutoCAD to zakazuje vždy +- [x] Špatné číslo skrývá chybu geometrie +- Text musí být v palcích +- Kóta by zmizela z hladiny +> Správná kóta měří geometrii. Ruční text 120 na čáře dlouhé 119 je lež. + +## Od čeho se kótuje poloha díry? +- Od okraje kružnice +- [x] Od středu +- Od osy razítka +- Od nejbližší šrafy +> Výroba potřebuje střed. Průměr díry je samostatná kóta. + +## Co má platit pro řetězec 20, 45 a 15 na součásti dlouhé 80? +- Součet má být 100 +- [x] Součet má být 80 +- Celková kóta se vynechá vždy +- Řetězec se kreslí čerchovaně +> 20 + 45 + 15 = 80. Nesoulad znamená minutý úchop na některé pomocné čáře. diff --git a/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.en.md b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.en.md new file mode 100644 index 0000000..9b60f02 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.en.md @@ -0,0 +1,6 @@ +--- +nazev: Diameter, radius and angle dimensions +popis: The diameter symbol, a fillet radius and a chamfer angle. +nazev_cviceni: Quiz: diameter and angle +nadpis_kvizu: Match the symbol to the size +--- diff --git a/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.md b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.md new file mode 100644 index 0000000..17ce4d0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/06-koty-prumeru-a-uhlu.md @@ -0,0 +1,80 @@ +--- +id: tg-2-koty-prumeru +nazev: Kóty průměru, poloměru a úhlu +popis: Značka průměru, poloměr zaoblení a úhel zkosení. +nazev_cviceni: Kvíz: průměr a úhel +nadpis_kvizu: Doplňte značku ke správnému rozměru +--- + +# Výklad + +## Průměr a poloměr +*Díra a oblouk* + +- `_DIMDIAMETER` kótuje kružnici. Před číslo patří značka průměru. +- `_DIMRADIUS` kótuje oblouk. Před číslo patří R. +- Díra má kótu průměru, i když jste ji kreslili poloměrem. Výroba vrtá průměrem. +- Zaoblení rohu má kótu poloměru. Jedno R na výkresu platí pro všechny stejné rohy, když to poznámka říká. + +> Tip: díru Ø8 nekótujte jako R4. Nástroj se jmenuje podle průměru. + +## Úhel +*Zkosení a klín* + +- `_DIMANGULAR` měří úhel mezi dvěma úsečkami nebo obloukem. +- Zkosení 2 × 45° se často zapíše poznámkou, ne obloukovou kótou v každém rohu. +- Úhel se měří ve stupních. 45 stupňů je polovina pravého úhlu. +- Kóta úhlu nesmí překrýt číslo sousední délky. Odsuňte ji dál od vrcholu. + +> Tip: pravý úhel se na strojařském výkresu obvykle nekótuje. 90 stupňů se předpokládá. + +## Kolik desetin +*Přesnost* + +- Školní kóta celého milimetru nemá zbytečné desetinné nuly, pokud styl neříká jinak. +- Průměr a poloměr zaokrouhlete stejně jako délky. Nemíchejte 8 a 8,00 bez důvodu. +- `_DIMSTYLE` nastaví přesnost i značky najednou. Neupravujte každou kótu ručně. +- Kóta, která ukazuje 7,99 místo 8, minula střed. Geometrii opravte, text ne. + +> Tip: 7,99 je skoro jistě úchop vedle středu. `_ID` středu díry to ukáže. + +## Zadání: díra a roh +*Cvičení* + +- Na destičce okótujte díru Ø8 příkazem `_DIMDIAMETER`. +- Zaoblení R8 na drážce okótujte `_DIMRADIUS` jednou. K ostatním stejné poznámkou. +- Zkosení 2 × 45° zapište k příslušné hraně. +- Žádná kóta průměru nesmí ukazovat 4 ani R8 u díry, která se vrtá. + +> Tip: čtyři stejné díry se kótují jednou, s poznámkou 4× Ø8, plus rozteč středů. + + +# Kvíz + +## Jak se kótuje vrtaná díra o průměru 8 mm? +- R8 +- R4 +- [x] Ø8 +- 8 bez značky, jako délka +> Vrták se volí podle průměru. Poloměr by výrobce přepočítával a snadno spletl. + +## Co znamená R8? +- Průměr 8 +- [x] Poloměr zaoblení 8 mm +- Rozteč 8 mm +- Tloušťka čáry 8 +> R je poloměr oblouku. U díry se R nepoužívá. + +## Kóta díry ukazuje 7,99. Co s tím? +- Přepsat text na 8 +- [x] Opravit geometrii, střed minul úchop +- Smazat všechny kóty +- Změnit měřítko na 1:2 +> Text by lež zakryl. Střed díry má ležet přesně tam, kde ho kóty polohy uvádějí. + +## Jak jednou kótou popsat čtyři stejné díry Ø8? +- Čtyřikrát opsat Ø8 +- [x] 4× Ø8 a rozteč středů +- Jen jednu díru a ostatní nechat bez kóty +- R4 krát čtyři +> Počet, průměr a poloha středů stačí. Opakovat stejnou kótu u každé díry výkres zanáší. diff --git a/content/technicka-grafika/2-rocnik/07-pole.en.md b/content/technicka-grafika/2-rocnik/07-pole.en.md new file mode 100644 index 0000000..23f02f0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/07-pole.en.md @@ -0,0 +1,6 @@ +--- +nazev: Rectangular and polar arrays +popis: Repeating holes in a row or on a circle. +nazev_cviceni: Quiz: arrays +nadpis_kvizu: Work out the spacing and the count +--- diff --git a/content/technicka-grafika/2-rocnik/07-pole.md b/content/technicka-grafika/2-rocnik/07-pole.md new file mode 100644 index 0000000..c0f8fd0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/07-pole.md @@ -0,0 +1,80 @@ +--- +id: tg-2-pole +nazev: Obdélníkové a polární pole +popis: Opakování děr v řadě nebo na kružnici. +nazev_cviceni: Kvíz: pole +nadpis_kvizu: Dopočítejte rozteč a počet +--- + +# Výklad + +## Obdélníkové pole +*Řady a sloupce* + +- `_ARRAYRECT` rozkopíruje objekt do řádků a sloupců. +- Zadává se počet sloupců, počet řádků a rozteč, ne celkový rozměr. +- Rozteč 30 při třech sloupcích dá od první osy ke třetí 60 mm. +- Pole může zůstat svázané. Úprava jedné díry se propíše do ostatních. + +> Tip: tři díry v řadě mají dvě mezery. Celková délka je dvojnásobek rozteče, ne trojnásobek. + +## Polární pole +*Na kružnici* + +- `_ARRAYPOLAR` chce střed otáčení, počet prvků a úhel vyplnění. +- Čtyři díry po 360 stupních jsou po 90 stupních. +- Vzorová díra musí ležet na roztečné kružnici dřív, než pole spustíte. +- Střed pole je střed příruby, ne střed první díry. + +> Tip: roztečná kružnice Ø60 má poloměr 30. Vzorová díra leží třeba v bodě 30,0. + +## Roztečná kružnice se nekreslí tlustě +*Osa* + +- Kružnice, na které leží středy děr, je tenká čerchovaná osa. +- Není to hrana součásti a nepatří na OBRYS. +- Kótuje se jejím průměrem, často jako Ø60, a počtem děr. +- Samotné díry mají svou kótu průměru jen jednou. + +> Tip: na výkresu čtete 4× Ø8 na Ø60. Osm je díra, 60 je kružnice středů. + +## Zadání: čtyři díry na kružnici +*Cvičení* + +- Příruba je kružnice Ø80, střed 0,0. +- Vzorovou díru Ø8 dejte středem do 30,0. +- `_ARRAYPOLAR` kolem 0,0, čtyři prvky, 360 stupňů. +- Roztečnou kružnici Ø60 dejte na hladinu OSA. Ověřte `_DIST` mezi sousedními středy. + +> Tip: sousední středy svírají 90 stupňů na poloměru 30. Vzdálenost je 30 × √2, asi 42,4 mm. + + +# Kvíz + +## Tři díry v řadě mají rozteč 30. Jak daleko je první osa od třetí? +- 30 +- 90 +- [x] 60 +- 120 +> Mezi třemi dírami jsou dvě mezery. 2 × 30 = 60. + +## Kam se umísťuje vzorová díra pro pole na Ø60? +- Do středu příruby +- [x] Na poloměr 30 od středu +- 60 mm od středu +- Kamkoli, pole si poloměr dopočítá +> Ø60 znamená poloměr 30. Střed vzorové díry musí na této kružnici už ležet. + +## Čím je nakreslená kružnice středů děr? +- Tlustým obrysem +- [x] Tenkou čerchovanou osou +- Šrafou +- Kótovací čarou +> Není to hrana materiálu. Je to osa rozteče a patří na hladinu OSA. + +## Co znamená zápis 4× Ø8 na Ø60? +- Čtyři díry o průměru 60 +- [x] Čtyři díry Ø8, jejich středy leží na Ø60 +- Osazení 4 mm +- Zkosení 8 × 60 +> První průměr je díra, druhý je roztečná kružnice. diff --git a/content/technicka-grafika/2-rocnik/08-zadani-priruba.en.md b/content/technicka-grafika/2-rocnik/08-zadani-priruba.en.md new file mode 100644 index 0000000..1bb0ae0 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/08-zadani-priruba.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a flange +popis: A drawing with an offset, a hole pattern, a hatch and dimensions. +nazev_cviceni: Quiz: the flange +nadpis_kvizu: Check the flange before you hand it in +--- diff --git a/content/technicka-grafika/2-rocnik/08-zadani-priruba.md b/content/technicka-grafika/2-rocnik/08-zadani-priruba.md new file mode 100644 index 0000000..6c75f73 --- /dev/null +++ b/content/technicka-grafika/2-rocnik/08-zadani-priruba.md @@ -0,0 +1,80 @@ +--- +id: tg-2-zadani-priruba +nazev: Zadání: příruba +popis: Výkres s ekvidistantou, polem děr, šrafou a kótami. +nazev_cviceni: Kvíz: příruba +nadpis_kvizu: Zkontrolujte přírubu před odevzdáním +--- + +# Výklad + +## Tvar +*Rozměry* + +- Příruba Ø100, střed 0,0. Středová díra Ø40. +- Čtyři díry Ø9 na roztečné kružnici Ø70, po 90 stupních. +- Vnější hrana má zkosení 1 × 45°, které v tomto pohledu vidíte jako tenkou kružnici Ø98. +- Tloušťka příruby 12 mm se okótuje v druhém pohledu nebo v řezu. V čelním pohledu je kružnice. + +> Tip: rozteč Ø70 znamená vzorovou díru ve vzdálenosti 35 mm od středu. + +## Řez +*Aby byla vidět tloušťka* + +- Druhý pohled je řez přes osu. Ukáže tloušťku 12 a průchod díry Ø40. +- V řezu se šrafuje materiál příruby. Díra Ø40 zůstane prázdná. +- Díry Ø9 v tomto řezu nemusí vyjít všechny. Řezná rovina bere dvě protilehlé, nebo žádnou, podle úhlu. +- Zvolte řez rovinou, která prochází dvěma protilehlými dírami Ø9, ať je v řezu vidět i jejich průchod. + +> Tip: rovina pod úhlem 0 stupňů vezme díry na ose X. Jejich průchod je Ø9 skrz tloušťku 12. + +## Kóty a hladiny +*Jednou* + +- Kóty: Ø100, Ø40, 4× Ø9 na Ø70, tloušťka 12, zkosení 1 × 45°. +- OBRYS, OSA, SRAFA, KOTA, RAM. Typy čar jako ve druháku. +- Razítko: Příruba, 1:1, ocel. +- Formát A4 na šířku nebo A3, když se řez vedle čela nevejde čitelně. + +> Tip: kóta 4× Ø9 se neopakuje u každé díry. Roztečná kružnice má vlastní kótu Ø70. + +## Kontrola +*Před tiskem* + +- `_DIST` od středu k ose díry Ø9 je 35. +- Šrafa nezasahuje do díry Ø40 ani do děr Ø9. +- Vypnutí hladiny KOTA nechá čistý tvar. Čísla nezmizí z obrysu, protože na něm nemají být. +- Soubor priruba.dwg, jednotky milimetry, žádná kóta přepsaná ručně. + +> Tip: když kóta Ø100 ukazuje 99,9, vnější kružnice nemá průměr 100. Opravte kružnici, ne text. + + +# Kvíz + +## Jak daleko od středu leží osa díry na roztečné kružnici Ø70? +- 70 +- [x] 35 +- 9 +- 100 +> Rozteč se kótuje průměrem. Poloměr, tedy vzdálenost středu díry, je 35. + +## Jak jednou kótou popsat čtyři malé díry? +- Ø9 +- Ø70 +- [x] 4× Ø9 +- R4,5 +> Počet krát průměr. Poloha je až kóta roztečné kružnice. + +## Co se v řezu příruby nešrafuje? +- Těleso příruby +- [x] Průchod díry Ø40 +- Obrys +- Razítko se šrafuje vždy +> Šrafuje se materiál. Díra je prázdná, šrafa končí na její hraně. + +## Kóta vnějšího průměru ukazuje 99,9. Co uděláte? +- Přepíšete ji na 100 +- [x] Opravíte kružnici na Ø100 +- Změníte měřítko na 1:2 +- Smažete řez +> Kóta měří geometrii. Ruční text by zakryl, že kružnice je špatně. diff --git a/content/technicka-grafika/3-rocnik/01-pohledy.en.md b/content/technicka-grafika/3-rocnik/01-pohledy.en.md new file mode 100644 index 0000000..a25979f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/01-pohledy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Views and first-angle projection +popis: Front, top and side views the way they are drawn here. +nazev_cviceni: Quiz: views +nadpis_kvizu: Put the view in the right place +--- diff --git a/content/technicka-grafika/3-rocnik/01-pohledy.md b/content/technicka-grafika/3-rocnik/01-pohledy.md new file mode 100644 index 0000000..f247128 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/01-pohledy.md @@ -0,0 +1,80 @@ +--- +id: tg-3-pohledy +nazev: Pohledy a evropské promítání +popis: Nárys, půdorys a bokorys tak, jak se kreslí u nás. +nazev_cviceni: Kvíz: pohledy +nadpis_kvizu: Umístěte pohled na správné místo +--- + +# Výklad + +## Tři základní pohledy +*Nárys* + +- Nárys je pohled zepředu. Vybírá se tak, aby ukázal tvar co nejlíp. +- Půdorys je pohled shora. Bokorys je pohled zleva. +- V evropském promítání leží půdorys pod nárysem, ne nad ním. +- Bokorys zleva leží vpravo od nárysu. Předmět si představte překlopený k vám. + +> Tip: americké promítání má půdorys nahoře. Na českém školním výkresu se nekreslí. + +## Co se v pohledu opakuje +*Stejná hrana* + +- Výška nárysu a výška bokorysu je stejné číslo. Šířka nárysu a šířka půdorysu taky. +- Hloubka půdorysu se rovná šířce bokorysu. Pomůže si 45stupňová pomocná čára. +- Skrytá hrana se v pohledu, kde není vidět, kreslí tenkou čárkovanou čarou. +- Hrana, která v daném pohledu splývá s obrysem, se podruhé nekreslí. + +> Tip: když je nárys vysoký 40, bokorys nemůže být vysoký 36. Někde chybí hrana nebo měřítko. + +## Zarovnání +*Jedna osa* + +- Pohledy se zarovnají. Svislé hrany nárysu a půdorysu leží na stejných svislicích. +- Vodorovné hrany nárysu a bokorysu leží na stejných vodorovných přímkách. +- Mezi pohledy nechte mezeru na kóty, třeba 30 až 40 mm. Pohledy se nesmí překrývat. +- Osa souměrnosti prochází všemi pohledy, ve kterých je součást souměrná. + +> Tip: `_XLINE` pod 45 stupňů přenese hloubku z půdorysu do bokorysu. Po dokreslení ji smažte nebo dejte na pomocnou hladinu. + +## Zadání: kvádr s osazením +*Cvičení* + +- Kvádr 60 × 40 × 30. Zepředu je na něm osazení: pravá část je o 10 mm nižší, v délce 20 mm. +- Nárys je 60 široký a 30 vysoký. Půdorys pod ním je 60 × 40. +- Bokorys vpravo je 40 široký a 30 vysoký. Osazení v něm vyjde jako skrytá nebo viditelná hrana podle směru. +- Pohledy zarovnejte. Výška 30 musí sedět v nárysu i v bokorysu. + +> Tip: osazení dlouhé 20 a nižší o 10 znamená, že v nárysu je horní hrana 40 mm v plné výšce a 20 mm ve výšce 20. + + +# Kvíz + +## Kam se v evropském promítání kreslí půdorys? +- Nad nárys +- [x] Pod nárys +- Vlevo od nárysu +- Místo bokorysu +> Evropské promítání klade pohled shora pod pohled zepředu. Nahoře by byl americký zvyk. + +## Co musí mít nárys a bokorys společné? +- Šířku +- [x] Výšku +- Hloubku jako jediné číslo bez převodu +- Razítko +> Výška předmětu je v obou pohledech stejná. Hloubka se přenáší mezi půdorysem a bokorysem. + +## Jak se kreslí hrana, kterou v daném pohledu není vidět? +- Tlustou plnou +- [x] Tenkou čárkovanou +- Čerchovanou osou +- Nezobrazuje se nikdy +> Skrytá hrana je čárkovaná. Vynechat ji smíte jen tam, kde by výkres zbytečně zanesla a norma to dovolí. + +## Kvádr je vysoký 30. Jak vysoký je jeho bokorys? +- 40 +- [x] 30 +- 60 +- Podle měřítka jinak +> Výška se mezi nárysem a bokorysem nemění. Měřítko je pro celý výkres jedno. diff --git a/content/technicka-grafika/3-rocnik/02-rezy.en.md b/content/technicka-grafika/3-rocnik/02-rezy.en.md new file mode 100644 index 0000000..3b30d5d --- /dev/null +++ b/content/technicka-grafika/3-rocnik/02-rezy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sections and cross-sections +popis: The cutting plane, the arrows and what gets hatched. +nazev_cviceni: Quiz: sections +nadpis_kvizu: Read the cutting plane +--- diff --git a/content/technicka-grafika/3-rocnik/02-rezy.md b/content/technicka-grafika/3-rocnik/02-rezy.md new file mode 100644 index 0000000..0845683 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/02-rezy.md @@ -0,0 +1,80 @@ +--- +id: tg-3-rezy +nazev: Řezy a průřezy +popis: Řezná rovina, šipky a co se šrafuje. +nazev_cviceni: Kvíz: řezy +nadpis_kvizu: Přečtěte řeznou rovinu +--- + +# Výklad + +## Úplný řez +*Rovina A-A* + +- Řez ukáže vnitřek, který by jinak byl samá čárkovaná čára. +- Řezná rovina se v pohledu označí tlustou čerchovanou čarou se šipkami a písmeny, třeba A. +- Obraz řezu se popíše A-A. Šipky říkají, kterým směrem se díváme. +- Materiál proťatý rovinou se šrafuje. Prázdné díry ne. + +> Tip: šipky řezu míří na plochu, kterou uvidíte. Obrácené šipky otočí celý obraz. + +## Poloviční řez +*Souměrná součást* + +- Souměrná součást smí mít půl pohledu a půl řezu v jednom obrazu. +- Dělí je osa. Pohled bývá nad osou nebo vlevo, řez pod osou nebo vpravo. +- Na ose mezi polovinou pohledu a řezu se nekreslí tlustý obrys. +- Skryté hrany v neřezané polovině se často vynechají, protože řez už vnitřek ukázal. + +> Tip: hřídel s dírou na ose je typický poloviční řez. Nahoře obrys, dole díra a šrafa. + +## Co řezem není +*Průřez* + +- Průřez ukáže jen tvar v řezné rovině, bez toho, co leží za ní. +- Řez ukáže i hrany za rovinou. Proto je na řezu víc čar než na průřezu. +- Tenký průřez se smí nakreslit přímo do pohledu, pootočený na místě. +- Žebro řezané podélně se nešrafuje ani v úplném řezu. + +> Tip: když za rovinou vidíte další osazení, je to řez. Když je tam jen vyšrafovaný profil, je to průřez. + +## Zadání: pouzdro +*Cvičení* + +- Pouzdro je válec Ø50, díra Ø30, délka 40. Nakreslete nárys jako úplný řez A-A osou. +- V řezu vyšrafujte mezikruží. Díra Ø30 zůstane prázdná po celé délce 40. +- Šipky A umístěte do půdorysu nebo do místa, odkud je rovina jasná. +- Kóty Ø50, Ø30 a 40. Hřídel to není, šrafa tu být má. + +> Tip: tloušťka stěny je (50 − 30) / 2 = 10 mm. V řezu je vidět vlevo i vpravo od díry. + + +# Kvíz + +## Co říkají šipky u řezné roviny? +- Tloušťku čáry +- [x] Směr pohledu na řez +- Měřítko šrafy +- Počet děr +> Obraz řezu je to, co bychom viděli ve směru šipek. Obrácené šipky obraz otočí. + +## Šrafuje se díra Ø30 v řezu pouzdra? +- Ano, je uprostřed +- [x] Ne, není tam materiál +- Jen v měřítku 1:2 +- Jen když je pouzdro z oceli +> Šrafuje se stěna. Průchozí díra zůstane bílá. + +## Čím se liší průřez od řezu? +- Průřez se nešrafuje +- [x] Průřez neukazuje hrany za řeznou rovinou +- Řez nemá písmena +- Průřez je vždy v měřítku 2:1 +> Řez dokreslí i to, co leží za rovinou. Průřez je jen proťatý tvar. + +## Kam se u souměrného polovičního řezu kreslí řez? +- Přes celý obraz +- [x] Jen na jednu stranu od osy +- Mimo pohled, bez označení +- Do razítka +> Druhá polovina zůstane pohledem. Dělí je osa, ne tlustá hrana. diff --git a/content/technicka-grafika/3-rocnik/03-bloky.en.md b/content/technicka-grafika/3-rocnik/03-bloky.en.md new file mode 100644 index 0000000..f9b3ccc --- /dev/null +++ b/content/technicka-grafika/3-rocnik/03-bloky.en.md @@ -0,0 +1,6 @@ +--- +nazev: Blocks +popis: A repeated part as one object with a base point. +nazev_cviceni: Quiz: blocks +nadpis_kvizu: Insert the block correctly +--- diff --git a/content/technicka-grafika/3-rocnik/03-bloky.md b/content/technicka-grafika/3-rocnik/03-bloky.md new file mode 100644 index 0000000..933c73f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/03-bloky.md @@ -0,0 +1,80 @@ +--- +id: tg-3-bloky +nazev: Bloky +popis: Opakovaný díl jako jeden objekt se základním bodem. +nazev_cviceni: Kvíz: bloky +nadpis_kvizu: Vložte blok správně +--- + +# Výklad + +## K čemu blok je +*Jeden díl* + +- Blok je pojmenovaná skupina objektů, která se vkládá jako jeden kus. +- Šroub, značka nebo razítko se nekreslí pokaždé znovu. +- Úprava definice bloku se propíše do všech vložení. Kopie by zůstaly staré. +- Blok patří do výkresu. `_WBLOCK` ho navíc uloží do vlastního souboru. + +> Tip: čtyři stejné šrouby jsou čtyři vložení jednoho bloku, ne čtyři překreslené šrouby. + +## Základní bod +*Kam se chytá* + +- `_BLOCK` se zeptá na jméno, základní bod a objekty. +- Základní bod je místo, za které budete blok vkládat. U šroubu třeba průsečík osy a dosedací plochy. +- Špatný základní bod posune každé vložení o stejnou chybu. +- Objekty bloku nedávejte na hladinu 0, pokud nemají přebírat barvu místa vložení. Na škole nechte jejich vlastní hladiny. + +> Tip: základní bod chytejte úchopem. Bod „někde u šroubu“ se při vložení mine o několik milimetrů. + +## Vložení +*Místo, měřítko, úhel* + +- `_INSERT` vybere jméno, bod vložení, měřítko a otočení. +- Měřítko 1 a úhel 0 vloží blok tak, jak byl nakreslen. +- Měřítko 2 ho zvětší. Na výkresu 1:1 se měřítko bloku nemění, mění se až měřítko výřezu. +- Rozbití bloku příkazem `_EXPLODE` z něj zase udělá obyčejné čáry. Dělejte to jen když máte měnit jediné vložení. + +> Tip: rozbitý blok se už neaktualizuje s definicí. Rozbijte ho, jen když opravdu chcete výjimku. + +## Zadání: šroub M8 jako blok +*Cvičení* + +- V nárysu nakreslete zjednodušený šroub: dřík Ø8 dlouhý 20 a hlava široká 13 vysoká 5,5. +- Základní bod dejte na osu, do dosedací plochy pod hlavou. +- Blok pojmenujte SROUB-M8 a vložte ho dvakrát, po 40 mm. +- Změňte délku dříku v definici na 25. Obě vložení se musí prodloužit. + +> Tip: dosedací plocha je rozhraní hlavy a dříku. Od ní se měří, jak hluboko šroub sedí. + + +# Kvíz + +## Čím se liší blok od obyčejné kopie? +- Ničím +- [x] Úprava definice se propíše do všech vložení +- Blok nejde otočit +- Kopie zabere méně místa +> Kopie jsou nezávislé. Vložení bloku sdílejí jednu definici. + +## Co je základní bod bloku? +- Střed razítka +- [x] Bod, za který se blok vkládá +- Vždy 0,0 výkresu +- Pravý horní roh objektů +> Špatný základní bod posune každé vložení. Chytejte ho úchopem na osu nebo na dosedací plochu. + +## Kdy blok rozbít příkazem `_EXPLODE`? +- Vždy po vložení +- [x] Jen když jedno vložení má být výjimka +- Aby se zmenšil soubor +- Před každým tiskem +> Rozbitý blok se s definicí už neaktualizuje. Ostatní vložení nechte celé. + +## Měřítko vložení 1 na výkresu 1:1 znamená? +- Blok se kreslí poloviční +- [x] Blok se vloží ve velikosti, v jaké byl nakreslen +- Blok se převrátí +- Měřítko razítka se změní na 1:2 +> Měřítko výkresu řeší až výřez na papíře. Blok součásti nechte v měřítku 1. diff --git a/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.en.md b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.en.md new file mode 100644 index 0000000..b19bfc1 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.en.md @@ -0,0 +1,6 @@ +--- +nazev: Attributes and the parts list +popis: Text in a block that changes with every insert. +nazev_cviceni: Quiz: attributes +nadpis_kvizu: Read the parts list +--- diff --git a/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.md b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.md new file mode 100644 index 0000000..03df34e --- /dev/null +++ b/content/technicka-grafika/3-rocnik/04-atributy-a-kusovnik.md @@ -0,0 +1,80 @@ +--- +id: tg-3-atributy +nazev: Atributy a kusovník +popis: Text v bloku, který se mění s každým vložením. +nazev_cviceni: Kvíz: atributy +nadpis_kvizu: Přečtěte kusovník +--- + +# Výklad + +## Atribut je popisek bloku +*Značka* + +- Atribut je text uložený v bloku. Při každém vložení se zeptá znovu. +- `_ATTDEF` založí značku, výzvu a výchozí hodnotu. Značka je třeba OZN. +- Do bloku se atribut vloží spolu s geometrií. Samotný atribut na výkresu nic neopakuje. +- Hodnotu už vloženého bloku změníte `_ATTEDIT` nebo dvojklikem, ne přepsáním čáry. + +> Tip: značka OZN není text, který se tiskne. Tiskne se hodnota, třeba 1 nebo M8×20. + +## Kusovník +*Co je na výkresu* + +- Kusovník je tabulka nad razítkem. Řádek je jedna položka sestavy. +- Sloupce bývají pozice, název, počet, materiál nebo norma a rozměr. +- Pozice v kolečku na výkresu musí souhlasit s řádkem tabulky. +- Počet se počítá z vložení, ne od oka. Dva stejné šrouby jsou počet 2, ne dva řádky. + +> Tip: tabulku `_TABLE` dejte na hladinu TEXT nebo RAM. Čáry tabulky nejsou obrys součásti. + +## Pořadí +*Nejdřív definice* + +- Nejdřív nakreslete díl, přidejte atribut, teprve pak z toho udělejte blok. +- Atribut přidaný až po vytvoření bloku se do starých vložení sám nedostane. +- `_BATTMAN` nebo nové definování bloku vložení srovná. Na škole je jistější blok založit znovu. +- Výchozí hodnota atributu je nápověda. U šroubu třeba M8×20, a při vložení ji smíte změnit. + +> Tip: prázdná hodnota v kusovníku znamená, že jste při vložení výzvu odklepli bez textu. + +## Zadání: tři položky +*Cvičení* + +- Sestava má přírubu (pozice 1, 1 kus, ocel), šroub M8×20 (pozice 2, 4 kusy) a matici M8 (pozice 3, 4 kusy). +- Šroub je blok s atributem OZN. Čtyři vložení mají stejnou hodnotu. +- Kusovník nad razítkem má tři řádky. Pozice 2 má počet 4, ne 1. +- Odkazové čáry z koleček 1, 2 a 3 míří na správný díl a nekříží se bez důvodu. + +> Tip: čtyři matice jsou pořád jedna položka. Do tabulky nepište čtyři řádky se stejným šroubem. + + +# Kvíz + +## Co se při vložení bloku s atributem ptá znovu? +- Barva hladiny +- [x] Hodnota atributu, třeba označení +- Základní bod definice +- Měřítko výkresu +> Geometrie bloku zůstává. Mění se text, který jste založili jako atribut. + +## Čtyři stejné šrouby v kusovníku jsou? +- Čtyři řádky +- [x] Jeden řádek s počtem 4 +- Pozice 1 až 4 +- Atribut bez bloku +> Stejný díl je jedna položka. Počet říká, kolikrát je vložený. + +## Kdy se atribut musí založit? +- Až po tisku +- [x] Před vytvořením bloku, spolu s geometrií +- Jen v razítku +- Atribut nejde uložit do bloku +> Atribut přidaný později se do existující definice sám nepřidá. + +## Kam patří kusovník? +- Přes nárys +- [x] Nad razítko +- Na hladinu OBRYS +- Mimo rámeček +> Čte se spolu s razítkem. Čáry tabulky nejsou hrany součásti. diff --git a/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.en.md b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.en.md new file mode 100644 index 0000000..14a29ca --- /dev/null +++ b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.en.md @@ -0,0 +1,6 @@ +--- +nazev: Layouts and viewports +popis: The sheet of paper that looks into the model. +nazev_cviceni: Quiz: viewports +nadpis_kvizu: Set the viewport scale +--- diff --git a/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.md b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.md new file mode 100644 index 0000000..0f26aa9 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/05-rozvrzeni-a-vyrezy.md @@ -0,0 +1,80 @@ +--- +id: tg-3-rozvrzeni +nazev: Rozvržení a výřezy +popis: Papír, na kterém leží pohled do modelu. +nazev_cviceni: Kvíz: výřezy +nadpis_kvizu: Nastavte měřítko výřezu +--- + +# Výklad + +## Model a papír +*Dva prostory* + +- V modelu je součást v milimetrech 1:1. Papír je rozvržení, záložka nad příkazovým řádkem. +- Na papír přijde rámeček, razítko a výřez. Výřez je okno do modelu. +- Dvojklik dovnitř výřezu přepne do modelu. Dvojklik ven vrátí papír. +- Kóty součásti nechte v modelu. Razítko kreslete v rozvržení, ať má na papíře stálou velikost. + +> Tip: rámeček nakreslený v modelu se při změně měřítka výřezu zmenší spolu se součástí. Proto patří na papír. + +## Měřítko výřezu +*1:2 na papíře* + +- Výřez má vlastní měřítko. Součást v modelu zůstává 1:1. +- Měřítko 1:2 znamená, že 1 mm na papíře je 2 mm na součásti. +- Ve stavovém řádku výřezu se měřítko vybírá ze seznamu. Starší zápis je `_ZOOM` a `1/2xp`. +- Výřez zamkněte, ať kolečko myši měřítko omylem nerozhodí. + +> Tip: nezamčený výřez je nejčastější důvod, proč ráno výkres není 1:1, i když včera byl. + +## Co je vidět +*Hladiny ve výřezu* + +- Hladinu lze ve výřezu zmrazit a v modelu ji nechat. Pomocné čáry tak na papír nejdou. +- Rámeček výřezu dejte na hladinu VYREZ a tu před tiskem vypněte. Na papíře zůstane jen obsah. +- Jeden výkres smí mít víc výřezů: čelo, řez, detail v jiném měřítku. +- Detail 2:1 je druhý výřez. V razítku se pak píše měřítko hlavní, detail má vlastní popisku. + +> Tip: detail zvětšený 2:1 nekreslete znovu. Je to stejný model v druhém výřezu. + +## Zadání: A3 a měřítko 1:2 +*Cvičení* + +- Rozvržení nastavte na A3 na šířku. Rámeček a razítko nakreslete v rozvržení. +- Výřez ukazuje přírubu Ø100 v měřítku 1:2. Na papíře má mít průměr 50 mm. +- Výřez zamkněte. Hladinu jeho rámečku vypněte. +- `_DIST` na papíře přes průměr příruby má ukázat 50, ne 100. + +> Tip: měření v rozvržení, mimo výřez, čte milimetry papíru. 100 mm součásti v měřítku 1:2 je na papíře 50 mm. + + +# Kvíz + +## Kde se kreslí tvar součásti v měřítku 1:1? +- V rozvržení +- [x] V modelu +- V razítku +- V hladině VYREZ +> Model zůstává skutečný. Měřítko pro papír nastavuje až výřez. + +## Součást Ø100 ve výřezu 1:2 má na papíře průměr? +- 100 mm +- [x] 50 mm +- 200 mm +- 1 mm +> 1:2 zmenšuje na polovinu. Kóta ve modelu pořád říká 100. + +## Proč výřez zamknout? +- Aby nešel tisknout +- [x] Aby kolečko myši nezměnilo měřítko +- Zamknutí smaže model +- Aby šel rámeček smazat +> Nezamčený výřez se při zoomu rozhodí a výkres už není v měřítku z razítka. + +## Kam patří rámeček a razítko? +- Do modelu, na hladinu OBRYS +- [x] Do rozvržení +- Do definice bloku šroubu +- Mimo papír +> V rozvržení mají stálou velikost bez ohledu na měřítko výřezu. diff --git a/content/technicka-grafika/3-rocnik/06-tisk.en.md b/content/technicka-grafika/3-rocnik/06-tisk.en.md new file mode 100644 index 0000000..2111b75 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/06-tisk.en.md @@ -0,0 +1,6 @@ +--- +nazev: Plotting and PDF +popis: The sheet, the plot area and a black pen. +nazev_cviceni: Quiz: plotting +nadpis_kvizu: Choose the plotter settings +--- diff --git a/content/technicka-grafika/3-rocnik/06-tisk.md b/content/technicka-grafika/3-rocnik/06-tisk.md new file mode 100644 index 0000000..9294a6f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/06-tisk.md @@ -0,0 +1,80 @@ +--- +id: tg-3-tisk +nazev: Tisk a PDF +popis: Papír, oblast tisku a černobílé pero. +nazev_cviceni: Kvíz: tisk +nadpis_kvizu: Vyberte nastavení plotru +--- + +# Výklad + +## Plotr +*Na papír nebo do PDF* + +- `_PLOT` tiskne aktuální rozvržení. Oblast tisku nechte Layout, ne Extents. +- Tiskárna DWG To PDF uloží výkres, který jde odevzdat bez posunu měřítka. +- Formát papíru musí sedět s rozvržením. A3 v tisku a A4 v rozvržení ořízne razítko. +- Měřítko tisku z rozvržení je 1:1. Měřítko součásti už drží výřez. + +> Tip: tisk 1:2 z rozvržení, které už má výřez 1:2, zmenší výkres podruhé. Na papíře bude 1:4. + +## Pero +*Černá čára* + +- Tabulka per monochrome.ctb tiskne vše černě a tloušťku bere z hladin. +- Barevný tisk ve škole nechte jen na náhled. Odevzdává se černobílý výkres. +- Tloušťka obrysu 0,5 mm a tenká čára 0,25 mm musí být v tabulce nebo na hladině vidět. +- Náhled tisku ukáže, jestli razítko neleze z papíru. Bez náhledu netiskněte. + +> Tip: když je na PDF všechno vlásečnicové, tloušťky hladin se netisknou. V `_PLOT` je zapněte. + +## Co do PDF nepatří +*Čistý výkres* + +- Rámeček výřezu je vypnutý. Pomocné konstrukce taky. +- Kóty jsou čitelné a nejsou přes šrafu, pokud to šlo jinak. +- PDF pojmenujte jako výkres, třeba priruba-A3.pdf, ne Plot1.pdf. +- Vytiskněte i kontrolní kópii na papír, když učitel odevzdání na papíře chce. + +> Tip: otevřete PDF a změřte rámeček A3. Má mít 420 × 297 mm. Jinak je špatně formát nebo měřítko tisku. + +## Zadání: PDF příruby +*Cvičení* + +- Rozvržení A3, výřez 1:1 nebo 1:2 podle toho, co se vejde i s kótami. +- `_PLOT`, tiskárna DWG To PDF, oblast Layout, měřítko 1:1, pero monochrome.ctb. +- Náhled zkontroluje, že razítko je celé a šrafa není černá plocha. +- Soubor uložte jako priruba-A3.pdf vedle dwg. + +> Tip: hustá šrafa v náhledu znamená velké měřítko vzoru nebo malé. Vraťte se do `_HATCH` a rozteč zvětšete. + + +# Kvíz + +## Jaké měřítko tisku se dává při tisku rozvržení? +- Stejné jako měřítko výřezu, znovu +- [x] 1:1 +- Fit to paper +- 2:1, ať je výkres větší +> Měřítko součásti už má výřez. Tisk papíru je 1:1, jinak se měřítko zdvojí. + +## K čemu je monochrome.ctb? +- Zvětší písmo +- [x] Tiskne černě a dodrží tloušťky +- Zamkne výřez +- Smaže hladinu OSA +> Barevné čáry na obrazovce jsou pro práci. Odevzdává se černý výkres s tlustým obrysem. + +## Proč nenechat oblast tisku na Extents? +- Extents je rychlejší +- [x] Vzalo by i objekty mimo papír a rozhodilo by měřítko +- Extents netiskne kóty +- Extents vyžaduje 3D +> Layout tiskne papír tak, jak jste ho složili. Extents hledá vše nakreslené a měřítko si dopočítá samo. + +## Co má mít rámeček A3 v hotovém PDF? +- 210 × 297 mm +- [x] 420 × 297 mm +- 100 × 70 mm +- Libovolný rozměr, rozhoduje obrazovka +> A3 je 420 × 297 mm. Když PDF vyjde menší, je špatně formát papíru nebo měřítko tisku. diff --git a/content/technicka-grafika/3-rocnik/07-izometrie.en.md b/content/technicka-grafika/3-rocnik/07-izometrie.en.md new file mode 100644 index 0000000..609e526 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/07-izometrie.en.md @@ -0,0 +1,6 @@ +--- +nazev: Isometric drawing +popis: A pictorial view that does not shrink edges the way perspective does. +nazev_cviceni: Quiz: isometrics +nadpis_kvizu: Pick the axis and the ellipse +--- diff --git a/content/technicka-grafika/3-rocnik/07-izometrie.md b/content/technicka-grafika/3-rocnik/07-izometrie.md new file mode 100644 index 0000000..3dda985 --- /dev/null +++ b/content/technicka-grafika/3-rocnik/07-izometrie.md @@ -0,0 +1,80 @@ +--- +id: tg-3-izometrie +nazev: Izometrie +popis: Názorný obraz, ve kterém se rozměry nezkreslují stejně jako v perspektivě. +nazev_cviceni: Kvíz: izometrie +nadpis_kvizu: Vyberte osu a elipsu +--- + +# Výklad + +## Tři osy +*120 stupňů* + +- Izometrie má tři osy po 120 stupních. Svislá zůstává svislá. +- Délky se nanášejí ve skutečné velikosti. Není to perspektiva, hrany se do dálky nezkracují. +- `_ISODRAFT` přepne kreslicí rovinu. F5 cyklí mezi levou, pravou a horní rovinou. +- Orto v izometrii drží čáry podél aktuálních os, ne podél obyčejného X a Y. + +> Tip: než nakreslíte hranu, podívejte se, která izorovina svítí. Čára v špatné rovině neleží na stěně. + +## Kružnice je elipsa +*Isokružnice* + +- Kružnice v izometrii se kreslí jako elipsa. `_ELLIPSE` a volba Isocircle. +- Nejdřív F5 na rovinu, ve které kružnice leží. Horní díra chce horní rovinu. +- Průměr se zadává skutečný. Elipsa ho zobrazí zkoseně, kóta zůstává Ø. +- Obyčejná kružnice `_CIRCLE` v izometrii je chyba. Na papíře by byla pořád kulatá a neladila by s osami. + +> Tip: díra na horní stěně a díra na přední stěně jsou dvě různě natočené elipsy. F5 mezi nimi přepněte. + +## K čemu izometrie je +*Názornost* + +- Izometrie nenahrazuje nárys a řez. Výrobu řídí okótované pohledy. +- Na školní práci bývá izometrie v rohu, bez kót nebo jen s hlavními. +- Skryté hrany se v izometrii často vynechají, ať obraz zůstane čitelný. +- Tlustý obrys nechte na viditelných hranách. Osy děr smíte naznačit tenčeji. + +> Tip: když se izometrie neshoduje s nárysem, platí nárys. Izometrii překreslete. + +## Zadání: kvádr s dírou +*Cvičení* + +- V izometrii nakreslete kvádr 60 × 40 × 30. +- Na horní stěně je díra Ø16 uprostřed. Použijte isokružnici v horní rovině. +- Soustředně naznačte tenčí elipsu, jen pokud by šlo o zahloubení. Tady zahloubení není. +- Porovnejte rozměry s nárysem téhož kvádru. 60, 40 a 30 musí sedět na obou. + +> Tip: střed horní stěny najdete v polovině šířky a hloubky. Elipsa se středem v rohu je špatně. + + +# Kvíz + +## Čím se v izometrii kreslí kružnice? +- `_CIRCLE` +- [x] `_ELLIPSE` s volbou Isocircle +- `_ARC` o 360 stupňů +- `_POLYGON` +> Obyčejná kružnice zůstane kulatá a s izometrickými osami neladí. + +## Co přepíná klávesa F5 při zapnutém `_ISODRAFT`? +- Hladiny +- [x] Izorovina: levá, pravá, horní +- Měřítko tisku +- Orto +> Každá stěna má vlastní natočení elipsy. F5 vybere stěnu, na kterou kreslíte. + +## Zkracují se v izometrii hrany směrem dozadu? +- Ano, jako v perspektivě +- [x] Ne, délky se nanášejí skutečné +- Jen svislé +- Jen při měřítku 1:2 +> Izometrie není perspektiva. Hrana dlouhá 60 mm se nanese jako 60 mm. + +## Co platí, když izometrie nesedí s nárysem? +- Platí izometrie, je názornější +- [x] Platí okótovaný nárys +- Oba se nechají a učitel si vybere +- Smaže se razítko +> Výrobu určují pohledy a kóty. Izometrie je doplněk a musí se jim přizpůsobit. diff --git a/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.en.md b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.en.md new file mode 100644 index 0000000..64e0b7f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.en.md @@ -0,0 +1,6 @@ +--- +nazev: Assignment: a sleeve +popis: A sectioned front view, a second view, dimensions and an A3 plot. +nazev_cviceni: Quiz: the sleeve +nadpis_kvizu: Check the sleeve drawing +--- diff --git a/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.md b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.md new file mode 100644 index 0000000..f6d3a4f --- /dev/null +++ b/content/technicka-grafika/3-rocnik/08-zadani-pouzdro.md @@ -0,0 +1,80 @@ +--- +id: tg-3-zadani-pouzdro +nazev: Zadání: pouzdro +popis: Nárys v řezu, půdorys, kóty a tisk na A3. +nazev_cviceni: Kvíz: pouzdro +nadpis_kvizu: Zkontrolujte výkres pouzdra +--- + +# Výklad + +## Tvar +*Rozměry* + +- Pouzdro je dutý válec. Vnější Ø60, díra Ø36, délka 80. +- Na pravém konci je osazení: vnější Ø48 v délce 20. Díra Ø36 jde skrz. +- Na Ø60 jsou čtyři díry M6 do hloubky 12, na roztečné kružnici Ø48. Nejsou průchozí. +- Závit M6 se v nárysu kreslí zjednodušeně: velký průměr tenkou čarou, malý tlustou, v délce 12 mm. + +> Tip: rozteč děr M6 je Ø48, tedy 24 mm od osy. Hloubka 12 není délka pouzdra. + +## Pohledy +*Řez a bok* + +- Nárys je úplný řez osou. Šrafuje se stěna, díra Ø36 zůstane prázdná. +- Půdorys nebo bokorys ukáže čtyři díry M6 na Ø48. Jednu okótujte 4× M6 a hloubku 12. +- Evropské promítání: druhý pohled pod nárysem nebo vpravo, zarovnaný. +- Izometrii přidejte vpravo dole, bez kót, ať je tvar jasný. + +> Tip: v řezu osou jsou vidět dvě protilehlé díry M6, pokud rovina A-A jimi prochází. Hloubka 12 se kótuje tady. + +## Výkres +*A3* + +- Model 1:1. Rozvržení A3, měřítko výřezu 1:1, tisk 1:1, monochrome.ctb. +- Hladiny OBRYS, OSA, SKRYTE, SRAFA, KOTA, ZAVIT, RAM. +- Razítko: Pouzdro, ocel, 1:1. Kóty Ø60, Ø36, Ø48, délka 80, osazení 20 a Ø48, 4× M6 hloubka 12. +- Závitová hladina je tenká. Není to obrys válce. + +> Tip: malý průměr závitu M6 je zhruba 5 mm. Tenká čára velkého průměru je 6 mm. Neměňte je za Ø36. + +## Kontrola +*Odevzdání* + +- Stěna v řezu má tloušťku (60 − 36) / 2 = 12 mm. +- Osazení dlouhé 20 nešrafujte jinak než zbytek stěny. Je to pořád jeden kus. +- PDF priruba nahraďte souborem pouzdro-A3.pdf. Razítko je celé na papíře. +- Žádná kóta není přepsaná. `_DIST` přes vnější průměr dá 60. + +> Tip: dva kusy by měly opačný sklon šrafy. Tady je kus jeden, šrafa má jeden směr. + + +# Kvíz + +## Jaká je tloušťka stěny pouzdra Ø60 s dírou Ø36? +- 24 mm +- [x] 12 mm +- 36 mm +- 6 mm +> Rozdíl průměrů je 24 mm materiálu na obě strany. Na jednu stěnu připadá 12 mm. + +## Jak se zjednodušeně kreslí závit M6? +- Obě čáry tlusté +- [x] Velký průměr tenkou čarou, malý tlustou +- Jen osou +- Šrafou ANSI31 +> Tenká čára je velký průměr 6 mm. Tlustá je malý průměr, u M6 zhruba 5 mm. + +## Kam patří čtyři díry M6? +- Průchozí skrz Ø36 +- [x] Do hloubky 12 mm na roztečné kružnici Ø48 +- Na osazení Ø48 jako drážka +- Do razítka +> Nejsou to průchozí díry hlavní dutiny. Hloubka 12 a rozteč Ø48 jsou v zadání. + +## Proč má šrafa pouzdra jeden směr? +- Dva směry jsou zakázané vždy +- [x] Je to jeden kus +- Řez je poloviční +- Měřítko je 1:2 +> Opačný sklon by říkal, že se dotýkají dva díly. Osazení je pořád totéž pouzdro. diff --git a/content/technicka-grafika/4-rocnik/01-prostor-3d.en.md b/content/technicka-grafika/4-rocnik/01-prostor-3d.en.md new file mode 100644 index 0000000..b7db361 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/01-prostor-3d.en.md @@ -0,0 +1,6 @@ +--- +nazev: Model space in 3D +popis: Axes, the user coordinate system and orbiting the model. +nazev_cviceni: Quiz: space +nadpis_kvizu: Find the axis and the AutoCAD edition +--- diff --git a/content/technicka-grafika/4-rocnik/01-prostor-3d.md b/content/technicka-grafika/4-rocnik/01-prostor-3d.md new file mode 100644 index 0000000..2c94303 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/01-prostor-3d.md @@ -0,0 +1,80 @@ +--- +id: tg-4-prostor +nazev: Modelový prostor ve 3D +popis: Osy, uživatelský souřadný systém a obíhání kolem modelu. +nazev_cviceni: Kvíz: prostor +nadpis_kvizu: Najděte osu a verzi AutoCADu +--- + +# Výklad + +## Tři osy +*X, Y, Z* + +- K ose X a Y přibude Z. Na obrazovce při startu míří Z k vám. +- Pravidlo pravé ruky: prsty od X k Y, palec ukazuje Z. +- `_BOX` a další tělesa potřebují plný AutoCAD. Verze LT tělesa nevytvoří. +- 2D výkres z dřívějška zůstává. 3D model je druhý soubor, nebo stejný model v jiném pohledu. + +> Tip: když příkaz `_BOX` AutoCAD nezná, máte LT. Tělesa v ní nejdou. + +## Pohled +*Orbit* + +- ViewCube v rohu nebo `_ORBIT` otáčí pohled. Model se neotáčí, otáčí se kamera. +- Přední, horní a levý pohled odpovídají nárysu, půdorysu a bokorysu. +- `_UCS` posune a natočí souřadnice. Nová tělesa vznikají vůči aktuálnímu UCS. +- `_UCS` a volba World vrátí původní osy. Ztracený UCS je nejčastější zmatek ve 3D. + +> Tip: než vysunete profil, zkontrolujte ikonu os. Z má trčet z plochy, kterou vysouváte. + +## Drát a stínování +*Ať něco vidíte* + +- Drátový model ukáže všechny hrany, i zadní. Na začátku se v něm líp trefuje. +- Stínovaný pohled schová zadní hrany. Přepíná se `_VSCURRENT` nebo nabídkou pohledu. +- Zoom kolečkem pořád platí. Výřez na papír přijde až v lekci o výkresu z modelu. +- Jednotky zůstávají milimetry. Těleso 60 × 40 × 30 je v tomtéž souboru jako 2D profil. + +> Tip: ztracený model najdete `_ZOOM` Extents. Často leží daleko od počátku, protože UCS byl posunutý. + +## Zadání: tři osy a kvádr +*Cvičení* + +- V novém souboru v milimetrech nakreslete `_BOX` se základnou 60 × 40 a výškou 30, roh v 0,0,0. +- Přepněte na přední, horní a levý pohled. V každém má být vidět jiná dvojice rozměrů. +- `_UCS` World a `_DIST` mezi dvěma rohy podél X má dát 60. +- Uložte jako kvadr-60x40x30.dwg. LT na tento soubor nestačí. + +> Tip: přední pohled ukáže 60 × 30, horní 60 × 40, levý 40 × 30. + + +# Kvíz + +## Který AutoCAD umí tělesa `_BOX`? +- Každá verze včetně LT +- [x] Plný AutoCAD, ne LT +- Jen verze na Mac bez příkazů +- Žádný, tělesa jsou v jiném programu +> LT je 2D. Příkaz `_BOX` v ní není. + +## Co dělá `_ORBIT`? +- Otočí těleso v souřadnicích +- [x] Otočí pohled kamery +- Nastaví měřítko tisku +- Smaže osu Z +> Model zůstane. Změní se, odkud se na něj díváte. + +## Co vrátí souřadnice do původního směru? +- `_ZOOM` Extents +- [x] `_UCS` a volba World +- Vypnutí hladiny 0 +- `_PLOT` +> Posunutý UCS nechá nová tělesa „ležet na boku“. World je výchozí osy výkresu. + +## Co ukáže přední pohled na kvádr 60 × 40 × 30, když 30 je výška? +- 60 × 40 +- [x] 60 × 30 +- 40 × 30 +- Jen osu Z +> Přední pohled je šířka a výška. Hloubka 40 je v půdorysu a v bokorysu. diff --git a/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.en.md b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.en.md new file mode 100644 index 0000000..8684db3 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.en.md @@ -0,0 +1,6 @@ +--- +nazev: Solids and extrude +popis: A box, a cylinder and a profile pulled up by a height. +nazev_cviceni: Quiz: extrude +nadpis_kvizu: Pick the profile and the height +--- diff --git a/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.md b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.md new file mode 100644 index 0000000..d115dcf --- /dev/null +++ b/content/technicka-grafika/4-rocnik/02-telesa-a-vysunuti.md @@ -0,0 +1,80 @@ +--- +id: tg-4-vysunuti +nazev: Tělesa a vysunutí +popis: Kvádr, válec a profil tažený o výšku. +nazev_cviceni: Kvíz: vysunutí +nadpis_kvizu: Vyberte profil a výšku +--- + +# Výklad + +## Hotová tělesa +*Kvádr a válec* + +- `_BOX` chce roh nebo střed základny, půdorys a výšku. +- `_CYLINDER` chce střed, poloměr nebo průměr a výšku. +- Výška se měří ve směru osy Z aktuálního UCS. +- Tato tělesa jsou plná. Nejsou to stěny z ploch. + +> Tip: válec Ø40 a výška 25 má v příkazu buď poloměr 20, nebo volbu průměru 40. + +## Vysunutí profilu +*Z 2D do výšky* + +- `_EXTRUDE` vezme uzavřenou křivku nebo oblast a táhne ji o výšku. +- Profil musí být zavřený. Čtyři nenapojené úsečky vysunout nejdou. +- Kladná výška jde ve směru Z. Záporná na opačnou stranu. +- `_PRESSPULL` umí přitáhnout uzavřenou oblast, na kterou kliknete, bez předchozího výběru. + +> Tip: obdélník 80 × 50 vysunutý o 12 je destička z druháku, teď jako těleso. + +## Profil nejdřív ve 2D +*Plocha* + +- Profil nakreslete v rovině, ze které chcete táhnout. Nejčastěji půdorys, Z ven z obrazovky. +- `_REGION` z uzavřené křivky udělá jednu plochu. Vysouvá se spolehlivěji než volné čáry. +- Po vysunutí profil smíte smazat, nebo ho nechte na hladině PROFIL a tu zmrazte. +- Jeden profil, jedna výška. Osazení vznikne až druhým tělesem a booleanem v další lekci. + +> Tip: otevřená křivka se vysune na plochu, ne na těleso. Pro součást potřebujete těleso. + +## Zadání: destička jako těleso +*Cvičení* + +- Profil 120 × 70, `_EXTRUDE` o 10 mm. To je tloušťka destičky. +- Díru zatím nekreslete. Ověřte `_MASSPROP`: objem má být 120 × 70 × 10 = 84 000 mm³. +- Přední pohled ukáže 120 × 10. Horní pohled 120 × 70. +- Uložte jako desticka-3d.dwg. + +> Tip: když objem vyjde dvojnásobek, výška je 20 místo 10. Kóta textu to nespraví, `_MASSPROP` čte model. + + +# Kvíz + +## Jaký poloměr má `_CYLINDER` o průměru 40? +- 40 +- [x] 20 +- 80 +- 10 +> Příkaz bez přepnutí bere poloměr. Průměr je volba, nebo napište polovinu. + +## Co `_EXTRUDE` potřebuje, aby vzniklo těleso? +- Otevřenou úsečku +- [x] Uzavřený profil +- Razítko +- Už hotový válec +> Neuzavřený profil vytvoří jen plochu. Těleso má mít objem. + +## Kam míří kladná výška vysunutí? +- Vždy doprava na obrazovce +- [x] Ve směru osy Z aktuálního UCS +- Do záporného X +- Do razítka +> Špatně natočený UCS vysune destičku „na bok“. Před příkazem zkontrolujte osy. + +## Objem destičky 120 × 70 × 10 mm je? +- 840 mm³ +- [x] 84 000 mm³ +- 200 mm³ +- 12 000 mm³ +> 120 × 70 = 8 400, krát 10 je 84 000. `_MASSPROP` má ukázat totéž. diff --git a/content/technicka-grafika/4-rocnik/03-rotace.en.md b/content/technicka-grafika/4-rocnik/03-rotace.en.md new file mode 100644 index 0000000..4f4c6b4 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/03-rotace.en.md @@ -0,0 +1,6 @@ +--- +nazev: Revolving a profile +popis: A part symmetric about an axis is the section spun around. +nazev_cviceni: Quiz: revolve +nadpis_kvizu: Pick the profile and the axis +--- diff --git a/content/technicka-grafika/4-rocnik/03-rotace.md b/content/technicka-grafika/4-rocnik/03-rotace.md new file mode 100644 index 0000000..9a77666 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/03-rotace.md @@ -0,0 +1,80 @@ +--- +id: tg-4-rotace +nazev: Rotace profilu +popis: Součást souměrná podle osy vznikne otočením řezu. +nazev_cviceni: Kvíz: rotace +nadpis_kvizu: Vyberte profil a osu +--- + +# Výklad + +## Kdy rotovat +*Místo skládání válců* + +- `_REVOLVE` otočí uzavřený profil kolem osy o zadaný úhel. +- 360 stupňů udělá plné rotační těleso. 180 udělá polovinu. +- Hodí se na hřídel, pouzdro, přírubu a každou součást souměrnou podle osy. +- Profil je polovina řezu. Osa rotace je osa součásti a profil ji smí jen lemovat, ne křížit. + +> Tip: pouzdro z třetího ročníku je jeden profil stěny otočený o 360 stupňů, ne válec minus válec. Oba postupy jsou správně, rotace je u čistě rotačního dílu kratší. + +## Profil +*Polovina* + +- Nakreslete jen polovinu řezu, vpravo od osy. Uzavřete ho. +- U pouzdra Ø60 / Ø36 dlouhého 80 je profil obdélník 12 × 80. Dvanáct je tloušťka stěny. +- Osa je svislá hrana díry. Profil se osy dotýká jen vnější? Ne: vnitřní hrana profilu je o poloměr díry od osy. +- Vnitřní hrana leží 18 mm od osy, vnější 30 mm. Šířka profilu je 12 mm. + +> Tip: 36 / 2 = 18 a 60 / 2 = 30. Profil je pás mezi těmito poloměry. + +## Osa a úhel +*360* + +- Osu vyberte dva body, nebo jednu úsečku nakreslenou na hladině OSA. +- Úhel 360. Menší úhel nechá výseč a na školní hřídeli bývá omyl. +- Profil nesmí osu křížit. Křížení AutoCAD odmítne, nebo udělá těleso, které se protíná samo. +- Po rotaci profil zmrazte. V řezu ho zase použijete jako kontrolu. + +> Tip: když vznikne plný válec bez díry, profil začínal na ose. Vnitřní hrana musí být 18 mm od osy, ne 0. + +## Zadání: pouzdro rotací +*Cvičení* + +- Osa je úsečka z 0,0 do 0,80. +- Profil: obdélník s rohy 18,0 a 30,80. +- `_REVOLVE` o 360 stupňů kolem této osy. +- `_MASSPROP` objem mezikruží: π × (30² − 18²) × 80. Má vyjít zhruba 144 764 mm³. + +> Tip: 900 − 324 = 576, krát π je asi 1 810, krát 80 je asi 144 800. Řád tisíců, ne desítek. + + +# Kvíz + +## Jaký úhel udělá plné rotační těleso? +- 90 +- 180 +- [x] 360 +- 45 +> Polovina otáčky je výseč. Uzavřené pouzdro potřebuje celých 360 stupňů. + +## Kde leží vnitřní hrana profilu pouzdra Ø36? +- Na ose +- [x] 18 mm od osy +- 36 mm od osy +- 60 mm od osy +> Poloměr díry je polovina průměru. Profil díru ohraničuje, nesmí začínat na ose, jinak díra zmizí. + +## Co se stane, když profil osu kříží? +- Vznikne správné pouzdro +- [x] AutoCAD rotaci odmítne, nebo těleso protne samo sebe +- Profil se jen posune +- Vznikne šrafa +> Osa smí být hranou profilu, ne smí jím procházet skrz. + +## Proč rotovat pouzdro místo skládání dvou válců? +- Jinak to nejde +- [x] U rotačního dílu stačí jeden profil +- Rotace umí i hranaté osazení bez profilu +- `_REVOLVE` funguje i v LT +> Oba postupy jsou správně. Rotace je kratší, když je součást souměrná podle osy. LT ji neumí. diff --git a/content/technicka-grafika/4-rocnik/04-boolean.en.md b/content/technicka-grafika/4-rocnik/04-boolean.en.md new file mode 100644 index 0000000..e6fc00f --- /dev/null +++ b/content/technicka-grafika/4-rocnik/04-boolean.en.md @@ -0,0 +1,6 @@ +--- +nazev: Union, subtract and intersect +popis: Solids add and subtract like sets. +nazev_cviceni: Quiz: booleans +nadpis_kvizu: Pick the order of the solids +--- diff --git a/content/technicka-grafika/4-rocnik/04-boolean.md b/content/technicka-grafika/4-rocnik/04-boolean.md new file mode 100644 index 0000000..b833fe4 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/04-boolean.md @@ -0,0 +1,80 @@ +--- +id: tg-4-boolean +nazev: Sjednocení, rozdíl a průnik +popis: Tělesa se sčítají a odečítají jako množiny. +nazev_cviceni: Kvíz: boolean +nadpis_kvizu: Vyberte pořadí těles +--- + +# Výklad + +## Sjednocení +*Jeden kus* + +- `_UNION` slepí tělesa, která se dotýkají nebo pronikají, v jeden objekt. +- Osazení na hřídeli jsou dva válce. Po sjednocení je to jedna součást. +- Tělesa, která se nedotýkají, sjednocení také spojí do jedné skupiny, ale výrobek to není. +- Než sjednotíte, zkontrolujte přesah. Mezera 0,01 mm nechá dva kusy. + +> Tip: `_INTERFERE` ukáže, jestli se dvě tělesa opravdu pronikají, nebo se jen zdají dotýkat. + +## Rozdíl +*Díra* + +- `_SUBTRACT` nejdřív vyberete těleso, které má zůstat, potvrdíte, a teprve pak těleso, které se odečte. +- Obrácené pořadí smaže součást a nechá díru jako váleček. To je nejčastější chyba. +- Díra Ø10 je válec Ø10 delší, než je tloušťka stěny, ať rozdíl prořízne celou stěnu. +- Válec díry po rozdílu zmizí. Nemusíte ho mazat ručně. + +> Tip: válec díry nechte o milimetr přečuhovat z obou stran. Jinak na stěně zůstane blanka. + +## Průnik +*Společný objem* + +- `_INTERSECT` nechá jen objem, který mají tělesa společný. Zbytek zmizí. +- Hodí se, když potřebujete tvar daný dvěma podmínkami, třeba válec seříznutý kvádrem. +- Na běžnou díru průnik není. Díra je rozdíl. +- Po průniku zůstanou obě původní tělesa pryč. Když je budete potřebovat, předem je zkopírujte. + +> Tip: sjednocení přidá, rozdíl ubere, průnik nechá jen překryv. U díry je to vždy rozdíl. + +## Zadání: destička s dírou +*Cvičení* + +- Destička `_BOX` 120 × 70 × 10. +- Válec Ø16, střed v 60,35, výška od -1 do 11, ať prochází skrz. +- `_SUBTRACT`: nejdřív destička, potom válec. +- `_MASSPROP` objem: 84 000 minus objem válce v tloušťce 10. Válec Ø16 a výška 10 má objem π × 8² × 10 ≈ 2 011. Výsledek je asi 81 989 mm³. + +> Tip: když zmizela destička a zbyl váleček, pořadí rozdílu bylo obrácené. Vraťte krok zpět. + + +# Kvíz + +## Jakým pořadím vznikne díra? +- Nejdřív válec, potom destička +- [x] Nejdřív destička, potom válec +- `_UNION` +- `_INTERSECT` +> První výběr zůstává. Druhý se odečte. Obrácené pořadí nechá jen váleček. + +## Proč má válec díry přečnívat z destičky? +- Aby šel okótovat +- [x] Aby ve stěně nezůstala blanka +- Přečnívat nesmí, rozdíl by selhal +- Aby vznikl závit +> Válec zarovnaný přesně s plochou někdy nechá tenkou slupku. Milimetr přesahu ji odstraní. + +## Který příkaz slepí dva dotýkající se válce hřídele? +- `_SUBTRACT` +- [x] `_UNION` +- `_INTERSECT` +- `_REVOLVE` +> Osazení je jeden kus. Sjednocení z dvou válců udělá jedno těleso. + +## Co nechá `_INTERSECT`? +- Součet objemů +- [x] Jen společný překryv +- Díru +- Původní tělesa vedle výsledku +> Zbytek obou těles zmizí. Před průnikem si je zkopírujte, pokud je ještě budete potřebovat. diff --git a/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.en.md b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.en.md new file mode 100644 index 0000000..cd46853 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.en.md @@ -0,0 +1,6 @@ +--- +nazev: Sectioning a solid and Flatshot +popis: From the model back to lines for the drawing. +nazev_cviceni: Quiz: the model section +nadpis_kvizu: Decide what lands on the sheet +--- diff --git a/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.md b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.md new file mode 100644 index 0000000..c8f2f53 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/05-rez-a-flatshot.md @@ -0,0 +1,80 @@ +--- +id: tg-4-rez-modelu +nazev: Řez tělesem a plochý snímek +popis: Z modelu zpátky čáry pro výkres. +nazev_cviceni: Kvíz: řez modelu +nadpis_kvizu: Vyberte, co vznikne na papíře +--- + +# Výklad + +## Řezná rovina +*Uvnitř tělesa* + +- `_SECTIONPLANE` položí rovinu řezu. Živé zobrazení ukáže řez, aniž by těleso rozřízlo natrvalo. +- `_SLICE` těleso rozřízne doopravdy. Polovinu můžete nechat, nebo smazat. +- Na školní výkres řežte živě nebo si před `_SLICE` těleso zkopírujte. Původní model budete ještě potřebovat. +- Řez osou pouzdra má ukázat stěnu 12 mm a díru Ø36, stejně jako ruční řez ve třetím ročníku. + +> Tip: rovina, která mine osu, ukáže jinou tloušťku stěny. U pouzdra veďte řez osou. + +## Plochý snímek +*Čáry z pohledu* + +- `_FLATSHOT` promítne aktuální pohled do čar v rovině XY. +- Viditelné hrany dá na jednu hladinu, skryté na druhou. Skryté pak přepněte na čárkované. +- Než příkaz spustíte, nastavte pohled: přední pro nárys, horní pro půdorys. +- Snímek je 2D. Dá se kótovat jako výkres z druháku. Model vedle něj nechte nedotčený. + +> Tip: plochý snímek pořízený v šikmém pohledu není nárys. Nejdřív srovnejte pohled, potom foťte. + +## Co snímek neudělá +*Kóty a šrafa* + +- Snímek neumí šrafu řezu ani kóty. Ty doplníte vy, na hladiny SRAFA a KOTA. +- Neumí ani razítko. To patří do rozvržení. +- Čáry snímku bývají na nových hladinách. Přesuňte je na OBRYS a SKRYTE, ať platí školní tloušťky. +- Když model potom změníte, snímek se sám neopraví. Po úpravě modelu ho vytvořte znovu. + +> Tip: výkresové pohledy z pozdější lekce se na rozdíl od snímku umí s modelem aktualizovat. Snímek je rychlá kontrola. + +## Zadání: nárys z tělesa +*Cvičení* + +- Otevřete pouzdro z rotace. Přední pohled, `_FLATSHOT`, viditelné hrany na OBRYS. +- Do řezu osou doplňte šrafu stěny ručně, nebo použijte živý řez a z něj čáry. +- Okótujte Ø60, Ø36 a délku 80. Čísla musí sedět s `_MASSPROP` a s poloměry profilu. +- Snímek nesmí zůstat šikmo. Nárys je v rovině, díra je obdélník 36 × 80 uvnitř obdélníku 60 × 80. + +> Tip: v nárysu řezu jsou dvě vodorovné čáry díry. Jejich vzdálenost je 36. + + +# Kvíz + +## Čím rozříznete těleso natrvalo? +- `_FLATSHOT` +- [x] `_SLICE` +- `_ORBIT` +- `_PLOT` +> Živá řezná rovina model nechá celý. `_SLICE` ho rozdělí. Předtím si nechte kopii. + +## Co musí předcházet plochému snímku nárysu? +- Tisk +- [x] Srovnaný přední pohled +- Rozbití tělesa +- Šrafa +> Snímek promítne to, co právě vidíte. Šikmý orbit není nárys. + +## Doplní `_FLATSHOT` kóty? +- Ano, z hladiny KOTA +- [x] Ne +- Jen průměry +- Jen když je měřítko 1:1 +> Snímek jsou čáry. Kóty, šrafu a razítko dodáte vy. + +## Jak daleko jsou v nárysu řezu pouzdra od sebe čáry díry Ø36? +- 18 +- 60 +- [x] 36 +- 12 +> V řezu je díra obdélník. Jeho výška je průměr, ne poloměr. diff --git a/content/technicka-grafika/4-rocnik/06-zobrazeni.en.md b/content/technicka-grafika/4-rocnik/06-zobrazeni.en.md new file mode 100644 index 0000000..3a7cfc8 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/06-zobrazeni.en.md @@ -0,0 +1,6 @@ +--- +nazev: Visual styles +popis: Wireframe, hidden lines and shading for checking the shape. +nazev_cviceni: Quiz: display +nadpis_kvizu: Pick a style for the task +--- diff --git a/content/technicka-grafika/4-rocnik/06-zobrazeni.md b/content/technicka-grafika/4-rocnik/06-zobrazeni.md new file mode 100644 index 0000000..687c0a4 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/06-zobrazeni.md @@ -0,0 +1,80 @@ +--- +id: tg-4-zobrazeni +nazev: Vizuální styly +popis: Drát, skryté hrany a stínování pro kontrolu tvaru. +nazev_cviceni: Kvíz: zobrazení +nadpis_kvizu: Vyberte styl podle úkolu +--- + +# Výklad + +## Drát a skryté hrany +*Kontrola* + +- Drátový model ukáže každou hranu. Hodí se, když saháte na zadní vrchol. +- Styl skrytých hran schová, co je za materiálem. Tvar se čte jako na fotografii bez stínu. +- `_VSCURRENT` styly přepíná. Na pásu karet je stejná nabídka u pohledu. +- Styl nemění těleso. Špatně odečtená díra zůstane špatně v každém stylu. + +> Tip: nejdřív drát, ať trefíte hranu, potom skryté hrany, ať zkontrolujete, že díra opravdu prochází. + +## Stínování +*Objem* + +- Konceptuální nebo stínovaný styl ukáže, jestli je díra průchozí, nebo je to jen namalovaná kružnice. +- Barva tělesa se bere z hladiny. OBRYS a těleso smí mít vlastní hladinu MODEL. +- Materiál a render na škole stačí náznak. Odevzdává se výkres, ne obrázek. +- Stínování zpomalí otáčení. Na slabším počítači se vraťte do drátu, než budete modelovat dál. + +> Tip: když v řezu vidíte dno díry, válec byl krátký. Rozdíl stěnu neprošel. + +## Řezy bez rozbití +*Pohled* + +- Živý řez v kombinaci se stínováním je nejrychlejší kontrola tloušťky stěny. +- Pohled uložte `_VIEW`, ať se k němu vrátíte. Název třeba REZ-AA. +- Izometrický stínovaný pohled smí přijít na výkres jako názorný doplněk, ne jako jediný obraz. +- Barvy na obrazovce pořád tiskněte černě. Styl zobrazení není tabulka per. + +> Tip: uložený pohled nese i styl. Když REZ-AA otevřete a je z něj drát, někdo styl přepsal. + +## Zadání: tři kontroly destičky +*Cvičení* + +- Destičku s dírou Ø16 zobrazte jako drát, jako skryté hrany a stínovaně. +- Ve stínovaném horním pohledu musí být skrz díru vidět. Když není, opakujte rozdíl. +- Uložte pohled HORNI a pohled PREDNI. +- Do výkresu tyto pohledy zatím nevkládejte. Jde o kontrolu modelu. + +> Tip: díra, která ve stínu má dno stejné barvy jako destička, je slepá. Výška válce byla menší než 10 mm. + + +# Kvíz + +## Který styl nechá vidět i zadní hrany? +- Stínovaný +- [x] Drátový +- Skryté hrany +- Monochrome.ctb +> Drát nekryje nic. Skryté hrany a stín zadní geometrii schovají. + +## Změní vizuální styl objem tělesa? +- Ano, stín ubere materiál +- [x] Ne +- Jen u rozdílu +- Jen při tisku +> Styl je způsob kresby na obrazovce. `_MASSPROP` zůstane stejný. + +## Co máte vidět skrz průchozí díru ve stínovaném pohledu? +- Dno stejné barvy +- [x] Prostor za destičkou +- Šrafu ANSI31 +- Razítko +> Dno znamená, že válec díry byl krátký a rozdíl stěnu neprošel. + +## Čím se na papír dostane černá čára, když je model na obrazovce modrý? +- Vizuálním stylem +- [x] Tabulkou per při tisku +- Smazáním hladiny +- `_ORBIT` +> Barva na monitoru je pracovní. monochrome.ctb z ní na PDF udělá černou. diff --git a/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.en.md b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.en.md new file mode 100644 index 0000000..4ad88b7 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.en.md @@ -0,0 +1,6 @@ +--- +nazev: A drawing from the 3D model +popis: A front view and a section that update with the model. +nazev_cviceni: Quiz: model views +nadpis_kvizu: Tie the sheet to the solid +--- diff --git a/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.md b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.md new file mode 100644 index 0000000..00b0c55 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/07-vykres-z-modelu.md @@ -0,0 +1,80 @@ +--- +id: tg-4-vykres-z-modelu +nazev: Výkres z 3D modelu +popis: Nárys a řez, které se s modelem aktualizují. +nazev_cviceni: Kvíz: pohledy z modelu +nadpis_kvizu: Propojte výkres s tělesem +--- + +# Výklad + +## Základní pohled +*Z tělesa* + +- V rozvržení příkaz základního pohledu vezme těleso a položí na papír nárys, půdorys nebo izometrii. +- Měřítko pohledu nastavíte jako u výřezu, třeba 1:1 nebo 1:2. +- Odvozené pohledy se položí nad, pod nebo vedle. Drží evropské promítání, když ho tak nastavíte. +- Pohled je svázaný s modelem. Změna tělesa a aktualizace pohledu přepíše čáry. + +> Tip: plochý snímek se sám neopraví. Pohled z modelu ano. Na maturitní výkres použijte pohled. + +## Řez pohledu +*A-A* + +- Z základního pohledu uděláte řez tažením řezné čáry. Písmena A doplní AutoCAD, nebo je přepište na A. +- Šrafu řezu nastavte na ANSI31 a školní měřítko. Hřídel v podélném řezu pořád nešrafujte, i když to AutoCAD nabídne. +- Skryté čáry v pohledu vypněte, pokud řez vnitřek ukázal. Výkres zůstane čistý. +- Kóty klikejte na pohled, ne do modelu naslepo. Berou rozměr z tělesa. + +> Tip: kóta napojená na pohled se po aktualizaci přepočítá. Kóta nakreslená vedle jako obyčejná čára ne. + +## Papír +*A3* + +- Rámeček a razítko jsou v rozvržení, měřítko 1:1 vůči papíru. +- Tisk je Layout, 1:1, monochrome.ctb, jako ve třetím ročníku. +- Izometrický pohled bez kót smí být třetí obraz. Nesmí být jediný. +- Než odevzdáte, aktualizujte pohledy. Starý pohled lže, když jste model mezitím změnili. + +> Tip: ve stromu nebo přes aktualizaci pohledu uvidíte, jestli je obraz aktuální. Nevěřte čáře, která přežila smazanou díru. + +## Zadání: pouzdro na A3 +*Cvičení* + +- Z 3D pouzdra vytvořte nárys jako řez A-A a půdorys nebo bokorys s roztečí, pokud na modelu díry jsou. +- Měřítko 1:1, formát A3. Kóty Ø60, Ø36 a 80. +- Změňte v modelu délku na 90, aktualizujte pohled a přesvědčte se, že kóta délky ukáže 90. +- Vraťte délku na 80, ať odevzdáte zadání, ne zkoušku. PDF pojmenujte pouzdro-z-modelu.pdf. + +> Tip: když po změně modelu kóta zůstane 80, není napojená na pohled. Smažte ji a okótujte znovu. + + +# Kvíz + +## Čím se pohled z modelu liší od `_FLATSHOT`? +- Ničím +- [x] Po změně tělesa se dá aktualizovat +- Umí jen izometrii +- Funguje v LT +> Snímek je jednorázová kopie čar. Pohled zůstává svázaný s tělesem. + +## Kam patří rámeček výkresu z modelu? +- Dovnitř tělesa +- [x] Do rozvržení +- Na hladinu OBRYS modelu +- Do definice bloku +> Papír má vlastní měřítko. Rámeček v modelu by se zmenšil s pohledem. + +## Co uděláte po změně délky pouzdra v modelu? +- Nic, kóty se nemění nikdy +- [x] Aktualizujete pohled +- Smažete razítko +- Přepnete na LT +> Neaktualizovaný pohled ukazuje starý tvar. Kóta napojená na něj se přepočítá spolu s ním. + +## Jaké měřítko má tisk tohoto rozvržení? +- 1:2, protože model je velký +- [x] 1:1 +- Podle `_MASSPROP` +- Fit +> Měřítko obrazu drží pohled. Tisk papíru je 1:1, jinak se měřítko zdvojí. diff --git a/content/technicka-grafika/4-rocnik/08-maturitni-zadani.en.md b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.en.md new file mode 100644 index 0000000..5bfcb04 --- /dev/null +++ b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.en.md @@ -0,0 +1,6 @@ +--- +nazev: Exam assignment +popis: From a sketch through the model to a PDF that stands on its own. +nazev_cviceni: Quiz: the exam piece +nadpis_kvizu: Walk the whole drawing +--- diff --git a/content/technicka-grafika/4-rocnik/08-maturitni-zadani.md b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.md new file mode 100644 index 0000000..19d47ea --- /dev/null +++ b/content/technicka-grafika/4-rocnik/08-maturitni-zadani.md @@ -0,0 +1,80 @@ +--- +id: tg-4-maturitni-zadani +nazev: Maturitní zadání +popis: Od náčrtu přes model po PDF, které obstojí samo. +nazev_cviceni: Kvíz: maturita +nadpis_kvizu: Projdete celý výkres +--- + +# Výklad + +## Součást +*Přírubové pouzdro* + +- Dutý válec Ø70 / Ø40, délka 60. Na jednom konci příruba Ø110 tlustá 12. +- Čtyři díry Ø11 na roztečné kružnici Ø90 v přírubě. Jsou průchozí skrz přírubu, ne skrz celé pouzdro. +- Vnitřní hrana díry Ø40 má zkosení 1 × 45° na straně příruby. +- Materiál ocel. Jednotky milimetry. Model 1:1. + +> Tip: tloušťka stěny válce je (70 − 40) / 2 = 15 mm. Příruba přesahuje válec o (110 − 70) / 2 = 20 mm. + +## Model +*Pořadí* + +- Profil řezu rotujte, nebo složte válec Ø70, válec příruby Ø110 výšky 12 a odečtěte díru Ø40. +- Čtyři díry Ø11 vzniknou polárním polem válce na poloměru 45 a rozdílem od příruby. +- Válec díry Ø11 musí být delší než 12 mm, ať je díra průchozí. Do dutiny Ø40 díra Ø11 nezasahuje, leží v materiálu příruby. +- `_MASSPROP` spočítejte orientačně a porovnejte s ručním odhadem. Řád objemu je statisíce mm³, ne tisíce. + +> Tip: rozteč Ø90 je poloměr 45. Díra se středem na poloměru 55 by už ležela mimo přírubu Ø110. + +## Výkres +*A3, 1:1* + +- Nárys je řez A-A osou. Půdorys ukáže čtyři díry. Izometrie bez kót je doplněk. +- Kóty: Ø110, Ø70, Ø40, tloušťka příruby 12, délka 60, 4× Ø11 na Ø90, zkosení 1 × 45°. +- Hladiny OBRYS, OSA, SRAFA, KOTA, RAM. Šrafa jen v řezu, jeden směr, díry prázdné. +- Razítko: Přírubové pouzdro, ocel, 1:1, jméno. Tisk A3, Layout, 1:1, monochrome.ctb. + +> Tip: evropské promítání. Půdorys pod nárysem. Čtyři díry se kótují jednou. + +## Než odevzdáte +*Kontrola* + +- Každá kóta se dá z modelu ověřit. Žádný text není přepsaný. +- Vypnutá hladina KOTA nechá čistý tvar. Vypnutá SRAFA nechá obrys řezu. +- PDF se jmenuje prirubove-pouzdro-A3.pdf a rámeček má 420 × 297 mm. +- Soubor dwg odevzdejte spolu s PDF. Bez modelu výkres nejde opravit. + +> Tip: otevřete PDF na celou stránku. Když je razítko useknuté nebo šrafa černá, tisk opakujte. Na obrazovce AutoCADu to vidět nebylo. + + +# Kvíz + +## Jaká je tloušťka stěny válce Ø70 s dírou Ø40? +- 30 mm +- [x] 15 mm +- 40 mm +- 12 mm +> Rozdíl průměrů je 30 mm na obě strany. Jedna stěna má 15 mm. 12 mm je tloušťka příruby, ne stěny. + +## Kam patří čtyři díry Ø11? +- Skrz celou délku 60 +- [x] Skrz přírubu tlustou 12, na Ø90 +- Do díry Ø40 +- Na osu jako závit M11 +> Leží v materiálu příruby mezi Ø70 a Ø110. Rozteč 90, poloměr 45. + +## Jaké měřítko má model i tisk tohoto výkresu? +- Model 1:2, tisk 1:2 +- [x] Model 1:1, tisk rozvržení 1:1 +- Model 2:1, tisk Fit +- Měřítko se do razítka nepíše +> Součást i papír jsou ve skutečných milimetrech. Měřítko pohledu je 1:1, protože se A3 vejde. + +## Co odevzdáváte spolu s PDF? +- Jen izometrii +- [x] I soubor dwg +- Rozbitý blok razítka +- Výkres bez kót, kóty jsou v PDF +> Z PDF už model neopravíte. DwG je práce, PDF je odevzdaný tisk. diff --git a/ios/Graduately/AppModel.swift b/ios/Graduately/AppModel.swift index 5d7d419..9d324ec 100644 --- a/ios/Graduately/AppModel.swift +++ b/ios/Graduately/AppModel.swift @@ -234,6 +234,86 @@ final class AppModel: ObservableObject { refresh() } + func markTg(_ id: Int) { + progress.markTg(id) + refresh() + } + + func markTg2(_ id: Int) { + progress.markTg2(id) + refresh() + } + + func markTg3(_ id: Int) { + progress.markTg3(id) + refresh() + } + + func markPg(_ id: Int) { + progress.markPg(id) + refresh() + } + + func markPg2(_ id: Int) { + progress.markPg2(id) + refresh() + } + + func markPg3(_ id: Int) { + progress.markPg3(id) + refresh() + } + + func markPg4(_ id: Int) { + progress.markPg4(id) + refresh() + } + + func markSw(_ id: Int) { + progress.markSw(id) + refresh() + } + + func markSw2(_ id: Int) { + progress.markSw2(id) + refresh() + } + + func markSw3(_ id: Int) { + progress.markSw3(id) + refresh() + } + + func markSw4(_ id: Int) { + progress.markSw4(id) + refresh() + } + + func markTg4(_ id: Int) { + progress.markTg4(id) + refresh() + } + + func markCs(_ id: Int) { + progress.markCs(id) + refresh() + } + + func markCs2(_ id: Int) { + progress.markCs2(id) + refresh() + } + + func markCs3(_ id: Int) { + progress.markCs3(id) + refresh() + } + + func markCs4(_ id: Int) { + progress.markCs4(id) + refresh() + } + func markMat0(_ id: Int) { progress.markMat0(id) refresh() diff --git a/ios/Graduately/Data/Content.swift b/ios/Graduately/Data/Content.swift index 7480e01..dafa975 100644 --- a/ios/Graduately/Data/Content.swift +++ b/ios/Graduately/Data/Content.swift @@ -59,6 +59,22 @@ final class Content { let fyz2: [J] let fyz3: [J] let fyz4: [J] + let tg: [J] + let tg2: [J] + let tg3: [J] + let tg4: [J] + let pg: [J] + let pg2: [J] + let pg3: [J] + let pg4: [J] + let sw: [J] + let sw2: [J] + let sw3: [J] + let sw4: [J] + let cs: [J] + let cs2: [J] + let cs3: [J] + let cs4: [J] let mat0: [J] let mat: [J] let mat2: [J] @@ -125,6 +141,22 @@ final class Content { fyz2 = raw.arr("fyz2") fyz3 = raw.arr("fyz3") fyz4 = raw.arr("fyz4") + tg = raw.arr("tg") + tg2 = raw.arr("tg2") + tg3 = raw.arr("tg3") + tg4 = raw.arr("tg4") + pg = raw.arr("pg") + pg2 = raw.arr("pg2") + pg3 = raw.arr("pg3") + pg4 = raw.arr("pg4") + sw = raw.arr("sw") + sw2 = raw.arr("sw2") + sw3 = raw.arr("sw3") + sw4 = raw.arr("sw4") + cs = raw.arr("cs") + cs2 = raw.arr("cs2") + cs3 = raw.arr("cs3") + cs4 = raw.arr("cs4") mat0 = raw.arr("mat0") mat = raw.arr("mat") mat2 = raw.arr("mat2") @@ -187,6 +219,22 @@ final class Content { func fyz2Lesson(_ id: Int) -> J? { fyz2.first { $0.int("id") == id } } func fyz3Lesson(_ id: Int) -> J? { fyz3.first { $0.int("id") == id } } func fyz4Lesson(_ id: Int) -> J? { fyz4.first { $0.int("id") == id } } + func tgLesson(_ id: Int) -> J? { tg.first { $0.int("id") == id } } + func tg2Lesson(_ id: Int) -> J? { tg2.first { $0.int("id") == id } } + func tg3Lesson(_ id: Int) -> J? { tg3.first { $0.int("id") == id } } + func tg4Lesson(_ id: Int) -> J? { tg4.first { $0.int("id") == id } } + func pgLesson(_ id: Int) -> J? { pg.first { $0.int("id") == id } } + func pg2Lesson(_ id: Int) -> J? { pg2.first { $0.int("id") == id } } + func pg3Lesson(_ id: Int) -> J? { pg3.first { $0.int("id") == id } } + func pg4Lesson(_ id: Int) -> J? { pg4.first { $0.int("id") == id } } + func swLesson(_ id: Int) -> J? { sw.first { $0.int("id") == id } } + func sw2Lesson(_ id: Int) -> J? { sw2.first { $0.int("id") == id } } + func sw3Lesson(_ id: Int) -> J? { sw3.first { $0.int("id") == id } } + func sw4Lesson(_ id: Int) -> J? { sw4.first { $0.int("id") == id } } + func csLesson(_ id: Int) -> J? { cs.first { $0.int("id") == id } } + func cs2Lesson(_ id: Int) -> J? { cs2.first { $0.int("id") == id } } + func cs3Lesson(_ id: Int) -> J? { cs3.first { $0.int("id") == id } } + func cs4Lesson(_ id: Int) -> J? { cs4.first { $0.int("id") == id } } func mat0Lesson(_ id: Int) -> J? { mat0.first { $0.int("id") == id } } func matLesson(_ id: Int) -> J? { mat.first { $0.int("id") == id } } func mat2Lesson(_ id: Int) -> J? { mat2.first { $0.int("id") == id } } diff --git a/ios/Graduately/Data/ProgressStore.swift b/ios/Graduately/Data/ProgressStore.swift index ddbf220..f77c330 100644 --- a/ios/Graduately/Data/ProgressStore.swift +++ b/ios/Graduately/Data/ProgressStore.swift @@ -94,6 +94,38 @@ final class ProgressStore { func markFyz3(_ id: Int) { mark("fyz3.\(id)") } func fyz4Done(_ id: Int) -> Bool { done("fyz4.\(id)") } func markFyz4(_ id: Int) { mark("fyz4.\(id)") } + func tgDone(_ id: Int) -> Bool { done("tg.\(id)") } + func markTg(_ id: Int) { mark("tg.\(id)") } + func tg2Done(_ id: Int) -> Bool { done("tg2.\(id)") } + func markTg2(_ id: Int) { mark("tg2.\(id)") } + func tg3Done(_ id: Int) -> Bool { done("tg3.\(id)") } + func markTg3(_ id: Int) { mark("tg3.\(id)") } + func tg4Done(_ id: Int) -> Bool { done("tg4.\(id)") } + func markTg4(_ id: Int) { mark("tg4.\(id)") } + func pgDone(_ id: Int) -> Bool { done("pg.\(id)") } + func markPg(_ id: Int) { mark("pg.\(id)") } + func pg2Done(_ id: Int) -> Bool { done("pg2.\(id)") } + func markPg2(_ id: Int) { mark("pg2.\(id)") } + func pg3Done(_ id: Int) -> Bool { done("pg3.\(id)") } + func markPg3(_ id: Int) { mark("pg3.\(id)") } + func pg4Done(_ id: Int) -> Bool { done("pg4.\(id)") } + func markPg4(_ id: Int) { mark("pg4.\(id)") } + func swDone(_ id: Int) -> Bool { done("sw.\(id)") } + func markSw(_ id: Int) { mark("sw.\(id)") } + func sw2Done(_ id: Int) -> Bool { done("sw2.\(id)") } + func markSw2(_ id: Int) { mark("sw2.\(id)") } + func sw3Done(_ id: Int) -> Bool { done("sw3.\(id)") } + func markSw3(_ id: Int) { mark("sw3.\(id)") } + func sw4Done(_ id: Int) -> Bool { done("sw4.\(id)") } + func markSw4(_ id: Int) { mark("sw4.\(id)") } + func csDone(_ id: Int) -> Bool { done("cs.\(id)") } + func markCs(_ id: Int) { mark("cs.\(id)") } + func cs2Done(_ id: Int) -> Bool { done("cs2.\(id)") } + func markCs2(_ id: Int) { mark("cs2.\(id)") } + func cs3Done(_ id: Int) -> Bool { done("cs3.\(id)") } + func markCs3(_ id: Int) { mark("cs3.\(id)") } + func cs4Done(_ id: Int) -> Bool { done("cs4.\(id)") } + func markCs4(_ id: Int) { mark("cs4.\(id)") } func mat0Done(_ id: Int) -> Bool { done("mat0.\(id)") } func markMat0(_ id: Int) { mark("mat0.\(id)") } func matDone(_ id: Int) -> Bool { done("mat.\(id)") } @@ -288,6 +320,134 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum { sum.doneUnits += 1 } } + for l in content.tg { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tgDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.tg2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tg2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.tg3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tg3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.tg4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.tg4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.pg { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pgDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.pg2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pg2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.pg3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pg3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.pg4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.pg4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.sw { + sum.totalEx += 1 + sum.openUnits += 1 + if p.swDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.sw2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.sw2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.sw3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.sw3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.sw4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.sw4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.cs { + sum.totalEx += 1 + sum.openUnits += 1 + if p.csDone(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.cs2 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.cs2Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.cs3 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.cs3Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } + for l in content.cs4 { + sum.totalEx += 1 + sum.openUnits += 1 + if p.cs4Done(l.int("id")) { + sum.doneEx += 1 + sum.doneUnits += 1 + } + } for l in content.mat0 { sum.totalEx += 1 sum.openUnits += 1 diff --git a/ios/Graduately/Route.swift b/ios/Graduately/Route.swift index b6290a6..a85395c 100644 --- a/ios/Graduately/Route.swift +++ b/ios/Graduately/Route.swift @@ -111,6 +111,54 @@ enum Route: Hashable { case fyz4Map case fyz4Lesson(Int) case fyz4Ex(Int) + case tgYears + case tgMap + case tgLesson(Int) + case tgEx(Int) + case tg2Map + case tg2Lesson(Int) + case tg2Ex(Int) + case tg3Map + case tg3Lesson(Int) + case tg3Ex(Int) + case tg4Map + case tg4Lesson(Int) + case tg4Ex(Int) + case pgYears + case pgMap + case pgLesson(Int) + case pgEx(Int) + case pg2Map + case pg2Lesson(Int) + case pg2Ex(Int) + case pg3Map + case pg3Lesson(Int) + case pg3Ex(Int) + case pg4Map + case pg4Lesson(Int) + case pg4Ex(Int) + case swYears + case swMap + case swLesson(Int) + case swEx(Int) + case sw2Map + case sw2Lesson(Int) + case sw2Ex(Int) + case sw3Map + case sw3Lesson(Int) + case sw3Ex(Int) + case sw4Map + case sw4Lesson(Int) + case sw4Ex(Int) + case csYears + case csMap + case csLesson(Int) + case cs2Map + case cs2Lesson(Int) + case cs3Map + case cs3Lesson(Int) + case cs4Map + case cs4Lesson(Int) case czechMap case mluvnice case mluvEx(Int) @@ -224,6 +272,54 @@ enum Route: Hashable { case .fyz4Map: return "fyz4map" case .fyz4Lesson(let id): return "fyz4unit\(id)" case .fyz4Ex(let id): return "fyz4ex\(id)" + case .tgYears: return "tgyears" + case .tgMap: return "tgmap" + case .tgLesson(let id): return "tgunit\(id)" + case .tgEx(let id): return "tgex\(id)" + case .tg2Map: return "tg2map" + case .tg2Lesson(let id): return "tg2unit\(id)" + case .tg2Ex(let id): return "tg2ex\(id)" + case .tg3Map: return "tg3map" + case .tg3Lesson(let id): return "tg3unit\(id)" + case .tg3Ex(let id): return "tg3ex\(id)" + case .tg4Map: return "tg4map" + case .tg4Lesson(let id): return "tg4unit\(id)" + case .tg4Ex(let id): return "tg4ex\(id)" + case .pgYears: return "pgyears" + case .pgMap: return "pgmap" + case .pgLesson(let id): return "pgunit\(id)" + case .pgEx(let id): return "pgex\(id)" + case .pg2Map: return "pg2map" + case .pg2Lesson(let id): return "pg2unit\(id)" + case .pg2Ex(let id): return "pg2ex\(id)" + case .pg3Map: return "pg3map" + case .pg3Lesson(let id): return "pg3unit\(id)" + case .pg3Ex(let id): return "pg3ex\(id)" + case .pg4Map: return "pg4map" + case .pg4Lesson(let id): return "pg4unit\(id)" + case .pg4Ex(let id): return "pg4ex\(id)" + case .swYears: return "swyears" + case .swMap: return "swmap" + case .swLesson(let id): return "swunit\(id)" + case .swEx(let id): return "swex\(id)" + case .sw2Map: return "sw2map" + case .sw2Lesson(let id): return "sw2unit\(id)" + case .sw2Ex(let id): return "sw2ex\(id)" + case .sw3Map: return "sw3map" + case .sw3Lesson(let id): return "sw3unit\(id)" + case .sw3Ex(let id): return "sw3ex\(id)" + case .sw4Map: return "sw4map" + case .sw4Lesson(let id): return "sw4unit\(id)" + case .sw4Ex(let id): return "sw4ex\(id)" + case .csYears: return "csyears" + case .csMap: return "csmap" + case .csLesson(let id): return "csunit\(id)" + case .cs2Map: return "cs2map" + case .cs2Lesson(let id): return "cs2unit\(id)" + case .cs3Map: return "cs3map" + case .cs3Lesson(let id): return "cs3unit\(id)" + case .cs4Map: return "cs4map" + case .cs4Lesson(let id): return "cs4unit\(id)" case .czechMap: return "czechmap" case .mluvnice: return "mluvnice" case .mluvEx(let n): return "mluve\(n)" @@ -448,6 +544,102 @@ enum Route: Hashable { return [.fyzYears, .fyz4Map, .fyz4Lesson(id)] case .fyz4Ex(let id): return [.fyzYears, .fyz4Map, .fyz4Lesson(id), .fyz4Ex(id)] + case .tgYears: + return [.tgYears] + case .tgMap: + return [.tgYears, .tgMap] + case .tgLesson(let id): + return [.tgYears, .tgMap, .tgLesson(id)] + case .tgEx(let id): + return [.tgYears, .tgMap, .tgLesson(id), .tgEx(id)] + case .tg2Map: + return [.tgYears, .tg2Map] + case .tg2Lesson(let id): + return [.tgYears, .tg2Map, .tg2Lesson(id)] + case .tg2Ex(let id): + return [.tgYears, .tg2Map, .tg2Lesson(id), .tg2Ex(id)] + case .tg3Map: + return [.tgYears, .tg3Map] + case .tg3Lesson(let id): + return [.tgYears, .tg3Map, .tg3Lesson(id)] + case .tg3Ex(let id): + return [.tgYears, .tg3Map, .tg3Lesson(id), .tg3Ex(id)] + case .tg4Map: + return [.tgYears, .tg4Map] + case .tg4Lesson(let id): + return [.tgYears, .tg4Map, .tg4Lesson(id)] + case .tg4Ex(let id): + return [.tgYears, .tg4Map, .tg4Lesson(id), .tg4Ex(id)] + case .pgYears: + return [.pgYears] + case .pgMap: + return [.pgYears, .pgMap] + case .pgLesson(let id): + return [.pgYears, .pgMap, .pgLesson(id)] + case .pgEx(let id): + return [.pgYears, .pgMap, .pgLesson(id), .pgEx(id)] + case .pg2Map: + return [.pgYears, .pg2Map] + case .pg2Lesson(let id): + return [.pgYears, .pg2Map, .pg2Lesson(id)] + case .pg2Ex(let id): + return [.pgYears, .pg2Map, .pg2Lesson(id), .pg2Ex(id)] + case .pg3Map: + return [.pgYears, .pg3Map] + case .pg3Lesson(let id): + return [.pgYears, .pg3Map, .pg3Lesson(id)] + case .pg3Ex(let id): + return [.pgYears, .pg3Map, .pg3Lesson(id), .pg3Ex(id)] + case .pg4Map: + return [.pgYears, .pg4Map] + case .pg4Lesson(let id): + return [.pgYears, .pg4Map, .pg4Lesson(id)] + case .pg4Ex(let id): + return [.pgYears, .pg4Map, .pg4Lesson(id), .pg4Ex(id)] + case .swYears: + return [.swYears] + case .swMap: + return [.swYears, .swMap] + case .swLesson(let id): + return [.swYears, .swMap, .swLesson(id)] + case .swEx(let id): + return [.swYears, .swMap, .swLesson(id), .swEx(id)] + case .sw2Map: + return [.swYears, .sw2Map] + case .sw2Lesson(let id): + return [.swYears, .sw2Map, .sw2Lesson(id)] + case .sw2Ex(let id): + return [.swYears, .sw2Map, .sw2Lesson(id), .sw2Ex(id)] + case .sw3Map: + return [.swYears, .sw3Map] + case .sw3Lesson(let id): + return [.swYears, .sw3Map, .sw3Lesson(id)] + case .sw3Ex(let id): + return [.swYears, .sw3Map, .sw3Lesson(id), .sw3Ex(id)] + case .sw4Map: + return [.swYears, .sw4Map] + case .sw4Lesson(let id): + return [.swYears, .sw4Map, .sw4Lesson(id)] + case .sw4Ex(let id): + return [.swYears, .sw4Map, .sw4Lesson(id), .sw4Ex(id)] + case .csYears: + return [.csYears] + case .csMap: + return [.csYears, .csMap] + case .csLesson(let id): + return [.csYears, .csMap, .csLesson(id)] + case .cs2Map: + return [.csYears, .cs2Map] + case .cs2Lesson(let id): + return [.csYears, .cs2Map, .cs2Lesson(id)] + case .cs3Map: + return [.csYears, .cs3Map] + case .cs3Lesson(let id): + return [.csYears, .cs3Map, .cs3Lesson(id)] + case .cs4Map: + return [.csYears, .cs4Map] + case .cs4Lesson(let id): + return [.csYears, .cs4Map, .cs4Lesson(id)] case .czechMap: return [.czechMap] case .mluvnice: @@ -508,6 +700,26 @@ func routeFromPage(_ page: String) -> Route? { case "fyz2map": return .fyz2Map case "fyz3map": return .fyz3Map case "fyz4map": return .fyz4Map + case "tgyears": return .tgYears + case "tgmap": return .tgMap + case "tg2map": return .tg2Map + case "tg3map": return .tg3Map + case "tg4map": return .tg4Map + case "pgyears": return .pgYears + case "pgmap": return .pgMap + case "pg2map": return .pg2Map + case "pg3map": return .pg3Map + case "pg4map": return .pg4Map + case "swyears": return .swYears + case "swmap": return .swMap + case "sw2map": return .sw2Map + case "sw3map": return .sw3Map + case "sw4map": return .sw4Map + case "csyears": return .csYears + case "csmap": return .csMap + case "cs2map": return .cs2Map + case "cs3map": return .cs3Map + case "cs4map": return .cs4Map case "czechmap": return .czechMap case "mluvnice": return .mluvnice case "readinglist": return .readingList @@ -617,6 +829,90 @@ func routeFromPage(_ page: String) -> Route? { if let m = page.wholeMatch(of: /fyzex(\d+)/), let n = Int(m.1) { return .fyzEx(n) } + if let m = page.wholeMatch(of: /sw4unit(\d+)/), let n = Int(m.1) { + return .sw4Lesson(n) + } + if let m = page.wholeMatch(of: /sw4ex(\d+)/), let n = Int(m.1) { + return .sw4Ex(n) + } + if let m = page.wholeMatch(of: /sw3unit(\d+)/), let n = Int(m.1) { + return .sw3Lesson(n) + } + if let m = page.wholeMatch(of: /sw3ex(\d+)/), let n = Int(m.1) { + return .sw3Ex(n) + } + if let m = page.wholeMatch(of: /sw2unit(\d+)/), let n = Int(m.1) { + return .sw2Lesson(n) + } + if let m = page.wholeMatch(of: /sw2ex(\d+)/), let n = Int(m.1) { + return .sw2Ex(n) + } + if let m = page.wholeMatch(of: /swunit(\d+)/), let n = Int(m.1) { + return .swLesson(n) + } + if let m = page.wholeMatch(of: /swex(\d+)/), let n = Int(m.1) { + return .swEx(n) + } + if let m = page.wholeMatch(of: /pg4unit(\d+)/), let n = Int(m.1) { + return .pg4Lesson(n) + } + if let m = page.wholeMatch(of: /pg4ex(\d+)/), let n = Int(m.1) { + return .pg4Ex(n) + } + if let m = page.wholeMatch(of: /pg3unit(\d+)/), let n = Int(m.1) { + return .pg3Lesson(n) + } + if let m = page.wholeMatch(of: /pg3ex(\d+)/), let n = Int(m.1) { + return .pg3Ex(n) + } + if let m = page.wholeMatch(of: /pg2unit(\d+)/), let n = Int(m.1) { + return .pg2Lesson(n) + } + if let m = page.wholeMatch(of: /pg2ex(\d+)/), let n = Int(m.1) { + return .pg2Ex(n) + } + if let m = page.wholeMatch(of: /pgunit(\d+)/), let n = Int(m.1) { + return .pgLesson(n) + } + if let m = page.wholeMatch(of: /pgex(\d+)/), let n = Int(m.1) { + return .pgEx(n) + } + if let m = page.wholeMatch(of: /tg4unit(\d+)/), let n = Int(m.1) { + return .tg4Lesson(n) + } + if let m = page.wholeMatch(of: /tg4ex(\d+)/), let n = Int(m.1) { + return .tg4Ex(n) + } + if let m = page.wholeMatch(of: /tg3unit(\d+)/), let n = Int(m.1) { + return .tg3Lesson(n) + } + if let m = page.wholeMatch(of: /tg3ex(\d+)/), let n = Int(m.1) { + return .tg3Ex(n) + } + if let m = page.wholeMatch(of: /tg2unit(\d+)/), let n = Int(m.1) { + return .tg2Lesson(n) + } + if let m = page.wholeMatch(of: /tg2ex(\d+)/), let n = Int(m.1) { + return .tg2Ex(n) + } + if let m = page.wholeMatch(of: /tgunit(\d+)/), let n = Int(m.1) { + return .tgLesson(n) + } + if let m = page.wholeMatch(of: /tgex(\d+)/), let n = Int(m.1) { + return .tgEx(n) + } + if let m = page.wholeMatch(of: /cs4unit(\d+)/), let n = Int(m.1) { + return .cs4Lesson(n) + } + if let m = page.wholeMatch(of: /cs3unit(\d+)/), let n = Int(m.1) { + return .cs3Lesson(n) + } + if let m = page.wholeMatch(of: /cs2unit(\d+)/), let n = Int(m.1) { + return .cs2Lesson(n) + } + if let m = page.wholeMatch(of: /csunit(\d+)/), let n = Int(m.1) { + return .csLesson(n) + } if let m = page.wholeMatch(of: /lit4unit(\d+)/), let n = Int(m.1) { return .lit4Lesson(n) } diff --git a/ios/Graduately/UI/Components.swift b/ios/Graduately/UI/Components.swift index 5c74383..e0aaf2a 100644 --- a/ios/Graduately/UI/Components.swift +++ b/ios/Graduately/UI/Components.swift @@ -50,109 +50,47 @@ enum MacChrome { static let pageInset: CGFloat = 28 static let cardPadding: CGFloat = 22 static let listRowMinHeight: CGFloat = 52 - static let homeTileMin: CGFloat = 240 - static let homeTileHeight: CGFloat = 168 + static let homeTileMin: CGFloat = 260 + static let homeTileHeight: CGFloat = 172 static let pathNode: CGFloat = 84 + /// Pages stop growing here so cards and text keep their shape on wide displays. + static let contentWidth: CGFloat = 1240 + static let readableWidth: CGFloat = 860 + static let actionWidth: CGFloat = 520 } -enum MacGrid { - /// Picks a column count that fills the width and keeps tiles wider than they are tall. - static func columns(width: CGFloat, height: CGFloat, count: Int, minWidth: CGFloat, minHeight: CGFloat, spacing: CGFloat) -> Int { - guard count > 0, width > 0 else { return 1 } - let maxCols = max(1, min(count, Int((width + spacing) / (minWidth + spacing)))) - var best = maxCols - var bestScore = CGFloat.greatestFiniteMagnitude - let offeredH = height > 1 ? height : minHeight * CGFloat(count) - for cols in 1...maxCols { - let rows = Int(ceil(Double(count) / Double(cols))) - let tileW = (width - spacing * CGFloat(cols - 1)) / CGFloat(cols) - if tileW + 0.5 < minWidth { continue } - let needed = CGFloat(rows) * minHeight + spacing * CGFloat(max(rows - 1, 0)) - let tileH = max(minHeight, (offeredH - spacing * CGFloat(max(rows - 1, 0))) / CGFloat(rows)) - let aspect = tileH / max(tileW, 1) - let rem = count % cols - let ragged = rem == 0 ? 0 : CGFloat(1.5) - let lonely = rem == 1 ? CGFloat(1.4) : 0 - let tooTall = max(0, aspect - 1.05) * 3 - let overflow = max(0, needed - offeredH) - let score = abs(aspect - 0.78) * 2 + ragged + lonely + tooTall + overflow / 180 - if score < bestScore { - bestScore = score - best = cols - } - } - return best - } - - static func contentHeight(width: CGFloat, height: CGFloat, count: Int, minWidth: CGFloat, minHeight: CGFloat, spacing: CGFloat) -> CGFloat { - let cols = columns(width: width, height: height, count: count, minWidth: minWidth, minHeight: minHeight, spacing: spacing) - let rows = max(1, Int(ceil(Double(max(count, 1)) / Double(cols)))) - return CGFloat(rows) * minHeight + spacing * CGFloat(max(rows - 1, 0)) - } -} - -struct MacHeaderHeight: PreferenceKey { - static var defaultValue: CGFloat = 0 - static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { - value = max(value, nextValue()) - } -} - -/// Card grid that grows into the space it is given and scrolls only when the -/// minimum tile size no longer fits. +/// Fixed-height cards in as many columns as fit, centred and capped in width. struct MacFillGrid: View { var count: Int var minWidth: CGFloat = 240 var minHeight: CGFloat = 148 var spacing: CGFloat = 16 var inset: CGFloat = 28 - /// Home embeds the grid in its own scroll view. A second scroller here - /// swallows the wheel once a banner (the changelog) makes the page taller. - var scrolls = true + var maxTileWidth: CGFloat = 340 @ViewBuilder var content: (Int) -> Content var body: some View { GeometryReader { geo in - let innerW = max(geo.size.width - inset * 2, minWidth) - let available = max(geo.size.height - inset * 2, minHeight) - let cols = MacGrid.columns(width: innerW, height: available, count: count, minWidth: minWidth, minHeight: minHeight, spacing: spacing) - let rows = max(1, Int(ceil(Double(max(count, 1)) / Double(cols)))) - let floorH = CGFloat(rows) * minHeight + spacing * CGFloat(max(rows - 1, 0)) - let gridH = max(available, floorH) - let tileH = max(minHeight, (gridH - spacing * CGFloat(max(rows - 1, 0))) / CGFloat(rows)) - let tileW = (innerW - spacing * CGFloat(max(cols - 1, 0))) / CGFloat(max(cols, 1)) - let lastCount = count - (rows - 1) * cols - let tiles = VStack(spacing: spacing) { - ForEach(0.. 1 - HStack(spacing: spacing) { - ForEach(0.. 0 { + HStack(spacing: 10) { + ProgressBar(value: Double(progress.done) / Double(progress.total)) + Text("\(progress.done) / \(progress.total)") + .font(.subheadline.monospacedDigit()) .foregroundStyle(.secondary) } } - Text(title) - .font(.title2.weight(.semibold)) - .foregroundStyle(locked ? .secondary : .primary) - .fixedSize(horizontal: false, vertical: true) - if let subtitle, !subtitle.isEmpty { - Text(subtitle) - .font(.body) - .foregroundStyle(.secondary) - .fixedSize(horizontal: false, vertical: true) - } - Spacer(minLength: 0) } - .padding(22) + .padding(20) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - .softSurface(cornerRadius: 22) + .softSurface(cornerRadius: 20) + .contentShape(RoundedRectangle(cornerRadius: 20, style: .continuous)) } } #endif +/// Flat capsule bar. Unlike ProgressView it shows nothing at zero. +struct ProgressBar: View { + let value: Double + var height: CGFloat = 8 + + var body: some View { + GeometryReader { geo in + ZStack(alignment: .leading) { + Capsule().fill(Color.primary.opacity(0.08)) + if value > 0 { + Capsule() + .fill(Color.accentColor) + .frame(width: max(height, geo.size.width * min(value, 1))) + } + } + } + .frame(height: height) + .animation(Motion.settle, value: value) + } +} + +struct ProgressRing: View { + let value: Double + var lineWidth: CGFloat = 12 + @ViewBuilder var label: () -> Label + + var body: some View { + ZStack { + Circle().stroke(Color.primary.opacity(0.08), lineWidth: lineWidth) + if value > 0 { + Circle() + .trim(from: 0, to: min(value, 1)) + .stroke(Color.accentColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) + .rotationEffect(.degrees(-90)) + } + label() + } + .animation(Motion.settle, value: value) + } +} + struct FlowLayout: Layout { var spacing: CGFloat = 6 @@ -574,15 +570,28 @@ extension View { #endif } - /// Full-width, leading-aligned content for exercise screens. + /// Leading-aligned content for exercise screens; on the Mac a centred readable column. func exerciseContent() -> some View { - frame(maxWidth: .infinity, alignment: .leading) - #if os(macOS) + #if os(macOS) + frame(maxWidth: MacChrome.readableWidth, alignment: .leading) .padding(.horizontal, MacChrome.pageInset) - .padding(.vertical, 20) - #else + .padding(.vertical, 28) + .frame(maxWidth: .infinity) + #else + frame(maxWidth: .infinity, alignment: .leading) .padding() - #endif + #endif + } + + /// Caps a Mac page at [width] and centres it; no-op on iOS. + @ViewBuilder + func macPage(_ width: CGFloat? = nil) -> some View { + #if os(macOS) + frame(maxWidth: width ?? MacChrome.contentWidth) + .frame(maxWidth: .infinity) + #else + self + #endif } /// macOS content lists use the inset style so they sit under the floating @@ -636,7 +645,8 @@ struct BottomAction: View { }()) #if os(macOS) .padding(.top, 12) - .padding(.bottom, 10) + .padding(.bottom, 14) + .frame(maxWidth: MacChrome.actionWidth) .frame(maxWidth: .infinity) #else .padding(.top, 8) @@ -645,10 +655,11 @@ struct BottomAction: View { } } -/// Primary + secondary text sized for desktop lists. +/// Primary + secondary text sized for desktop lists, with optional exercise progress. struct ListRowLabel: View { let title: String var subtitle: String? = nil + var progress: (done: Int, total: Int)? = nil var body: some View { VStack(alignment: .leading, spacing: 3) { @@ -665,6 +676,25 @@ struct ListRowLabel: View { #endif .foregroundStyle(.secondary) } + if let progress, progress.total > 0 { + ProgressLine(done: progress.done, total: progress.total) + .padding(.top, 4) + } + } + } +} + +/// Slim bar with "done / total" beside it, for list rows. +struct ProgressLine: View { + let done: Int + let total: Int + + var body: some View { + HStack(spacing: 8) { + ProgressBar(value: total == 0 ? 0 : Double(done) / Double(total), height: 5) + Text("\(done) / \(total)") + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) } } } @@ -679,6 +709,10 @@ func subjectSymbol(_ icon: String) -> String { case "flask": return "flask.fill" case "bolt": return "bolt.fill" case "function": return "function" + case "ruler": return "ruler.fill" + case "code": return "chevron.left.forwardslash.chevron.right" + case "brush": return "paintbrush.fill" + case "apps": return "square.grid.2x2.fill" default: return "lock.fill" } } diff --git a/ios/Graduately/UI/Exercises.swift b/ios/Graduately/UI/Exercises.swift index e71f134..fb4ccea 100644 --- a/ios/Graduately/UI/Exercises.swift +++ b/ios/Graduately/UI/Exercises.swift @@ -12,21 +12,9 @@ struct ExerciseScaffold: View { var body: some View { let p = vm.palette - Group { - #if os(macOS) - GeometryReader { geo in - ScrollView { - VStack(alignment: .leading, spacing: 8) { content() } - .exerciseContent() - .frame(maxWidth: .infinity, minHeight: geo.size.height, alignment: .center) - } - } - #else - ScrollView { - VStack(alignment: .leading, spacing: 8) { content() } - .exerciseContent() - } - #endif + ScrollView { + VStack(alignment: .leading, spacing: 8) { content() } + .exerciseContent() } .scrollDismissesKeyboard(.interactively) .detailScreen(title, subtitle: sub) diff --git a/ios/Graduately/UI/ExercisesMore.swift b/ios/Graduately/UI/ExercisesMore.swift index 4987c01..783dd4d 100644 --- a/ios/Graduately/UI/ExercisesMore.swift +++ b/ios/Graduately/UI/ExercisesMore.swift @@ -952,6 +952,105 @@ struct ChoiceLessonScreen: View { } } +struct TgQuizScreen: View { + @ObservedObject var vm: AppModel + let year: Int + let id: Int + + var body: some View { + let lesson: J? = { + switch year { + case 2: return vm.content.tg2Lesson(id) + case 3: return vm.content.tg3Lesson(id) + case 4: return vm.content.tg4Lesson(id) + default: return vm.content.tgLesson(id) + } + }() + if let lesson { + let spec = J([ + "type": "choice", + "title": vm.tr(lesson.str("exTitleKey")), + "sub": lesson.str("quizHeadKey"), + "questions": lesson.rawArray("quiz"), + ]) + DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) { + switch year { + case 2: vm.markTg2(id) + case 3: vm.markTg3(id) + case 4: vm.markTg4(id) + default: vm.markTg(id) + } + } + } + } +} + +struct PgQuizScreen: View { + @ObservedObject var vm: AppModel + let year: Int + let id: Int + + var body: some View { + let lesson: J? = { + switch year { + case 2: return vm.content.pg2Lesson(id) + case 3: return vm.content.pg3Lesson(id) + case 4: return vm.content.pg4Lesson(id) + default: return vm.content.pgLesson(id) + } + }() + if let lesson { + let spec = J([ + "type": "choice", + "title": vm.tr(lesson.str("exTitleKey")), + "sub": lesson.str("quizHeadKey"), + "questions": lesson.rawArray("quiz"), + ]) + DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) { + switch year { + case 2: vm.markPg2(id) + case 3: vm.markPg3(id) + case 4: vm.markPg4(id) + default: vm.markPg(id) + } + } + } + } +} + +struct SwQuizScreen: View { + @ObservedObject var vm: AppModel + let year: Int + let id: Int + + var body: some View { + let lesson: J? = { + switch year { + case 2: return vm.content.sw2Lesson(id) + case 3: return vm.content.sw3Lesson(id) + case 4: return vm.content.sw4Lesson(id) + default: return vm.content.swLesson(id) + } + }() + if let lesson { + let spec = J([ + "type": "choice", + "title": vm.tr(lesson.str("exTitleKey")), + "sub": lesson.str("quizHeadKey"), + "questions": lesson.rawArray("quiz"), + ]) + DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) { + switch year { + case 2: vm.markSw2(id) + case 3: vm.markSw3(id) + case 4: vm.markSw4(id) + default: vm.markSw(id) + } + } + } + } +} + struct FyzQuizScreen: View { @ObservedObject var vm: AppModel let year: Int diff --git a/ios/Graduately/UI/PathMap.swift b/ios/Graduately/UI/PathMap.swift index 3271b2e..5c7e4e4 100644 --- a/ios/Graduately/UI/PathMap.swift +++ b/ios/Graduately/UI/PathMap.swift @@ -8,6 +8,8 @@ struct MapNode: Identifiable { let current: Bool var finish: Bool = false var destination: Route? + /// Glyph for a node that has no number, such as a side branch. + var symbol: String? = nil } private struct NodePos { @@ -256,6 +258,8 @@ struct PathMap: View { .overlay { if node.locked { Circle().strokeBorder(palette.lockedBorder, lineWidth: 1) + } else if !node.current && !node.done { + Circle().strokeBorder(palette.accent.opacity(0.55), lineWidth: 2.5) } } if node.finish { @@ -298,6 +302,13 @@ struct PathMap: View { #else .font(.headline.weight(.black)) #endif + } else if let symbol = node.symbol { + Image(systemName: symbol) + #if os(macOS) + .font(.title3.weight(.semibold)) + #else + .font(.body.weight(.semibold)) + #endif } Image(systemName: "checkmark") #if os(macOS) @@ -315,6 +326,14 @@ struct PathMap: View { .font(.headline.weight(.black)) #endif .foregroundStyle(node.current ? palette.onAccent : palette.text) + } else if let symbol = node.symbol { + Image(systemName: symbol) + #if os(macOS) + .font(.title2.weight(.semibold)) + #else + .font(.headline.weight(.semibold)) + #endif + .foregroundStyle(node.current ? palette.onAccent : palette.text) } } .frame(width: nodeSize, height: nodeSize) diff --git a/ios/Graduately/UI/Screens.swift b/ios/Graduately/UI/Screens.swift index 3113582..d4535b2 100644 --- a/ios/Graduately/UI/Screens.swift +++ b/ios/Graduately/UI/Screens.swift @@ -1,10 +1,10 @@ import SwiftUI +#if os(macOS) +import AppKit +#endif struct PracticeHome: View { @ObservedObject var vm: AppModel - #if os(macOS) - @State private var homeHeader: CGFloat = 280 - #endif var body: some View { #if os(macOS) @@ -18,111 +18,124 @@ struct PracticeHome: View { private var macHome: some View { let sum = summarize(vm.content, vm.progress) let pct = sum.totalEx == 0 ? 0 : Double(sum.doneEx) / Double(sum.totalEx) - let subjects = vm.content.subjects - return GeometryReader { geo in - let width = max(geo.size.width - MacChrome.pageInset * 2, MacChrome.homeTileMin) - let offered = max(geo.size.height - homeHeader - 20 - MacChrome.pageInset * 2, MacChrome.homeTileHeight) - let minGrid = MacGrid.contentHeight( - width: width, - height: offered, - count: subjects.count, - minWidth: MacChrome.homeTileMin, - minHeight: MacChrome.homeTileHeight, - spacing: 16 - ) - let gridH = max(minGrid, geo.size.height - homeHeader - 20 - MacChrome.pageInset * 2) - ScrollView { - VStack(alignment: .leading, spacing: 20) { - VStack(alignment: .leading, spacing: 16) { - if vm.showChangelog { - ChangelogCard(vm: vm) + let open = vm.content.subjects.filter { $0.bool("open") } + let soon = vm.content.subjects.filter { !$0.bool("open") } + return ScrollView { + VStack(alignment: .leading, spacing: 28) { + if vm.showChangelog { + ChangelogCard(vm: vm) + } + welcomeCard(sum: sum, pct: pct) + VStack(alignment: .leading, spacing: 14) { + Text(vm.tr("subjects_title")) + .font(.title2.weight(.semibold)) + LazyVGrid( + columns: [GridItem(.adaptive(minimum: MacChrome.homeTileMin), spacing: 16, alignment: .top)], + alignment: .leading, + spacing: 16 + ) { + ForEach(Array(open.enumerated()), id: \.offset) { _, s in + subjectTile(s) } - GlassCard { - VStack(alignment: .leading, spacing: 12) { - Text(vm.tr("welcome_title")) - .font(.largeTitle.bold()) - Text(vm.tr(Self.welcomeBodyKey)) - .font(.title3) - .foregroundStyle(.secondary) - .fixedSize(horizontal: false, vertical: true) - ProgressView(value: pct) - .animation(Motion.settle, value: pct) - Text(vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx)) - .font(.subheadline) + } + } + if !soon.isEmpty { + VStack(alignment: .leading, spacing: 12) { + Text(vm.tr("subjects_soon")) + .font(.headline) + .foregroundStyle(.secondary) + FlowLayout(spacing: 10) { + ForEach(Array(soon.enumerated()), id: \.offset) { _, s in + Label(vm.tr(s.str("key")), systemImage: "lock.fill") + .font(.body) .foregroundStyle(.secondary) + .padding(.horizontal, 14) + .padding(.vertical, 9) + .background(Color.primary.opacity(0.05), in: Capsule()) } } - Text(vm.tr("subjects_title")) - .font(.title2.weight(.semibold)) } - .fixedSize(horizontal: false, vertical: true) - .background { - GeometryReader { proxy in - Color.clear.preference(key: MacHeaderHeight.self, value: proxy.size.height) - } - } - MacFillGrid( - count: subjects.count, - minWidth: MacChrome.homeTileMin, - minHeight: MacChrome.homeTileHeight, - spacing: 16, - inset: 0, - scrolls: false - ) { index in - subjectTile(subjects[index]) - } - .frame(height: gridH) } - .padding(MacChrome.pageInset) - .frame(maxWidth: .infinity, minHeight: geo.size.height, alignment: .topLeading) } - .onPreferenceChange(MacHeaderHeight.self) { homeHeader = $0 } + .padding(MacChrome.pageInset) + .macPage() } .navigationTitle("Graduately") } - @ViewBuilder - private func subjectTile(_ s: J) -> some View { - let open = s.bool("open") - let dest = s.strOrNull("target").flatMap(routeFromPage) - let part = subjectSum(vm, s) - let progress = open && part.totalEx > 0 ? Double(part.doneEx) / Double(part.totalEx) : 0 - Group { - if open, let dest, dest != .subjects, dest != .welcome { - NavigationLink(value: dest) { - subjectTileBody(s, open: true, progress: progress, part: part) + private func welcomeCard(sum: ProgressSum, pct: Double) -> some View { + GlassCard { + HStack(alignment: .center, spacing: 32) { + VStack(alignment: .leading, spacing: 12) { + Text(vm.tr("welcome_title")) + .font(.system(size: 34, weight: .bold)) + .fixedSize(horizontal: false, vertical: true) + Text(vm.tr(Self.welcomeBodyKey)) + .font(.title3) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) } - .buttonStyle(PressScaleStyle()) - } else { - subjectTileBody(s, open: false, progress: 0, part: part) + .frame(maxWidth: .infinity, alignment: .leading) + ProgressRing(value: pct) { + VStack(spacing: 2) { + Text(vm.fmt("stats_pct_fmt", Int((pct * 100).rounded(.down)))) + .font(.system(size: 26, weight: .bold, design: .rounded)) + .monospacedDigit() + Text(vm.fmt("stats_ex_fmt", sum.doneEx, sum.totalEx)) + .font(.subheadline.monospacedDigit()) + .foregroundStyle(.secondary) + } + } + .frame(width: 132, height: 132) } + .padding(.vertical, 6) } } - private func subjectTileBody(_ s: J, open: Bool, progress: Double, part: ProgressSum) -> some View { - VStack(alignment: .leading, spacing: 16) { - SubjectIcon(icon: s.str("icon"), open: open, size: 44) + @ViewBuilder + private func subjectTile(_ s: J) -> some View { + let dest = s.strOrNull("target").flatMap(routeFromPage) + if let dest, dest != .subjects, dest != .welcome { + NavigationLink(value: dest) { + subjectTileBody(s) + } + .buttonStyle(PressScaleStyle()) + } else { + subjectTileBody(s) + } + } + + private func subjectTileBody(_ s: J) -> some View { + let part = subjectSum(vm, s) + let progress = part.totalEx > 0 ? Double(part.doneEx) / Double(part.totalEx) : 0 + return VStack(alignment: .leading, spacing: 14) { + HStack(alignment: .center) { + SubjectIcon(icon: s.str("icon"), open: true, size: 40) + Spacer(minLength: 0) + Image(systemName: "chevron.right") + .font(.body.weight(.semibold)) + .foregroundStyle(.tertiary) + } Text(vm.tr(s.str("key"))) .font(.title3.weight(.semibold)) - .foregroundStyle(open ? .primary : .secondary) + .foregroundStyle(.primary) .multilineTextAlignment(.leading) + .lineLimit(2) Spacer(minLength: 0) - if open && part.totalEx > 0 { - ProgressView(value: progress) - .animation(Motion.settle, value: progress) + ProgressBar(value: progress) + HStack { Text(vm.fmt("stats_ex_fmt", part.doneEx, part.totalEx)) - .font(.subheadline) - .foregroundStyle(.secondary) - .monospacedDigit() - } else if !open { - Text(vm.tr("stats_locked")) - .font(.subheadline) - .foregroundStyle(.secondary) + Spacer(minLength: 0) + Text(vm.fmt("stats_pct_fmt", Int((progress * 100).rounded(.down)))) } + .font(.subheadline.monospacedDigit()) + .foregroundStyle(.secondary) } - .padding(22) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding(20) + .frame(maxWidth: .infinity, alignment: .topLeading) + .frame(height: MacChrome.homeTileHeight) .softSurface(cornerRadius: 22) + .contentShape(RoundedRectangle(cornerRadius: 22, style: .continuous)) } #endif @@ -160,16 +173,28 @@ struct PracticeHome: View { } Section(vm.tr("subjects_title")) { - ForEach(Array(vm.content.subjects.enumerated()), id: \.offset) { _, s in - let open = s.bool("open") + ForEach(Array(vm.content.subjects.filter { $0.bool("open") }.enumerated()), id: \.offset) { _, s in let dest = s.strOrNull("target").flatMap(routeFromPage) - if open, let dest, dest != .subjects, dest != .welcome { + if let dest, dest != .subjects, dest != .welcome { NavigationLink(value: dest) { subjectLabel(s) } } else { subjectLabel(s) - .foregroundStyle(.secondary) + } + } + } + + let soon = vm.content.subjects.filter { !$0.bool("open") } + if !soon.isEmpty { + Section(vm.tr("subjects_soon")) { + ForEach(Array(soon.enumerated()), id: \.offset) { _, s in + Label { + Text(vm.tr(s.str("key"))) + } icon: { + SubjectIcon(icon: s.str("icon"), open: false) + } + .foregroundStyle(.secondary) } } } @@ -179,15 +204,17 @@ struct PracticeHome: View { } private func subjectLabel(_ s: J) -> some View { - Label { - VStack(alignment: .leading, spacing: 2) { + let part = subjectSum(vm, s) + return Label { + VStack(alignment: .leading, spacing: 6) { Text(vm.tr(s.str("key"))) - if !s.bool("open") { - Text(vm.tr("stats_locked")).font(.caption).foregroundStyle(.secondary) + if part.totalEx > 0 { + ProgressLine(done: part.doneEx, total: part.totalEx) } } + .padding(.vertical, 2) } icon: { - SubjectIcon(icon: s.str("icon"), open: s.bool("open")) + SubjectIcon(icon: s.str("icon"), open: true) } } #endif @@ -423,7 +450,8 @@ struct SettingsScreen: View { } } .padding(MacChrome.pageInset) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: MacChrome.contentWidth, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .navigationTitle(vm.tr("settings_title")) } @@ -566,7 +594,8 @@ struct StatsScreen: View { } } .padding(MacChrome.pageInset) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: MacChrome.contentWidth, maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .navigationTitle(vm.tr("stats_title")) } @@ -664,6 +693,76 @@ struct StatsScreen: View { } } +/// Done and total exercises behind one card of a years screen, or nil when there is no count. +func routeProgress(_ vm: AppModel, _ r: Route) -> (done: Int, total: Int)? { + let c = vm.content + let p = vm.progress + func tally(_ lessons: [J], _ done: (Int) -> Bool) -> (done: Int, total: Int) { + (lessons.filter { done($0.int("id")) }.count, lessons.count) + } + switch r { + case .netMap: return tally(c.netLessons, p.netDone) + case .net2Map: return tally(c.itLessons("net2")) { p.courseDone("net2", $0) } + case .net3Map: return tally(c.itLessons("net3")) { p.courseDone("net3", $0) } + case .net4Map: return tally(c.itLessons("net4")) { p.courseDone("net4", $0) } + case .hwMap: return tally(c.hw, p.hwDone) + case .hw2Map: return tally(c.itLessons("hw2")) { p.courseDone("hw2", $0) } + case .hw3Map: return tally(c.itLessons("hw3")) { p.courseDone("hw3", $0) } + case .hw4Map: return tally(c.itLessons("hw4")) { p.courseDone("hw4", $0) } + case .onMap: return tally(c.on, p.onDone) + case .on2Map: return tally(c.on2, p.on2Done) + case .on3Map: return tally(c.on3, p.on3Done) + case .on4Map: return tally(c.on4, p.on4Done) + case .enMap(let year): return tally(c.enYear(year)) { p.enDone(year, $0) } + case .deMap(let year): return tally(c.deYear(year)) { p.deDone(year, $0) } + case .roadmap: + var done = 0 + var total = 0 + for u in c.german where u.bool("unlocked") { + let n = u.strs("names").count + total += n + done += (0.. ProgressSum { var sum = ProgressSum() switch s.str("target") { @@ -713,6 +812,30 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum { + vm.content.mat2.filter { vm.progress.mat2Done($0.int("id")) }.count + vm.content.mat3.filter { vm.progress.mat3Done($0.int("id")) }.count + vm.content.mat4.filter { vm.progress.mat4Done($0.int("id")) }.count + case "csyears", "csmap", "cs2map", "cs3map", "cs4map": + sum.totalEx = vm.content.cs.count + vm.content.cs2.count + vm.content.cs3.count + vm.content.cs4.count + sum.doneEx = vm.content.cs.filter { vm.progress.csDone($0.int("id")) }.count + + vm.content.cs2.filter { vm.progress.cs2Done($0.int("id")) }.count + + vm.content.cs3.filter { vm.progress.cs3Done($0.int("id")) }.count + + vm.content.cs4.filter { vm.progress.cs4Done($0.int("id")) }.count + case "pgyears", "pgmap", "pg2map", "pg3map", "pg4map": + sum.totalEx = vm.content.pg.count + vm.content.pg2.count + vm.content.pg3.count + vm.content.pg4.count + sum.doneEx = vm.content.pg.filter { vm.progress.pgDone($0.int("id")) }.count + + vm.content.pg2.filter { vm.progress.pg2Done($0.int("id")) }.count + + vm.content.pg3.filter { vm.progress.pg3Done($0.int("id")) }.count + + vm.content.pg4.filter { vm.progress.pg4Done($0.int("id")) }.count + case "swyears", "swmap", "sw2map", "sw3map", "sw4map": + sum.totalEx = vm.content.sw.count + vm.content.sw2.count + vm.content.sw3.count + vm.content.sw4.count + sum.doneEx = vm.content.sw.filter { vm.progress.swDone($0.int("id")) }.count + + vm.content.sw2.filter { vm.progress.sw2Done($0.int("id")) }.count + + vm.content.sw3.filter { vm.progress.sw3Done($0.int("id")) }.count + + vm.content.sw4.filter { vm.progress.sw4Done($0.int("id")) }.count + case "tgyears", "tgmap", "tg2map", "tg3map", "tg4map": + sum.totalEx = vm.content.tg.count + vm.content.tg2.count + vm.content.tg3.count + vm.content.tg4.count + sum.doneEx = vm.content.tg.filter { vm.progress.tgDone($0.int("id")) }.count + + vm.content.tg2.filter { vm.progress.tg2Done($0.int("id")) }.count + + vm.content.tg3.filter { vm.progress.tg3Done($0.int("id")) }.count + + vm.content.tg4.filter { vm.progress.tg4Done($0.int("id")) }.count case "fyzyears", "fyzmap", "fyz2map", "fyz3map", "fyz4map": sum.totalEx = vm.content.fyz.count + vm.content.fyz2.count + vm.content.fyz3.count + vm.content.fyz4.count sum.doneEx = vm.content.fyz.filter { vm.progress.fyzDone($0.int("id")) }.count @@ -783,6 +906,7 @@ struct SearchScreen: View { } } .padding(MacChrome.pageInset) + .macPage() } } .navigationTitle(vm.tr("search")) @@ -1015,6 +1139,86 @@ private func buildSearch(_ vm: AppModel) -> [Hit] { add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit\(l.int("id"))", extra: "fyzika") add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex\(l.int("id"))") } + add(vm.tr("Programové vybavení"), vm.tr("search_lesson"), "swyears", extra: "office windows excel mysql") + add(vm.tr("sw_year1"), vm.tr("search_lesson"), "swmap", extra: "rocnik") + add(vm.tr("sw_year2"), vm.tr("search_lesson"), "sw2map", extra: "rocnik") + add(vm.tr("sw_year3"), vm.tr("search_lesson"), "sw3map", extra: "rocnik") + add(vm.tr("sw_year4"), vm.tr("search_lesson"), "sw4map", extra: "rocnik") + for l in vm.content.sw { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "swunit\(l.int("id"))", extra: "windows") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "swex\(l.int("id"))") + } + for l in vm.content.sw2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw2unit\(l.int("id"))", extra: "word excel") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw2ex\(l.int("id"))") + } + for l in vm.content.sw3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw3unit\(l.int("id"))", extra: "access mysql") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw3ex\(l.int("id"))") + } + for l in vm.content.sw4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "sw4unit\(l.int("id"))", extra: "wordpress") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "sw4ex\(l.int("id"))") + } + add(vm.tr("Prezentační grafika"), vm.tr("search_lesson"), "pgyears", extra: "photoshop premiere illustrator") + add(vm.tr("pg_year1"), vm.tr("search_lesson"), "pgmap", extra: "rocnik") + add(vm.tr("pg_year2"), vm.tr("search_lesson"), "pg2map", extra: "rocnik") + add(vm.tr("pg_year3"), vm.tr("search_lesson"), "pg3map", extra: "rocnik") + add(vm.tr("pg_year4"), vm.tr("search_lesson"), "pg4map", extra: "rocnik") + for l in vm.content.pg { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pgunit\(l.int("id"))", extra: "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pgex\(l.int("id"))") + } + for l in vm.content.pg2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg2unit\(l.int("id"))", extra: "photoshop") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg2ex\(l.int("id"))") + } + for l in vm.content.pg3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg3unit\(l.int("id"))", extra: "premiere") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg3ex\(l.int("id"))") + } + for l in vm.content.pg4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "pg4unit\(l.int("id"))", extra: "illustrator") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "pg4ex\(l.int("id"))") + } + add(vm.tr("Technická grafika"), vm.tr("search_lesson"), "tgyears", extra: "autocad grafika") + add(vm.tr("tg_year1"), vm.tr("search_lesson"), "tgmap", extra: "rocnik") + add(vm.tr("tg_year2"), vm.tr("search_lesson"), "tg2map", extra: "rocnik") + add(vm.tr("tg_year3"), vm.tr("search_lesson"), "tg3map", extra: "rocnik") + add(vm.tr("tg_year4"), vm.tr("search_lesson"), "tg4map", extra: "rocnik") + for l in vm.content.tg { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tgunit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tgex\(l.int("id"))") + } + for l in vm.content.tg2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg2unit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg2ex\(l.int("id"))") + } + for l in vm.content.tg3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg3unit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg3ex\(l.int("id"))") + } + for l in vm.content.tg4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "tg4unit\(l.int("id"))", extra: "autocad") + add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "tg4ex\(l.int("id"))") + } + add(vm.tr("Programování"), vm.tr("search_lesson"), "csyears", extra: "csharp c#") + add(vm.tr("cs_year1"), vm.tr("search_lesson"), "csmap", extra: "rocnik") + add(vm.tr("cs_year2"), vm.tr("search_lesson"), "cs2map", extra: "rocnik") + add(vm.tr("cs_year3"), vm.tr("search_lesson"), "cs3map", extra: "rocnik") + add(vm.tr("cs_year4"), vm.tr("search_lesson"), "cs4map", extra: "rocnik") + for l in vm.content.cs { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "csunit\(l.int("id"))", extra: "csharp") + } + for l in vm.content.cs2 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs2unit\(l.int("id"))", extra: "csharp") + } + for l in vm.content.cs3 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs3unit\(l.int("id"))", extra: "csharp") + } + for l in vm.content.cs4 { + add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "cs4unit\(l.int("id"))", extra: "csharp") + } add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra: "chemie") add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra: "biologie") for l in vm.content.chem { @@ -1058,7 +1262,7 @@ struct RoadmapScreen: View { vm.progress.germanDone(u.int("id"), $0 + 1) } return MapNode( - id: u.str("page"), + id: "unit-\(i)", label: u.str("title"), locked: !unlocked, done: done, @@ -1136,9 +1340,10 @@ struct UnitMapScreen: View { id: "vocab-\(unitId)", label: vm.tr("Vokabeltraining"), locked: false, - done: false, + done: vm.progress.vocabDone(unitId), current: false, - destination: .vocab(unitId) + destination: .vocab(unitId), + symbol: "character.book.closed.fill" ) ) : nil @@ -1206,9 +1411,9 @@ struct NetYearsScreen: View { ("net_year3", "net3_sub", .net3Map), ("net_year4", "net4_sub", .net4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1217,16 +1422,16 @@ struct NetYearsScreen: View { #else List { NavigationLink(value: Route.netMap) { - ListRowLabel(title: vm.tr("net_year1"), subtitle: vm.tr("net_sub")) + ListRowLabel(title: vm.tr("net_year1"), subtitle: vm.tr("net_sub"), progress: routeProgress(vm, Route.netMap)) } NavigationLink(value: Route.net2Map) { - ListRowLabel(title: vm.tr("net_year2"), subtitle: vm.tr("net2_sub")) + ListRowLabel(title: vm.tr("net_year2"), subtitle: vm.tr("net2_sub"), progress: routeProgress(vm, Route.net2Map)) } NavigationLink(value: Route.net3Map) { - ListRowLabel(title: vm.tr("net_year3"), subtitle: vm.tr("net3_sub")) + ListRowLabel(title: vm.tr("net_year3"), subtitle: vm.tr("net3_sub"), progress: routeProgress(vm, Route.net3Map)) } NavigationLink(value: Route.net4Map) { - ListRowLabel(title: vm.tr("net_year4"), subtitle: vm.tr("net4_sub")) + ListRowLabel(title: vm.tr("net_year4"), subtitle: vm.tr("net4_sub"), progress: routeProgress(vm, Route.net4Map)) } } .navigationTitle(vm.tr("net_years_title")) @@ -1247,9 +1452,9 @@ struct HwYearsScreen: View { ("hw_year3", "hw3_sub", .hw3Map), ("hw_year4", "hw4_sub", .hw4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1258,16 +1463,16 @@ struct HwYearsScreen: View { #else List { NavigationLink(value: Route.hwMap) { - ListRowLabel(title: vm.tr("hw_year1"), subtitle: vm.tr("hw_sub")) + ListRowLabel(title: vm.tr("hw_year1"), subtitle: vm.tr("hw_sub"), progress: routeProgress(vm, Route.hwMap)) } NavigationLink(value: Route.hw2Map) { - ListRowLabel(title: vm.tr("hw_year2"), subtitle: vm.tr("hw2_sub")) + ListRowLabel(title: vm.tr("hw_year2"), subtitle: vm.tr("hw2_sub"), progress: routeProgress(vm, Route.hw2Map)) } NavigationLink(value: Route.hw3Map) { - ListRowLabel(title: vm.tr("hw_year3"), subtitle: vm.tr("hw3_sub")) + ListRowLabel(title: vm.tr("hw_year3"), subtitle: vm.tr("hw3_sub"), progress: routeProgress(vm, Route.hw3Map)) } NavigationLink(value: Route.hw4Map) { - ListRowLabel(title: vm.tr("hw_year4"), subtitle: vm.tr("hw4_sub")) + ListRowLabel(title: vm.tr("hw_year4"), subtitle: vm.tr("hw4_sub"), progress: routeProgress(vm, Route.hw4Map)) } } .navigationTitle(vm.tr("Technické vybavení")) @@ -1289,9 +1494,9 @@ struct DeHomeScreen: View { ("3", "de_year3", "de_y3_sub", .deMap(3)), ("4", "de_year4", "de_y4_sub", .deMap(4)), ] - MacFillGrid(count: rows.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: rows.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: rows[i].3) { - MacLinkCard(title: vm.tr(rows[i].1), subtitle: vm.tr(rows[i].2), badge: rows[i].0) + MacLinkCard(title: vm.tr(rows[i].1), subtitle: vm.tr(rows[i].2), badge: rows[i].0, progress: routeProgress(vm, rows[i].3)) } .buttonStyle(PressScaleStyle()) } @@ -1300,11 +1505,11 @@ struct DeHomeScreen: View { #else List { NavigationLink(value: Route.roadmap) { - ListRowLabel(title: vm.tr("de_book_title"), subtitle: vm.tr("de_book_sub")) + ListRowLabel(title: vm.tr("de_book_title"), subtitle: vm.tr("de_book_sub"), progress: routeProgress(vm, Route.roadmap)) } ForEach(1...4, id: \.self) { year in NavigationLink(value: Route.deMap(year)) { - ListRowLabel(title: vm.tr("de_year\(year)"), subtitle: vm.tr("de_y\(year)_sub")) + ListRowLabel(title: vm.tr("de_year\(year)"), subtitle: vm.tr("de_y\(year)_sub"), progress: routeProgress(vm, Route.deMap(year))) } } } @@ -1325,9 +1530,9 @@ struct EnYearsScreen: View { ("en_year3", "en_y3_sub", .enMap(3)), ("en_year4", "en_y4_sub", .enMap(4)), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1337,7 +1542,7 @@ struct EnYearsScreen: View { List { ForEach(1...4, id: \.self) { year in NavigationLink(value: Route.enMap(year)) { - ListRowLabel(title: vm.tr("en_year\(year)"), subtitle: vm.tr("en_y\(year)_sub")) + ListRowLabel(title: vm.tr("en_year\(year)"), subtitle: vm.tr("en_y\(year)_sub"), progress: routeProgress(vm, Route.enMap(year))) } } } @@ -1359,9 +1564,9 @@ struct OnYearsScreen: View { ("on_year3", "on3_sub", .on3Map), ("on_year4", "on4_sub", .on4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1370,16 +1575,16 @@ struct OnYearsScreen: View { #else List { NavigationLink(value: Route.onMap) { - ListRowLabel(title: vm.tr("on_year1"), subtitle: vm.tr("on_sub")) + ListRowLabel(title: vm.tr("on_year1"), subtitle: vm.tr("on_sub"), progress: routeProgress(vm, Route.onMap)) } NavigationLink(value: Route.on2Map) { - ListRowLabel(title: vm.tr("on_year2"), subtitle: vm.tr("on2_sub")) + ListRowLabel(title: vm.tr("on_year2"), subtitle: vm.tr("on2_sub"), progress: routeProgress(vm, Route.on2Map)) } NavigationLink(value: Route.on3Map) { - ListRowLabel(title: vm.tr("on_year3"), subtitle: vm.tr("on3_sub")) + ListRowLabel(title: vm.tr("on_year3"), subtitle: vm.tr("on3_sub"), progress: routeProgress(vm, Route.on3Map)) } NavigationLink(value: Route.on4Map) { - ListRowLabel(title: vm.tr("on_year4"), subtitle: vm.tr("on4_sub")) + ListRowLabel(title: vm.tr("on_year4"), subtitle: vm.tr("on4_sub"), progress: routeProgress(vm, Route.on4Map)) } } .navigationTitle(vm.tr("Občanská nauka")) @@ -1525,12 +1730,15 @@ struct SlidesScreen: View { .padding(.horizontal) .padding(.top, 8) .padding(.bottom, 4) + #if os(macOS) + .macPage(MacChrome.actionWidth) + #endif } } private func slidePage(_ slide: J) -> some View { ScrollView { - slideStack(slide) + SlideBody(slide: slide) .padding(.horizontal, slideInset) .padding(.top, 22) .padding(.bottom, 28) @@ -1546,6 +1754,15 @@ struct SlidesScreen: View { 16 #endif } +} + +/// One explanation slide: label, title, tip and numbered points. +struct SlideBody: View { + let slide: J + + var body: some View { + slideStack(slide) + } private func slideStack(_ slide: J) -> some View { VStack(alignment: .leading, spacing: 18) { @@ -1674,9 +1891,9 @@ struct LitYearsScreen: View { ("lit_year3", "lit3_sub", .lit3Map), ("lit_year4", "lit4_sub", .lit4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1685,16 +1902,16 @@ struct LitYearsScreen: View { #else List { NavigationLink(value: Route.litMap) { - ListRowLabel(title: vm.tr("lit_year1"), subtitle: vm.tr("lit_sub")) + ListRowLabel(title: vm.tr("lit_year1"), subtitle: vm.tr("lit_sub"), progress: routeProgress(vm, Route.litMap)) } NavigationLink(value: Route.lit2Map) { - ListRowLabel(title: vm.tr("lit_year2"), subtitle: vm.tr("lit2_sub")) + ListRowLabel(title: vm.tr("lit_year2"), subtitle: vm.tr("lit2_sub"), progress: routeProgress(vm, Route.lit2Map)) } NavigationLink(value: Route.lit3Map) { - ListRowLabel(title: vm.tr("lit_year3"), subtitle: vm.tr("lit3_sub")) + ListRowLabel(title: vm.tr("lit_year3"), subtitle: vm.tr("lit3_sub"), progress: routeProgress(vm, Route.lit3Map)) } NavigationLink(value: Route.lit4Map) { - ListRowLabel(title: vm.tr("lit_year4"), subtitle: vm.tr("lit4_sub")) + ListRowLabel(title: vm.tr("lit_year4"), subtitle: vm.tr("lit4_sub"), progress: routeProgress(vm, Route.lit4Map)) } } .navigationTitle(vm.tr("Literatura")) @@ -1716,9 +1933,9 @@ struct MatYearsScreen: View { ("mat_year3", "mat3_sub", .mat3Map, "3."), ("mat_year4", "mat4_sub", .mat4Map, "4."), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: years[i].3) + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: years[i].3, progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1727,19 +1944,19 @@ struct MatYearsScreen: View { #else List { NavigationLink(value: Route.mat0Map) { - ListRowLabel(title: vm.tr("mat0_year"), subtitle: vm.tr("mat0_sub")) + ListRowLabel(title: vm.tr("mat0_year"), subtitle: vm.tr("mat0_sub"), progress: routeProgress(vm, Route.mat0Map)) } NavigationLink(value: Route.matMap) { - ListRowLabel(title: vm.tr("mat_year1"), subtitle: vm.tr("mat_sub")) + ListRowLabel(title: vm.tr("mat_year1"), subtitle: vm.tr("mat_sub"), progress: routeProgress(vm, Route.matMap)) } NavigationLink(value: Route.mat2Map) { - ListRowLabel(title: vm.tr("mat_year2"), subtitle: vm.tr("mat2_sub")) + ListRowLabel(title: vm.tr("mat_year2"), subtitle: vm.tr("mat2_sub"), progress: routeProgress(vm, Route.mat2Map)) } NavigationLink(value: Route.mat3Map) { - ListRowLabel(title: vm.tr("mat_year3"), subtitle: vm.tr("mat3_sub")) + ListRowLabel(title: vm.tr("mat_year3"), subtitle: vm.tr("mat3_sub"), progress: routeProgress(vm, Route.mat3Map)) } NavigationLink(value: Route.mat4Map) { - ListRowLabel(title: vm.tr("mat_year4"), subtitle: vm.tr("mat4_sub")) + ListRowLabel(title: vm.tr("mat_year4"), subtitle: vm.tr("mat4_sub"), progress: routeProgress(vm, Route.mat4Map)) } } .navigationTitle(vm.tr("Matematika")) @@ -1749,6 +1966,170 @@ struct MatYearsScreen: View { } } +struct CsYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("cs_year1", "cs_sub", .csMap), + ("cs_year2", "cs2_sub", .cs2Map), + ("cs_year3", "cs3_sub", .cs3Map), + ("cs_year4", "cs4_sub", .cs4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Programování")) + .navigationSubtitle(vm.tr("cs_years_sub")) + #else + List { + NavigationLink(value: Route.csMap) { + ListRowLabel(title: vm.tr("cs_year1"), subtitle: vm.tr("cs_sub"), progress: routeProgress(vm, Route.csMap)) + } + NavigationLink(value: Route.cs2Map) { + ListRowLabel(title: vm.tr("cs_year2"), subtitle: vm.tr("cs2_sub"), progress: routeProgress(vm, Route.cs2Map)) + } + NavigationLink(value: Route.cs3Map) { + ListRowLabel(title: vm.tr("cs_year3"), subtitle: vm.tr("cs3_sub"), progress: routeProgress(vm, Route.cs3Map)) + } + NavigationLink(value: Route.cs4Map) { + ListRowLabel(title: vm.tr("cs_year4"), subtitle: vm.tr("cs4_sub"), progress: routeProgress(vm, Route.cs4Map)) + } + } + .navigationTitle(vm.tr("Programování")) + .navigationSubtitle(vm.tr("cs_years_sub")) + .appListStyle() + #endif + } +} + +struct SwYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("sw_year1", "sw_sub", .swMap), + ("sw_year2", "sw2_sub", .sw2Map), + ("sw_year3", "sw3_sub", .sw3Map), + ("sw_year4", "sw4_sub", .sw4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Programové vybavení")) + .navigationSubtitle(vm.tr("sw_years_sub")) + #else + List { + NavigationLink(value: Route.swMap) { + ListRowLabel(title: vm.tr("sw_year1"), subtitle: vm.tr("sw_sub"), progress: routeProgress(vm, Route.swMap)) + } + NavigationLink(value: Route.sw2Map) { + ListRowLabel(title: vm.tr("sw_year2"), subtitle: vm.tr("sw2_sub"), progress: routeProgress(vm, Route.sw2Map)) + } + NavigationLink(value: Route.sw3Map) { + ListRowLabel(title: vm.tr("sw_year3"), subtitle: vm.tr("sw3_sub"), progress: routeProgress(vm, Route.sw3Map)) + } + NavigationLink(value: Route.sw4Map) { + ListRowLabel(title: vm.tr("sw_year4"), subtitle: vm.tr("sw4_sub"), progress: routeProgress(vm, Route.sw4Map)) + } + } + .navigationTitle(vm.tr("Programové vybavení")) + .navigationSubtitle(vm.tr("sw_years_sub")) + .appListStyle() + #endif + } +} + +struct PgYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("pg_year1", "pg_sub", .pgMap), + ("pg_year2", "pg2_sub", .pg2Map), + ("pg_year3", "pg3_sub", .pg3Map), + ("pg_year4", "pg4_sub", .pg4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Prezentační grafika")) + .navigationSubtitle(vm.tr("pg_years_sub")) + #else + List { + NavigationLink(value: Route.pgMap) { + ListRowLabel(title: vm.tr("pg_year1"), subtitle: vm.tr("pg_sub"), progress: routeProgress(vm, Route.pgMap)) + } + NavigationLink(value: Route.pg2Map) { + ListRowLabel(title: vm.tr("pg_year2"), subtitle: vm.tr("pg2_sub"), progress: routeProgress(vm, Route.pg2Map)) + } + NavigationLink(value: Route.pg3Map) { + ListRowLabel(title: vm.tr("pg_year3"), subtitle: vm.tr("pg3_sub"), progress: routeProgress(vm, Route.pg3Map)) + } + NavigationLink(value: Route.pg4Map) { + ListRowLabel(title: vm.tr("pg_year4"), subtitle: vm.tr("pg4_sub"), progress: routeProgress(vm, Route.pg4Map)) + } + } + .navigationTitle(vm.tr("Prezentační grafika")) + .navigationSubtitle(vm.tr("pg_years_sub")) + .appListStyle() + #endif + } +} + +struct TgYearsScreen: View { + @ObservedObject var vm: AppModel + + var body: some View { + #if os(macOS) + let years: [(String, String, Route)] = [ + ("tg_year1", "tg_sub", .tgMap), + ("tg_year2", "tg2_sub", .tg2Map), + ("tg_year3", "tg3_sub", .tg3Map), + ("tg_year4", "tg4_sub", .tg4Map), + ] + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in + NavigationLink(value: years[i].2) { + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) + } + .buttonStyle(PressScaleStyle()) + } + .navigationTitle(vm.tr("Technická grafika")) + .navigationSubtitle(vm.tr("tg_years_sub")) + #else + List { + NavigationLink(value: Route.tgMap) { + ListRowLabel(title: vm.tr("tg_year1"), subtitle: vm.tr("tg_sub"), progress: routeProgress(vm, Route.tgMap)) + } + NavigationLink(value: Route.tg2Map) { + ListRowLabel(title: vm.tr("tg_year2"), subtitle: vm.tr("tg2_sub"), progress: routeProgress(vm, Route.tg2Map)) + } + NavigationLink(value: Route.tg3Map) { + ListRowLabel(title: vm.tr("tg_year3"), subtitle: vm.tr("tg3_sub"), progress: routeProgress(vm, Route.tg3Map)) + } + NavigationLink(value: Route.tg4Map) { + ListRowLabel(title: vm.tr("tg_year4"), subtitle: vm.tr("tg4_sub"), progress: routeProgress(vm, Route.tg4Map)) + } + } + .navigationTitle(vm.tr("Technická grafika")) + .navigationSubtitle(vm.tr("tg_years_sub")) + .appListStyle() + #endif + } +} + struct FyzYearsScreen: View { @ObservedObject var vm: AppModel @@ -1760,9 +2141,9 @@ struct FyzYearsScreen: View { ("fyz_year3", "fyz3_sub", .fyz3Map), ("fyz_year4", "fyz4_sub", .fyz4Map), ] - MacFillGrid(count: years.count, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: years.count, minWidth: 260, minHeight: 200) { i in NavigationLink(value: years[i].2) { - MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).") + MacLinkCard(title: vm.tr(years[i].0), subtitle: vm.tr(years[i].1), badge: "\(i + 1).", progress: routeProgress(vm, years[i].2)) } .buttonStyle(PressScaleStyle()) } @@ -1771,16 +2152,16 @@ struct FyzYearsScreen: View { #else List { NavigationLink(value: Route.fyzMap) { - ListRowLabel(title: vm.tr("fyz_year1"), subtitle: vm.tr("fyz_sub")) + ListRowLabel(title: vm.tr("fyz_year1"), subtitle: vm.tr("fyz_sub"), progress: routeProgress(vm, Route.fyzMap)) } NavigationLink(value: Route.fyz2Map) { - ListRowLabel(title: vm.tr("fyz_year2"), subtitle: vm.tr("fyz2_sub")) + ListRowLabel(title: vm.tr("fyz_year2"), subtitle: vm.tr("fyz2_sub"), progress: routeProgress(vm, Route.fyz2Map)) } NavigationLink(value: Route.fyz3Map) { - ListRowLabel(title: vm.tr("fyz_year3"), subtitle: vm.tr("fyz3_sub")) + ListRowLabel(title: vm.tr("fyz_year3"), subtitle: vm.tr("fyz3_sub"), progress: routeProgress(vm, Route.fyz3Map)) } NavigationLink(value: Route.fyz4Map) { - ListRowLabel(title: vm.tr("fyz_year4"), subtitle: vm.tr("fyz4_sub")) + ListRowLabel(title: vm.tr("fyz_year4"), subtitle: vm.tr("fyz4_sub"), progress: routeProgress(vm, Route.fyz4Map)) } } .navigationTitle(vm.tr("Fyzika")) @@ -1795,15 +2176,15 @@ struct SciMapScreen: View { var body: some View { #if os(macOS) - MacFillGrid(count: 2, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: 2, minWidth: 260, minHeight: 200) { i in if i == 0 { NavigationLink(value: Route.chemMap) { - MacLinkCard(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub"), systemImage: "flask.fill") + MacLinkCard(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub"), systemImage: "flask.fill", progress: routeProgress(vm, .chemMap)) } .buttonStyle(PressScaleStyle()) } else { NavigationLink(value: Route.bioMap) { - MacLinkCard(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub"), systemImage: "leaf.fill") + MacLinkCard(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub"), systemImage: "leaf.fill", progress: routeProgress(vm, .bioMap)) } .buttonStyle(PressScaleStyle()) } @@ -1814,14 +2195,14 @@ struct SciMapScreen: View { List { NavigationLink(value: Route.chemMap) { Label { - ListRowLabel(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub")) + ListRowLabel(title: vm.tr("Chemie"), subtitle: vm.tr("chem_sub"), progress: routeProgress(vm, .chemMap)) } icon: { Image(systemName: "flask.fill") } } NavigationLink(value: Route.bioMap) { Label { - ListRowLabel(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub")) + ListRowLabel(title: vm.tr("Biologie"), subtitle: vm.tr("bio_sub"), progress: routeProgress(vm, .bioMap)) } icon: { Image(systemName: "leaf.fill") } @@ -1839,21 +2220,21 @@ struct CzechMapScreen: View { var body: some View { #if os(macOS) - MacFillGrid(count: 3, minWidth: 260, minHeight: 180) { i in + MacFillGrid(count: 3, minWidth: 260, minHeight: 200) { i in switch i { case 1: NavigationLink(value: Route.mluvnice) { - MacLinkCard(title: vm.tr("Mluvnice"), systemImage: "textformat") + MacLinkCard(title: vm.tr("Mluvnice"), subtitle: vm.tr("mluv_sub"), systemImage: "textformat", progress: routeProgress(vm, .mluvnice)) } .buttonStyle(PressScaleStyle()) case 2: NavigationLink(value: Route.readingList) { - MacLinkCard(title: vm.tr("Maturitní četba"), systemImage: "books.vertical") + MacLinkCard(title: vm.tr("Maturitní četba"), subtitle: vm.tr("reading_sub"), systemImage: "books.vertical") } .buttonStyle(PressScaleStyle()) default: NavigationLink(value: Route.litYears) { - MacLinkCard(title: vm.tr("Literatura"), systemImage: "book.closed") + MacLinkCard(title: vm.tr("Literatura"), subtitle: vm.tr("lit_years_sub"), systemImage: "book.closed", progress: routeProgress(vm, .litYears)) } .buttonStyle(PressScaleStyle()) } @@ -1864,21 +2245,21 @@ struct CzechMapScreen: View { List { NavigationLink(value: Route.litYears) { Label { - Text(vm.tr("Literatura")) + ListRowLabel(title: vm.tr("Literatura"), subtitle: vm.tr("lit_years_sub"), progress: routeProgress(vm, .litYears)) } icon: { Image(systemName: "book.closed") } } NavigationLink(value: Route.mluvnice) { Label { - Text(vm.tr("Mluvnice")) + ListRowLabel(title: vm.tr("Mluvnice"), subtitle: vm.tr("mluv_sub"), progress: routeProgress(vm, .mluvnice)) } icon: { Image(systemName: "textformat") } } NavigationLink(value: Route.readingList) { Label { - Text(vm.tr("Maturitní četba")) + ListRowLabel(title: vm.tr("Maturitní četba"), subtitle: vm.tr("reading_sub")) } icon: { Image(systemName: "books.vertical") } @@ -2808,6 +3189,262 @@ struct RouteDestination: View { } case .fyz4Ex(let id): FyzQuizScreen(vm: vm, year: 4, id: id) + case .csYears: + CsYearsScreen(vm: vm) + case .csMap: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year1"), + subtitle: vm.tr("cs_sub"), + rows: vm.content.cs.map { l in + let id = l.int("id") + return LessonRow(id: "c\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.csDone(id), destination: .csLesson(id)) + } + ) + case .csLesson(let id): + if let l = vm.content.csLesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs(id) }) + } + case .cs2Map: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year2"), + subtitle: vm.tr("cs2_sub"), + rows: vm.content.cs2.map { l in + let id = l.int("id") + return LessonRow(id: "c2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs2Done(id), destination: .cs2Lesson(id)) + } + ) + case .cs2Lesson(let id): + if let l = vm.content.cs2Lesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs2(id) }) + } + case .cs3Map: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year3"), + subtitle: vm.tr("cs3_sub"), + rows: vm.content.cs3.map { l in + let id = l.int("id") + return LessonRow(id: "c3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs3Done(id), destination: .cs3Lesson(id)) + } + ) + case .cs3Lesson(let id): + if let l = vm.content.cs3Lesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs3(id) }) + } + case .cs4Map: + LessonListScreen( + vm: vm, + title: vm.tr("cs_year4"), + subtitle: vm.tr("cs4_sub"), + rows: vm.content.cs4.map { l in + let id = l.int("id") + return LessonRow(id: "c4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.cs4Done(id), destination: .cs4Lesson(id)) + } + ) + case .cs4Lesson(let id): + if let l = vm.content.cs4Lesson(id) { + CodeLessonScreen(vm: vm, lesson: l, onSuccess: { vm.markCs4(id) }) + } + case .swYears: + SwYearsScreen(vm: vm) + case .swMap: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year1"), + subtitle: vm.tr("sw_sub"), + rows: vm.content.sw.map { l in + let id = l.int("id") + return LessonRow(id: "s\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.swDone(id), destination: .swLesson(id)) + } + ) + case .swLesson(let id): + if let l = vm.content.swLesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .swEx(id)) + } + case .swEx(let id): + SwQuizScreen(vm: vm, year: 1, id: id) + case .sw2Map: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year2"), + subtitle: vm.tr("sw2_sub"), + rows: vm.content.sw2.map { l in + let id = l.int("id") + return LessonRow(id: "s2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.sw2Done(id), destination: .sw2Lesson(id)) + } + ) + case .sw2Lesson(let id): + if let l = vm.content.sw2Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .sw2Ex(id)) + } + case .sw2Ex(let id): + SwQuizScreen(vm: vm, year: 2, id: id) + case .sw3Map: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year3"), + subtitle: vm.tr("sw3_sub"), + rows: vm.content.sw3.map { l in + let id = l.int("id") + return LessonRow(id: "s3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.sw3Done(id), destination: .sw3Lesson(id)) + } + ) + case .sw3Lesson(let id): + if let l = vm.content.sw3Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .sw3Ex(id)) + } + case .sw3Ex(let id): + SwQuizScreen(vm: vm, year: 3, id: id) + case .sw4Map: + LessonListScreen( + vm: vm, + title: vm.tr("sw_year4"), + subtitle: vm.tr("sw4_sub"), + rows: vm.content.sw4.map { l in + let id = l.int("id") + return LessonRow(id: "s4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.sw4Done(id), destination: .sw4Lesson(id)) + } + ) + case .sw4Lesson(let id): + if let l = vm.content.sw4Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .sw4Ex(id)) + } + case .sw4Ex(let id): + SwQuizScreen(vm: vm, year: 4, id: id) + case .pgYears: + PgYearsScreen(vm: vm) + case .pgMap: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year1"), + subtitle: vm.tr("pg_sub"), + rows: vm.content.pg.map { l in + let id = l.int("id") + return LessonRow(id: "p\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pgDone(id), destination: .pgLesson(id)) + } + ) + case .pgLesson(let id): + if let l = vm.content.pgLesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pgEx(id)) + } + case .pgEx(let id): + PgQuizScreen(vm: vm, year: 1, id: id) + case .pg2Map: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year2"), + subtitle: vm.tr("pg2_sub"), + rows: vm.content.pg2.map { l in + let id = l.int("id") + return LessonRow(id: "p2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pg2Done(id), destination: .pg2Lesson(id)) + } + ) + case .pg2Lesson(let id): + if let l = vm.content.pg2Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pg2Ex(id)) + } + case .pg2Ex(let id): + PgQuizScreen(vm: vm, year: 2, id: id) + case .pg3Map: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year3"), + subtitle: vm.tr("pg3_sub"), + rows: vm.content.pg3.map { l in + let id = l.int("id") + return LessonRow(id: "p3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pg3Done(id), destination: .pg3Lesson(id)) + } + ) + case .pg3Lesson(let id): + if let l = vm.content.pg3Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pg3Ex(id)) + } + case .pg3Ex(let id): + PgQuizScreen(vm: vm, year: 3, id: id) + case .pg4Map: + LessonListScreen( + vm: vm, + title: vm.tr("pg_year4"), + subtitle: vm.tr("pg4_sub"), + rows: vm.content.pg4.map { l in + let id = l.int("id") + return LessonRow(id: "p4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.pg4Done(id), destination: .pg4Lesson(id)) + } + ) + case .pg4Lesson(let id): + if let l = vm.content.pg4Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .pg4Ex(id)) + } + case .pg4Ex(let id): + PgQuizScreen(vm: vm, year: 4, id: id) + case .tgYears: + TgYearsScreen(vm: vm) + case .tgMap: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year1"), + subtitle: vm.tr("tg_sub"), + rows: vm.content.tg.map { l in + let id = l.int("id") + return LessonRow(id: "t\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tgDone(id), destination: .tgLesson(id)) + } + ) + case .tgLesson(let id): + if let l = vm.content.tgLesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tgEx(id)) + } + case .tgEx(let id): + TgQuizScreen(vm: vm, year: 1, id: id) + case .tg2Map: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year2"), + subtitle: vm.tr("tg2_sub"), + rows: vm.content.tg2.map { l in + let id = l.int("id") + return LessonRow(id: "t2\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tg2Done(id), destination: .tg2Lesson(id)) + } + ) + case .tg2Lesson(let id): + if let l = vm.content.tg2Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tg2Ex(id)) + } + case .tg2Ex(let id): + TgQuizScreen(vm: vm, year: 2, id: id) + case .tg3Map: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year3"), + subtitle: vm.tr("tg3_sub"), + rows: vm.content.tg3.map { l in + let id = l.int("id") + return LessonRow(id: "t3\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tg3Done(id), destination: .tg3Lesson(id)) + } + ) + case .tg3Lesson(let id): + if let l = vm.content.tg3Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tg3Ex(id)) + } + case .tg3Ex(let id): + TgQuizScreen(vm: vm, year: 3, id: id) + case .tg4Map: + LessonListScreen( + vm: vm, + title: vm.tr("tg_year4"), + subtitle: vm.tr("tg4_sub"), + rows: vm.content.tg4.map { l in + let id = l.int("id") + return LessonRow(id: "t4\(id)", title: vm.tr(l.str("titleKey")), done: vm.progress.tg4Done(id), destination: .tg4Lesson(id)) + } + ) + case .tg4Lesson(let id): + if let l = vm.content.tg4Lesson(id) { + SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .tg4Ex(id)) + } + case .tg4Ex(let id): + TgQuizScreen(vm: vm, year: 4, id: id) case .mluvnice: LessonListScreen( vm: vm, @@ -2836,3 +3473,446 @@ struct RouteDestination: View { } } } + +struct CodeLessonScreen: View { + @ObservedObject var vm: AppModel + let lesson: J + let onSuccess: () -> Void + @State private var code = "" + @State private var stdin = "" + @State private var output = "" + @State private var status = "" + + private var running: Bool { status == "running" } + private var needsInput: Bool { code.contains("ReadLine") } + + var body: some View { + GeometryReader { geo in + if geo.size.width >= 900 { + HStack(alignment: .top, spacing: 28) { + ScrollView { + explanation + .padding(.vertical, 4) + } + .frame(width: min(560, geo.size.width * 0.4)) + workspace(height: geo.size.height) + } + .padding(.horizontal, 28) + .padding(.vertical, 20) + .frame(maxWidth: 1640) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + explanation + editorCard.frame(height: 320) + if needsInput { inputField } + outputCard.frame(height: 220) + } + .padding(16) + } + } + } + .detailScreen(vm.tr(lesson.str("titleKey")), subtitle: vm.tr(lesson.str("subKey"))) + .onAppear { + if code.isEmpty { code = lesson.str("code") } + } + } + + private var explanation: some View { + VStack(alignment: .leading, spacing: 34) { + ForEach(Array(lesson.arr("slides").enumerated()), id: \.offset) { _, slide in + SlideBody(slide: slide) + } + } + } + + private func workspace(height: CGFloat) -> some View { + VStack(spacing: 16) { + editorCard + .frame(maxHeight: .infinity) + if needsInput { inputField } + outputCard + .frame(height: max(180, height * 0.32)) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + private var editorCard: some View { + VStack(spacing: 0) { + HStack(spacing: 10) { + Image(systemName: "chevron.left.forwardslash.chevron.right") + .foregroundStyle(Color.accentColor) + Text("Program.cs") + .font(.headline.monospaced()) + Spacer(minLength: 8) + Button { + code = lesson.str("code") + output = "" + status = "" + } label: { + Label(vm.tr("cs_reset"), systemImage: "arrow.counterclockwise") + } + .buttonStyle(.bordered) + .disabled(running) + Button { run() } label: { + Label(vm.tr("cs_run"), systemImage: "play.fill") + .padding(.horizontal, 4) + } + .buttonStyle(.borderedProminent) + .keyboardShortcut(.return, modifiers: .command) + .help("⌘↩") + .disabled(running) + } + #if os(macOS) + .controlSize(.large) + #endif + .padding(.horizontal, 14) + .padding(.vertical, 10) + Divider().opacity(0.5) + editorField + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + .background(codeFill, in: RoundedRectangle(cornerRadius: 16, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 16, style: .continuous) + .strokeBorder(.separator.opacity(0.6), lineWidth: 0.5) + } + } + + @ViewBuilder + private var editorField: some View { + #if os(macOS) + CodeEditor(text: $code) + #else + TextEditor(text: $code) + .font(.system(size: 15, design: .monospaced)) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .scrollContentBackground(.hidden) + .padding(8) + #endif + } + + private var inputField: some View { + VStack(alignment: .leading, spacing: 6) { + Label(vm.tr("cs_stdin"), systemImage: "keyboard") + .font(.subheadline) + .foregroundStyle(.secondary) + TextField("", text: $stdin, axis: .vertical) + .font(.system(size: 15, design: .monospaced)) + .lineLimit(1...4) + .textFieldStyle(.roundedBorder) + } + } + + private var outputCard: some View { + VStack(spacing: 0) { + HStack(spacing: 10) { + Image(systemName: "terminal") + .foregroundStyle(.secondary) + Text(vm.tr("cs_output")) + .font(.headline) + Spacer(minLength: 8) + statusBadge + } + .padding(.horizontal, 14) + .frame(height: 44) + Divider().opacity(0.5) + ScrollView { + Text(output.isEmpty ? vm.tr("cs_empty") : output) + .font(.system(size: 15, design: .monospaced)) + .foregroundStyle(output.isEmpty || running ? .secondary : .primary) + .frame(maxWidth: .infinity, alignment: .topLeading) + .textSelection(.enabled) + .padding(14) + } + } + .background(codeFill, in: RoundedRectangle(cornerRadius: 16, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 16, style: .continuous) + .strokeBorder(.separator.opacity(0.6), lineWidth: 0.5) + } + } + + @ViewBuilder + private var statusBadge: some View { + switch status { + case "running": + ProgressView().controlSize(.small) + case "ok": + Label(vm.tr("cs_done"), systemImage: "checkmark.circle.fill") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(vm.palette.success) + case "fail", "timeout": + Label(vm.tr("cs_failed"), systemImage: "xmark.octagon.fill") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(vm.palette.error) + default: + EmptyView() + } + } + + private var codeFill: AnyShapeStyle { + #if os(macOS) + AnyShapeStyle(.background.secondary) + #else + AnyShapeStyle(Color(.secondarySystemBackground)) + #endif + } + + private func run() { + guard !running else { return } + status = "running" + output = vm.tr("cs_running") + CSharpRunner.run(code, stdin: needsInput ? stdin : "") { result, text in + status = result + switch result { + case "unavailable": + output = vm.tr("cs_unavailable") + case "timeout": + output = (text + "\n\n" + vm.tr("cs_timeout")).trimmingCharacters(in: .whitespacesAndNewlines) + default: + output = text.isEmpty ? vm.tr("cs_empty") : text + } + if result == "ok" { onSuccess() } + } + } +} + +#if os(macOS) +/// Plain-text editor for C#: monospaced, coloured, and without smart quotes or +/// autocorrect, which would turn "text" into “text” and break the program. +struct CodeEditor: NSViewRepresentable { + @Binding var text: String + + func makeCoordinator() -> Coordinator { Coordinator(text: $text) } + + func makeNSView(context: Context) -> NSScrollView { + let scroll = NSTextView.scrollableTextView() + scroll.drawsBackground = false + scroll.autohidesScrollers = true + guard let view = scroll.documentView as? NSTextView else { return scroll } + view.delegate = context.coordinator + view.isRichText = false + view.allowsUndo = true + view.isAutomaticQuoteSubstitutionEnabled = false + view.isAutomaticDashSubstitutionEnabled = false + view.isAutomaticTextReplacementEnabled = false + view.isAutomaticSpellingCorrectionEnabled = false + view.isContinuousSpellCheckingEnabled = false + view.isGrammarCheckingEnabled = false + view.isAutomaticLinkDetectionEnabled = false + view.smartInsertDeleteEnabled = false + view.drawsBackground = false + view.font = CSharpStyle.font + view.textContainerInset = NSSize(width: 10, height: 12) + view.typingAttributes = CSharpStyle.base + view.string = text + CSharpStyle.highlight(view.textStorage) + return scroll + } + + func updateNSView(_ scroll: NSScrollView, context: Context) { + context.coordinator.text = $text + guard let view = scroll.documentView as? NSTextView, view.string != text else { return } + view.string = text + CSharpStyle.highlight(view.textStorage) + } + + final class Coordinator: NSObject, NSTextViewDelegate { + var text: Binding + + init(text: Binding) { + self.text = text + } + + func textDidChange(_ notification: Notification) { + guard let view = notification.object as? NSTextView else { return } + text.wrappedValue = view.string + CSharpStyle.highlight(view.textStorage) + view.typingAttributes = CSharpStyle.base + } + } +} + +enum CSharpStyle { + static let font = NSFont.monospacedSystemFont(ofSize: 15, weight: .regular) + static var base: [NSAttributedString.Key: Any] { [.font: font, .foregroundColor: NSColor.labelColor] } + + private static func adaptive(_ light: Int, _ dark: Int) -> NSColor { + func color(_ hex: Int) -> NSColor { + NSColor( + srgbRed: CGFloat((hex >> 16) & 0xFF) / 255, + green: CGFloat((hex >> 8) & 0xFF) / 255, + blue: CGFloat(hex & 0xFF) / 255, + alpha: 1 + ) + } + return NSColor(name: nil) { appearance in + appearance.bestMatch(from: [.aqua, .darkAqua]) == .darkAqua ? color(dark) : color(light) + } + } + + private static let keywords = [ + "abstract", "as", "async", "await", "base", "bool", "break", "case", "catch", "char", "class", + "const", "continue", "decimal", "default", "do", "double", "else", "enum", "false", "finally", + "float", "for", "foreach", "get", "if", "in", "int", "interface", "internal", "is", "long", + "namespace", "new", "null", "object", "out", "override", "private", "protected", "public", + "readonly", "record", "ref", "return", "sealed", "set", "static", "string", "struct", "switch", + "this", "throw", "true", "try", "using", "var", "virtual", "void", "while", + ] + + private static let rules: [(NSRegularExpression, NSColor)] = { + func re(_ pattern: String) -> NSRegularExpression { + try! NSRegularExpression(pattern: pattern) + } + return [ + (re("\\b(?:" + keywords.joined(separator: "|") + ")\\b"), adaptive(0x9B2393, 0xFF7AB2)), + (re("\\b(?:Console|Math|List|Dictionary|File|Task|Exception|Func|Action|String|DateTime|Random)\\b"), adaptive(0x0B4F79, 0x5DD8FF)), + (re("\\b\\d+(?:\\.\\d+)?\\b"), adaptive(0x1C00CF, 0xD9C97C)), + (re("\\$?@?\"(?:\\\\.|[^\"\\\\\\n])*\"|'(?:\\\\.|[^'\\\\\\n])'"), adaptive(0xC41A16, 0xFC6A5D)), + (re("//[^\\n]*|/\\*[\\s\\S]*?\\*/"), adaptive(0x5D6C79, 0x7F8C98)), + ] + }() + + static func highlight(_ storage: NSTextStorage?) { + guard let storage else { return } + let text = storage.string + let full = NSRange(location: 0, length: (text as NSString).length) + storage.beginEditing() + storage.setAttributes(base, range: full) + for (pattern, color) in rules { + pattern.enumerateMatches(in: text, range: full) { match, _, _ in + if let range = match?.range { + storage.addAttribute(.foregroundColor, value: color, range: range) + } + } + } + storage.endEditing() + } +} +#endif + +enum CSharpRunner { + static func run(_ source: String, stdin: String, done: @escaping (String, String) -> Void) { + #if os(macOS) + DispatchQueue.global(qos: .userInitiated).async { + let result = runMac(source, stdin: stdin) + DispatchQueue.main.async { done(result.0, result.1) } + } + #else + done("unavailable", "") + #endif + } + + #if os(macOS) + private static func runMac(_ source: String, stdin: String) -> (String, String) { + guard source.count <= 100_000 else { return ("fail", "Program je příliš dlouhý.") } + guard let dotnet = findDotnet() else { return ("unavailable", "") } + let dir = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent(".graduately/csharp-lab") + let csproj = dir.appendingPathComponent("Lab.csproj") + if !FileManager.default.fileExists(atPath: csproj.path) { + try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + let framework = targetFramework(dotnet) + let xml = """ + + + Exe + \(framework) + enable + disable + latest + + + """ + try? xml.write(to: csproj, atomically: true, encoding: .utf8) + let restored = command(dotnet, ["restore", dir.path], dir, "", 90) + if restored.0 != "ok" { return restored } + } + do { + try source.write(to: dir.appendingPathComponent("Program.cs"), atomically: true, encoding: .utf8) + } catch { + return ("fail", error.localizedDescription) + } + var args = ["run", "--project", dir.path, "--verbosity", "quiet"] + if FileManager.default.fileExists(atPath: dir.appendingPathComponent("obj").path) { + args.append("--no-restore") + } + return command(dotnet, args, dir, stdin, 20) + } + + private static func command(_ dotnet: String, _ args: [String], _ dir: URL, _ stdin: String, _ seconds: Double) -> (String, String) { + let proc = Process() + proc.executableURL = URL(fileURLWithPath: dotnet) + proc.arguments = args + proc.currentDirectoryURL = dir + var env = ProcessInfo.processInfo.environment + let root = (dotnet as NSString).deletingLastPathComponent + env["PATH"] = [root, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].joined(separator: ":") + env["DOTNET_ROOT"] = root + env["DOTNET_CLI_TELEMETRY_OPTOUT"] = "1" + env["DOTNET_NOLOGO"] = "1" + env["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1" + proc.environment = env + let out = Pipe() + let input = Pipe() + proc.standardOutput = out + proc.standardError = out + proc.standardInput = input + var collected = Data() + let lock = NSLock() + out.fileHandleForReading.readabilityHandler = { handle in + let chunk = handle.availableData + guard !chunk.isEmpty else { return } + lock.lock() + collected.append(chunk) + lock.unlock() + } + do { try proc.run() } catch { return ("fail", error.localizedDescription) } + if let data = stdin.data(using: .utf8) { input.fileHandleForWriting.write(data) } + try? input.fileHandleForWriting.close() + let group = DispatchGroup() + group.enter() + proc.terminationHandler = { _ in group.leave() } + if group.wait(timeout: .now() + seconds) == .timedOut { + proc.terminate() + kill(proc.processIdentifier, SIGKILL) + out.fileHandleForReading.readabilityHandler = nil + return ("timeout", clip(collected, lock)) + } + out.fileHandleForReading.readabilityHandler = nil + let rest = out.fileHandleForReading.readDataToEndOfFile() + lock.lock() + collected.append(rest) + lock.unlock() + let text = clip(collected, lock) + return proc.terminationStatus == 0 ? ("ok", text) : ("fail", text) + } + + private static func clip(_ data: Data, _ lock: NSLock) -> String { + lock.lock() + let copy = data + lock.unlock() + let text = String(data: copy, encoding: .utf8) ?? "" + let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) + return trimmed.count <= 16_000 ? trimmed : String(trimmed.prefix(16_000)) + "\n…" + } + + private static func targetFramework(_ dotnet: String) -> String { + let version = command(dotnet, ["--version"], URL(fileURLWithPath: NSTemporaryDirectory()), "", 20) + let major = version.1.split(separator: ".").first.map(String.init) ?? "" + return major.allSatisfy(\.isNumber) && !major.isEmpty ? "net\(major).0" : "net8.0" + } + + private static func findDotnet() -> String? { + let names = [ + "/usr/local/share/dotnet/dotnet", + "/opt/homebrew/share/dotnet/dotnet", + "/usr/local/bin/dotnet", + NSHomeDirectory() + "/.dotnet/dotnet", + ] + return names.first { FileManager.default.isExecutableFile(atPath: $0) } + } + #endif +} diff --git a/macos/Graduately.xcodeproj/project.pbxproj b/macos/Graduately.xcodeproj/project.pbxproj index 6baafad..42b58cc 100644 --- a/macos/Graduately.xcodeproj/project.pbxproj +++ b/macos/Graduately.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ B101000000000000000014 /* Exercises.swift in Sources */ = {isa = PBXBuildFile; fileRef = B201000000000000000014 /* Exercises.swift */; }; B101000000000000000015 /* ExercisesMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B201000000000000000015 /* ExercisesMore.swift */; }; B101000000000000000016 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B201000000000000000016 /* Assets.xcassets */; }; + B101000000000000000018 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B201000000000000000020 /* Localizable.strings */; }; B101000000000000000017 /* PathMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = B201000000000000000019 /* PathMap.swift */; }; /* End PBXBuildFile section */ @@ -46,6 +47,8 @@ B201000000000000000017 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B201000000000000000018 /* Graduately.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Graduately.entitlements; sourceTree = ""; }; B201000000000000000019 /* PathMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PathMap.swift; path = ../ios/Graduately/UI/PathMap.swift; sourceTree = SOURCE_ROOT; }; + B201000000000000000021 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + B201000000000000000022 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; B301000000000000000001 /* Graduately.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Graduately.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -79,6 +82,7 @@ B501000000000000000004 /* Update */, B501000000000000000005 /* UI */, B201000000000000000016 /* Assets.xcassets */, + B201000000000000000020 /* Localizable.strings */, B201000000000000000017 /* Info.plist */, B201000000000000000018 /* Graduately.entitlements */, ); @@ -128,6 +132,18 @@ }; /* End PBXGroup section */ +/* Begin PBXVariantGroup section */ + B201000000000000000020 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + B201000000000000000021 /* en */, + B201000000000000000022 /* cs */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin PBXNativeTarget section */ B601000000000000000001 /* Graduately */ = { isa = PBXNativeTarget; @@ -164,7 +180,7 @@ }; buildConfigurationList = B801000000000000000001 /* Build configuration list for PBXProject "Graduately" */; compatibilityVersion = "Xcode 14.0"; - developmentRegion = cs; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -187,6 +203,7 @@ buildActionMask = 2147483647; files = ( B101000000000000000016 /* Assets.xcassets in Resources */, + B101000000000000000018 /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos/Graduately/cs.lproj/Localizable.strings b/macos/Graduately/cs.lproj/Localizable.strings new file mode 100644 index 0000000..a080c4c --- /dev/null +++ b/macos/Graduately/cs.lproj/Localizable.strings @@ -0,0 +1 @@ +/* Označí češtinu jako podporovaný jazyk, aby nabídky macOS zůstaly česky na českém systému. */ diff --git a/macos/Graduately/en.lproj/Localizable.strings b/macos/Graduately/en.lproj/Localizable.strings new file mode 100644 index 0000000..3c0e8ca --- /dev/null +++ b/macos/Graduately/en.lproj/Localizable.strings @@ -0,0 +1 @@ +/* Marks English as a supported language so macOS menus follow an English system. */ diff --git a/scripts/build-content.py b/scripts/build-content.py index fe5fbcf..ca44c03 100644 --- a/scripts/build-content.py +++ b/scripts/build-content.py @@ -116,9 +116,20 @@ def parse_doc(path: Path) -> Doc: para: Node | None = None table: Node | None = None in_comment = False - for n in range(i, len(lines)): + indexes = iter(range(i, len(lines))) + for n in indexes: ln = lines[n].rstrip() no = n + 1 + if ln.startswith("```"): + para = table = None + body: list[str] = [] + for n2 in indexes: + raw_line = lines[n2] + if raw_line.rstrip().startswith("```"): + break + body.append(raw_line) + nodes.append(Node("code", "\n".join(body).strip("\n"), no)) + continue if in_comment: if "-->" in ln: in_comment = False @@ -539,6 +550,36 @@ def lesson_files(directory: Path) -> list[Path]: return files +def code_of(p: Part | None) -> str: + if p is None: + return "" + for nd in p.nodes: + if nd.kind == "code": + return nd.text + return "" + + +def code_lessons(directory: Path, prefix: str) -> list[dict]: + """Lessons with an explanation and a C# program the student can edit and run.""" + out = [] + for i, path in enumerate(lesson_files(directory), start=1): + doc, en = parse_doc(path), en_doc(path) + load_translations(en) + lid = lesson_id(doc, f"{prefix}.{i}") + code = code_of(part(doc, "Kód", "Code")) + if not code.strip(): + fail(path, None, "lekce nemá ukázkový kód v sekci # Kód") + out.append({ + "id": i, + "key": lid, + "titleKey": keyed(f"{prefix}_unit{i}", doc, en, "nazev"), + "subKey": keyed(f"{prefix}_unit{i}_sub", doc, en, "popis"), + "slides": read_slides(doc, part(doc, "Výklad")), + "code": code, + }) + return out + + def std_lessons(directory: Path, prefix: str) -> list[dict]: """Lessons with slides and a choice quiz (IT, civics, literature, sciences, physics).""" out = [] @@ -1065,6 +1106,16 @@ STD = [ ("prirodni-vedy/chemie", "chem"), ("prirodni-vedy/biologie", "bio"), ("fyzika/1-rocnik", "fyz"), ("fyzika/2-rocnik", "fyz2"), ("fyzika/3-rocnik", "fyz3"), ("fyzika/4-rocnik", "fyz4"), + ("technicka-grafika/1-rocnik", "tg"), ("technicka-grafika/2-rocnik", "tg2"), + ("technicka-grafika/3-rocnik", "tg3"), ("technicka-grafika/4-rocnik", "tg4"), + ("prezentacni-grafika/1-rocnik", "pg"), ("prezentacni-grafika/2-rocnik", "pg2"), + ("prezentacni-grafika/3-rocnik", "pg3"), ("prezentacni-grafika/4-rocnik", "pg4"), + ("programove-vybaveni/1-rocnik", "sw"), ("programove-vybaveni/2-rocnik", "sw2"), + ("programove-vybaveni/3-rocnik", "sw3"), ("programove-vybaveni/4-rocnik", "sw4"), +] +CODE = [ + ("programovani/1-rocnik", "cs"), ("programovani/2-rocnik", "cs2"), + ("programovani/3-rocnik", "cs3"), ("programovani/4-rocnik", "cs4"), ] MATH = [ ("matematika/0-opakovani", "mat0"), ("matematika/1-rocnik", "mat"), @@ -1089,6 +1140,8 @@ def build(content: Path = CONTENT) -> dict: payload[key] = std_lessons(content / d, key) for d, key in MATH: payload[key] = math_lessons(content / d, key) + for d, key in CODE: + payload[key] = code_lessons(content / d, key) for d, tag in SKILLS: for year in range(1, 5): payload[tag if year == 1 else f"{tag}{year}"] = skill_lessons(