j0h

text_w_vect

j0h
Jan 1st, 2026
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 26.45 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import sys
  3. import os
  4. import math
  5. from datetime import datetime
  6.  
  7. os.environ["QT_LOGGING_RULES"] = "*.warning=false"  # STFU Qt warnings
  8.  
  9. from PyQt5.QtWidgets import (
  10.     QApplication, QWidget, QFileDialog, QInputDialog,
  11.     QDialog, QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton,
  12.     QShortcut
  13. )
  14. from PyQt5.QtGui import QPainter, QFont, QColor, QKeySequence
  15. from PyQt5.QtCore import Qt, QPoint
  16.  
  17.  
  18. draw_string0 = "...oooOOO000OOOooo..."  # T
  19. draw_string1 = "H E L P  M Y   T E X T  E D I T O R  I S  B U S T E D"  # Y
  20. draw_string2 = "[[[]]]|||///\\\\"
  21. draw_string3 = "HACKTHEMATRIX"
  22. draw_string4 = "//"
  23. draw_string5 = "]]||"
  24. draw_string6 = "\\\\"
  25. draw_string7 = ">>><<<"
  26. draw_string8 = "-=+"
  27. draw_string9 = "    "  # Erase
  28.  
  29.  
  30. class AsciiArtWindow(QWidget):
  31.     def __init__(self, width=80, height=80):
  32.         super().__init__()
  33.         # Auto-commit vectors when the gesture ends
  34.         self.vector_commit_on_release = True
  35.  
  36.         # Ctrl+S Save
  37.         QShortcut(QKeySequence("Ctrl+S"), self, activated=self.save_file)
  38.         # Ctrl+O Open
  39.         QShortcut(QKeySequence("Ctrl+O"), self, activated=self.open_file)
  40.         # Ctrl+B Edit Brushes
  41.         QShortcut(QKeySequence("Ctrl+B"), self, activated=self.brush_editor)
  42.  
  43.         # Glyph tiles
  44.         self.glyphs = {
  45.             "Tile1": r'''
  46.      ______  ______
  47.     /     / /     /
  48.    /     / /     /
  49.   /     / /     /
  50.  /     / /     /
  51. /     / /     /
  52. /_____/ /_____/
  53. \    \ \    \
  54. \    \ \    \
  55.  \    \ \    \
  56.   \    \ \    \
  57.    \    \ \    \
  58.     \_____\ \_____\
  59. '''.lstrip("\n"),
  60.  
  61.             "Tile2": r'''
  62.  
  63.  
  64.  
  65. '''.lstrip("\n"),
  66.  
  67.             "Tile3": r'''
  68.  __,----,__
  69. .'          '.
  70. |  ________   |
  71. | [        |  |
  72. | |        |  |
  73. | |        |  |
  74. | |        |  |
  75. | |        |  |
  76. | [________|  |
  77. \____________/
  78. '''.lstrip("\n"),
  79.  
  80.             "Tile4": r'''
  81.     /_/
  82.    /_/
  83.   /_/
  84.  /_/
  85. /_/
  86. /__\
  87. '''.lstrip("\n"),
  88.  
  89.             "Tile5": r'''
  90. \_\
  91. \_\
  92.  \_\
  93.   \_\
  94.    \_\
  95.    /__\
  96. '''.lstrip("\n"),
  97.  
  98.             "Tile6": r'''
  99. |_|
  100. |_|
  101. |_|
  102. |_|
  103. |_|
  104. /___\
  105. '''.lstrip("\n"),
  106.  
  107.             "Tile7": r'''
  108. \_\
  109. \_\
  110.  \_\
  111.   \_\
  112.    \_\
  113.     /|\
  114. '''.lstrip("\n"),
  115.  
  116.             "Tile8": r'''
  117. (_)
  118. (_)
  119. (_)
  120. (_)
  121. (_)
  122. -
  123. |||
  124. '''.lstrip("\n"),
  125.  
  126.             "Tile9": r'''
  127. \|/
  128. -     __
  129. (_)   /_/
  130. (_)  /_/
  131. (_) /_/
  132. (_)/_/
  133. (_  /
  134. (_/
  135. '''.lstrip("\n"),
  136.  
  137.             "Tile10": r'''
  138. \|/
  139. -
  140. (_)
  141. (_)
  142. (_)
  143. (_)
  144. (_)
  145. '''.lstrip("\n"),
  146.  
  147.             "Tile11": r'''
  148. ///
  149. --      _
  150. \_\   /
  151. \_\ / /
  152.  \_\/ /
  153.   \__/
  154. '''.lstrip("\n"),
  155.  
  156.             "Tile12": r'''
  157.      nnn
  158.     /_/
  159.    /_/
  160.   /_/
  161.  /_/
  162. /_/
  163. /_/
  164. '''.lstrip("\n"),
  165.         }
  166.  
  167.         # Brushes
  168.         self.draw_string0 = draw_string0
  169.         self.draw_string1 = draw_string1
  170.         self.draw_string2 = draw_string2
  171.         self.draw_string3 = draw_string3
  172.         self.draw_string4 = draw_string4
  173.         self.draw_string5 = draw_string5
  174.         self.draw_string6 = draw_string6
  175.         self.draw_string7 = draw_string7
  176.         self.draw_string8 = draw_string8
  177.         self.draw_string9 = draw_string9
  178.  
  179.         self.draw_string = self.draw_string0
  180.         self.draw_index = 0
  181.  
  182.         # Canvas/grid setup
  183.         self.width_chars = width
  184.         self.height_chars = height
  185.         self.char_grid = [[' '] * width for _ in range(height)]
  186.         self.text_color = QColor("black")
  187.  
  188.         # NOTE: These are "cell" sizes. Your chosen font may not match perfectly.
  189.         self.char_width = 8
  190.         self.char_height = 24
  191.  
  192.         # Cursor for WASD movement
  193.         self.cursor_x = 0
  194.         self.cursor_y = 0
  195.  
  196.         # Vector preview (non-permanent overlay)
  197.         # Shift + Left-Drag => preview; Release => pinned preview; Enter => commit; Esc => clear
  198.         self.vector_active = False
  199.         self.vector_pinned = False
  200.         self.vector_start = None   # (x,y)
  201.         self.vector_end = None     # (x,y)
  202.         self.vector_points = []    # list[(x,y)]
  203.         self.vector_char = '_'
  204.         self.vector_len = 0
  205.  
  206.         # Window setup
  207.         self.base_title = "QtAwesome ASCII Arts!"
  208.         self.setWindowTitle(self.base_title)
  209.         self.resize(self.width_chars * self.char_width,
  210.                     self.height_chars * self.char_height)
  211.  
  212.         # Transparent background while keeping the frame
  213.         self.BG_Flag = True
  214.         self.setAttribute(Qt.WA_TranslucentBackground, self.BG_Flag)
  215.         self.setAutoFillBackground(False)
  216.  
  217.         # Show Cursor
  218.         self.Show_Cursor = False
  219.  
  220.         # Monospace font
  221.         self.font = QFont("Courier", 14)
  222.  
  223.         # Track mouse movement even without buttons (useful for cursor display)
  224.         self.setMouseTracking(True)
  225.  
  226.         # Minimum size so manual resizing doesn't collapse it
  227.         self.setMinimumSize(self.char_width * 10, self.char_height * 1)
  228.  
  229.         self.show()
  230.  
  231.     # ---------------- Title helper ----------------
  232.  
  233.     def set_base_title(self, s: str):
  234.         self.base_title = s
  235.         self.update_title()
  236.  
  237.     def update_title(self):
  238.         if self.vector_points and (self.vector_active or self.vector_pinned):
  239.             self.setWindowTitle(f"{self.base_title}  [vector len={self.vector_len} char='{self.vector_char}']")
  240.         else:
  241.             self.setWindowTitle(self.base_title)
  242.  
  243.     # ---------------- Brush editor ----------------
  244.  
  245.     def brush_editor(self):
  246.         # If already open
  247.         if hasattr(self, "brush_window") and self.brush_window.isVisible():
  248.             self.brush_window.raise_()
  249.             return
  250.  
  251.         self.brush_window = QDialog(self)
  252.         self.brush_window.setWindowTitle("Edit Brushes")
  253.         self.brush_window.setWindowFlags(Qt.Window)
  254.  
  255.         layout = QVBoxLayout()
  256.  
  257.         def make_row(label, getter, setter):
  258.             row = QHBoxLayout()
  259.             lbl = QLabel(label)
  260.             edit = QLineEdit(getter())
  261.             btn = QPushButton("Save")
  262.  
  263.             def save():
  264.                 new_value = edit.text()
  265.                 setter(new_value)
  266.                 # If this brush is currently active by value, keep drawing with the updated pattern.
  267.                 # (This is best-effort; you can refine by tracking an "active brush key" if desired.)
  268.                 if self.draw_string == getter():
  269.                     self.draw_string = getter()
  270.                 self.update_title()
  271.  
  272.             btn.clicked.connect(save)
  273.  
  274.             row.addWidget(lbl)
  275.             row.addWidget(edit)
  276.             row.addWidget(btn)
  277.             return row
  278.  
  279.         layout.addLayout(make_row("Brush T:", lambda: self.draw_string0, lambda v: setattr(self, "draw_string0", v)))
  280.         layout.addLayout(make_row("Brush Y:", lambda: self.draw_string1, lambda v: setattr(self, "draw_string1", v)))
  281.         layout.addLayout(make_row("Brush U:", lambda: self.draw_string2, lambda v: setattr(self, "draw_string2", v)))
  282.         layout.addLayout(make_row("Brush I:", lambda: self.draw_string3, lambda v: setattr(self, "draw_string3", v)))
  283.         layout.addLayout(make_row("Brush J:", lambda: self.draw_string4, lambda v: setattr(self, "draw_string4", v)))
  284.         layout.addLayout(make_row("Brush K:", lambda: self.draw_string5, lambda v: setattr(self, "draw_string5", v)))
  285.         layout.addLayout(make_row("Brush L:", lambda: self.draw_string6, lambda v: setattr(self, "draw_string6", v)))
  286.         layout.addLayout(make_row("Brush M:", lambda: self.draw_string7, lambda v: setattr(self, "draw_string7", v)))
  287.         layout.addLayout(make_row("Brush N:", lambda: self.draw_string8, lambda v: setattr(self, "draw_string8", v)))
  288.         layout.addLayout(make_row("Eraser:",  lambda: self.draw_string9, lambda v: setattr(self, "draw_string9", v)))
  289.  
  290.         self.brush_window.setLayout(layout)
  291.         self.brush_window.resize(700, 260)
  292.         self.brush_window.show()
  293.  
  294.     # ---------------- Drawing helpers ----------------
  295.  
  296.     def draw_at_cursor(self):
  297.         if 0 <= self.cursor_x < self.width_chars and 0 <= self.cursor_y < self.height_chars:
  298.             if not self.draw_string:
  299.                 return
  300.             char = self.draw_string[self.draw_index % len(self.draw_string)]
  301.             self.char_grid[self.cursor_y][self.cursor_x] = char
  302.             self.draw_index += 1
  303.             self.update()
  304.  
  305.     def draw_char(self, pos: QPoint):
  306.         x = pos.x() // self.char_width
  307.         y = pos.y() // self.char_height
  308.         if 0 <= x < self.width_chars and 0 <= y < self.height_chars:
  309.             if not self.draw_string:
  310.                 return
  311.             char = self.draw_string[self.draw_index % len(self.draw_string)]
  312.             self.char_grid[y][x] = char
  313.             self.draw_index += 1
  314.  
  315.     # ---------------- Vector preview helpers ----------------
  316.  
  317.     def grid_pos_from_pixel(self, pos: QPoint) -> tuple[int, int]:
  318.         x = pos.x() // self.char_width
  319.         y = pos.y() // self.char_height
  320.         # Clamp to bounds
  321.         x = max(0, min(self.width_chars - 1, x))
  322.         y = max(0, min(self.height_chars - 1, y))
  323.         return (x, y)
  324.  
  325.     def bresenham(self, x0: int, y0: int, x1: int, y1: int) -> list[tuple[int, int]]:
  326.         points = []
  327.         dx = abs(x1 - x0)
  328.         dy = -abs(y1 - y0)
  329.         sx = 1 if x0 < x1 else -1
  330.         sy = 1 if y0 < y1 else -1
  331.         err = dx + dy
  332.  
  333.         x, y = x0, y0
  334.         while True:
  335.             points.append((x, y))
  336.             if x == x1 and y == y1:
  337.                 break
  338.             e2 = 2 * err
  339.             if e2 >= dy:
  340.                 err += dy
  341.                 x += sx
  342.             if e2 <= dx:
  343.                 err += dx
  344.                 y += sy
  345.         return points
  346.  
  347.     def vector_char_for_angle(self, dx: int, dy: int) -> str:
  348.         """
  349.        Rules (visual orientation):
  350.          '_'  if within +/-15Β° of horizontal
  351.          '|'  if within +/-15Β° of vertical
  352.          '\'  if negative slope
  353.          '/'  if positive slope
  354.  
  355.        IMPORTANT:
  356.        Screen coords have +y downward; invert dy for math angle so that:
  357.          up-right => '/'
  358.          down-right => '\'
  359.        """
  360.         if dx == 0 and dy == 0:
  361.             return '_'
  362.  
  363.         ang = math.degrees(math.atan2(-dy, dx))  # -180..180 using inverted dy
  364.         abs_ang = abs(ang)
  365.  
  366.         # horizontal: near 0 or near 180
  367.         if abs_ang <= 15 or abs_ang >= 165:
  368.             return '_'
  369.  
  370.         # vertical: near 90
  371.         if abs(abs_ang - 90) <= 15:
  372.             return '|'
  373.  
  374.         return '\\' if ang < 0 else '/'
  375.  
  376.     def update_vector_preview(self):
  377.         if not self.vector_start or not self.vector_end:
  378.             self.vector_points = []
  379.             self.vector_len = 0
  380.             self.update_title()
  381.             return
  382.  
  383.         x0, y0 = self.vector_start
  384.         x1, y1 = self.vector_end
  385.  
  386.         dx = x1 - x0
  387.         dy = y1 - y0
  388.  
  389.         self.vector_char = self.vector_char_for_angle(dx, dy)
  390.         pts = self.bresenham(x0, y0, x1, y1)
  391.  
  392.         # Clip to bounds (should already be in bounds, but keep it safe)
  393.         clipped = []
  394.         for x, y in pts:
  395.             if 0 <= x < self.width_chars and 0 <= y < self.height_chars:
  396.                 clipped.append((x, y))
  397.  
  398.         self.vector_points = clipped
  399.         self.vector_len = len(self.vector_points)
  400.         self.update_title()
  401.  
  402.     def clear_vector_preview(self):
  403.         self.vector_active = False
  404.         self.vector_pinned = False
  405.         self.vector_start = None
  406.         self.vector_end = None
  407.         self.vector_points = []
  408.         self.vector_len = 0
  409.         self.update_title()
  410.         self.update()
  411.  
  412.     def commit_vector(self):
  413.         """Write the pinned/active vector into the grid (makes it permanent)."""
  414.         if not self.vector_points:
  415.             return
  416.         for x, y in self.vector_points:
  417.             self.char_grid[y][x] = self.vector_char
  418.         # After commit, clear overlay
  419.         self.clear_vector_preview()
  420.  
  421.     # ---------------- Mouse events ----------------
  422.  
  423.     def mouseMoveEvent(self, event):
  424.         self.cursor_x = event.x() // self.char_width
  425.         self.cursor_y = event.y() // self.char_height
  426.  
  427.         if event.buttons() & Qt.LeftButton:
  428.             if self.vector_active:
  429.                 self.vector_end = self.grid_pos_from_pixel(event.pos())
  430.                 self.update_vector_preview()
  431.             else:
  432.                 self.draw_char(event.pos())
  433.  
  434.         self.update()
  435.  
  436.     def mousePressEvent(self, event):
  437.         # Update cursor first
  438.         self.cursor_x = event.x() // self.char_width
  439.         self.cursor_y = event.y() // self.char_height
  440.  
  441.         if event.button() == Qt.LeftButton:
  442.             if event.modifiers() & Qt.ShiftModifier:
  443.                 # SHIFT + drag => vector preview
  444.                 self.vector_active = True
  445.                 self.vector_pinned = False
  446.                 p = self.grid_pos_from_pixel(event.pos())
  447.                 self.vector_start = p
  448.                 self.vector_end = p
  449.                 self.update_vector_preview()
  450.             else:
  451.                 self.draw_char(event.pos())
  452.  
  453.         self.update()
  454.        
  455.     def mouseReleaseEvent(self, event):
  456.         if event.button() == Qt.LeftButton and self.vector_active:
  457.             self.vector_active = False
  458.  
  459.             # finalize end point at release
  460.             self.vector_end = self.grid_pos_from_pixel(event.pos())
  461.             self.update_vector_preview()
  462.  
  463.             if self.vector_commit_on_release:
  464.                 self.commit_vector()          # writes into char_grid (permanent)
  465.             else:
  466.                 self.vector_pinned = True     # overlay-only behavior
  467.  
  468.         self.update()
  469.  
  470.     # ---------------- Help & file I/O ----------------
  471.  
  472.     def show_help(self):
  473.         if hasattr(self, "help_window") and self.help_window.isVisible():
  474.             self.help_window.raise_()
  475.             self.help_window.activateWindow()
  476.             return
  477.  
  478.         self.help_window = QDialog(self)
  479.         self.help_window.setWindowTitle("Key Bindings")
  480.         self.help_window.setWindowFlags(Qt.Window)
  481.  
  482.         layout = QVBoxLayout()
  483.         label = QLabel()
  484.         label.setText(
  485.             "QtAwesome ASCII Arts! - Key Bindings\n\n"
  486.             "Drawing (brush select):\n"
  487.             "  T  - Brush 0\n"
  488.             "  Y  - Brush 1\n"
  489.             "  U  - Brush 2\n"
  490.             "  I  - Brush 3\n"
  491.             "  J  - Brush 4\n"
  492.             "  K  - Brush 5\n"
  493.             "  L  - Brush 6\n"
  494.             "  M  - Brush 7\n"
  495.             "  N  - Brush 8\n"
  496.             "  E  - Eraser\n\n"
  497.             "Vector preview tool:\n"
  498.             "  Shift + Mouse Drag  - Preview vector line (non-permanent)\n"
  499.             "  release click       - Commit vector line (make permanent)\n"
  500.             "  Esc                 - Clear vector preview\n\n"
  501.             "Controls:\n"
  502.             "  Mouse Drag   - Draw characters\n"
  503.             "  WASD, Arrows - Move cursor and draw\n"
  504.             "  R            - Reset canvas\n"
  505.             "  P            - Populate entire area with text\n"
  506.             "  B            - Toggle background\n"
  507.             "  C            - Toggle Show Cursor\n"
  508.             "  Ctrl+S       - Save\n"
  509.             "  Ctrl+O       - Open\n"
  510.             "  Ctrl+B       - Edit brushes\n\n"
  511.             "Color Palette:\n"
  512.             "  1-9          - Change color\n"
  513.             "  0            - White\n\n"
  514.             "Help:\n"
  515.             "  H            - Show this help window\n"
  516.         )
  517.  
  518.         label.setStyleSheet("font-family: Courier; font-size: 16px;")
  519.         label.setTextInteractionFlags(Qt.TextSelectableByMouse)
  520.  
  521.         layout.addWidget(label)
  522.         self.help_window.setLayout(layout)
  523.         self.help_window.resize(520, 620)
  524.         self.help_window.show()
  525.  
  526.     def open_file(self):
  527.         path, _ = QFileDialog.getOpenFileName(
  528.             self,
  529.             "Open ASCII Text File",
  530.             "",
  531.             "Text Files (*.txt);;All Files (*)"
  532.         )
  533.         if not path:
  534.             return
  535.  
  536.         max_w = 500
  537.         max_h = 500
  538.  
  539.         with open(path, "r", encoding="ascii", errors="ignore") as f:
  540.             lines = f.read().splitlines()
  541.  
  542.         if not lines:
  543.             return
  544.  
  545.         max_width = max(len(line) for line in lines)
  546.         height = len(lines)
  547.  
  548.         capped_width = min(max_width, max_w)
  549.         capped_height = min(height, max_h)
  550.  
  551.         self.width_chars = max(1, capped_width)
  552.         self.height_chars = max(1, capped_height)
  553.  
  554.         self.char_grid = [[' ' for _ in range(self.width_chars)]
  555.                           for _ in range(self.height_chars)]
  556.  
  557.         for y in range(self.height_chars):
  558.             line = lines[y]
  559.             for x in range(min(len(line), self.width_chars)):
  560.                 self.char_grid[y][x] = line[x]
  561.  
  562.         # Resize window roughly to match loaded content
  563.         self.resize(self.width_chars * self.char_width,
  564.                     self.height_chars * self.char_height)
  565.  
  566.         self.clear_vector_preview()
  567.         self.update()
  568.         self.set_base_title(f"QtAwesome ASCII Arts!  [loaded: {os.path.basename(path)}]")
  569.  
  570.     def save_file(self):
  571.         filename = f"ascii_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
  572.         with open(filename, "w", encoding="ascii") as f:
  573.             for row in self.char_grid:
  574.                 line = "".join(row)
  575.                 if any(c != ' ' for c in line):
  576.                     out = line.rstrip()
  577.                 else:
  578.                     out = " "
  579.                 f.write(out + "\n")
  580.         self.set_base_title(f"QtAwesome ASCII Arts!  [saved: {filename}]")
  581.  
  582.     # ---------------- Layout / grid management ----------------
  583.  
  584.     def paintEvent(self, event):
  585.         painter = QPainter(self)
  586.         painter.setFont(self.font)
  587.         if not self.BG_Flag:
  588.             painter.fillRect(self.rect(), QColor("white"))
  589.         painter.setRenderHint(QPainter.TextAntialiasing)
  590.  
  591.         # Permanent grid
  592.         painter.setPen(self.text_color)
  593.         for y in range(self.height_chars):
  594.             for x in range(self.width_chars):
  595.                 ch = self.char_grid[y][x]
  596.                 if ch.strip():
  597.                     painter.drawText(x * self.char_width, (y + 1) * self.char_height, ch)
  598.  
  599.         # Vector overlay (non-permanent)
  600.         if self.vector_points and (self.vector_active or self.vector_pinned):
  601.             painter.save()
  602.             painter.setPen(QColor("darkGray"))
  603.             for (x, y) in self.vector_points:
  604.                 painter.drawText(x * self.char_width, (y + 1) * self.char_height, self.vector_char)
  605.             painter.restore()
  606.  
  607.         # Cursor rectangle overlay
  608.         if self.Show_Cursor:
  609.             painter.setPen(QColor("red"))
  610.             painter.drawRect(
  611.                 self.cursor_x * self.char_width,
  612.                 self.cursor_y * self.char_height,
  613.                 self.char_width,
  614.                 self.char_height
  615.             )
  616.  
  617.     def resizeEvent(self, event):
  618.         self.update_grid()
  619.         self.update()
  620.  
  621.     def update_grid(self):
  622.         new_w = max(1, self.width() // self.char_width)
  623.         new_h = max(1, self.height() // self.char_height)
  624.  
  625.         if new_w != self.width_chars or new_h != self.height_chars:
  626.             old_grid = self.char_grid
  627.             old_h = len(old_grid)
  628.             old_w = len(old_grid[0]) if old_grid else 0
  629.  
  630.             self.width_chars = new_w
  631.             self.height_chars = new_h
  632.  
  633.             new_grid = [[' '] * new_w for _ in range(new_h)]
  634.             for y in range(min(new_h, old_h)):
  635.                 for x in range(min(new_w, old_w)):
  636.                     new_grid[y][x] = old_grid[y][x]
  637.  
  638.             self.char_grid = new_grid
  639.  
  640.             # If vector exists, clamp it and recompute
  641.             if self.vector_points and (self.vector_active or self.vector_pinned):
  642.                 if self.vector_start:
  643.                     x, y = self.vector_start
  644.                     self.vector_start = (max(0, min(self.width_chars - 1, x)),
  645.                                          max(0, min(self.height_chars - 1, y)))
  646.                 if self.vector_end:
  647.                     x, y = self.vector_end
  648.                     self.vector_end = (max(0, min(self.width_chars - 1, x)),
  649.                                        max(0, min(self.height_chars - 1, y)))
  650.                 self.update_vector_preview()
  651.  
  652.     def populate(self, s):
  653.         if not s:
  654.             return
  655.         length = len(s)
  656.         index = 0
  657.         for y in range(self.height_chars):
  658.             for x in range(self.width_chars):
  659.                 self.char_grid[y][x] = s[index % length]
  660.                 index += 1
  661.  
  662.     # ---------------- Tiles ----------------
  663.  
  664.     def draw_tile(self, name):
  665.         if name not in self.glyphs:
  666.             return
  667.  
  668.         lines = self.glyphs[name].split("\n")
  669.         lines = lines.lstrip("\n").rstrip("\n")
  670.         for dy, line in enumerate(lines):
  671.             for dx, ch in enumerate(line):
  672.                 x = self.cursor_x + dx
  673.                 y = self.cursor_y + dy
  674.                 if 0 <= x < self.width_chars and 0 <= y < self.height_chars:
  675.                     self.char_grid[y][x] = ch
  676.         self.update()
  677.  
  678.     # ---------------- Key handling ----------------
  679.  
  680.     def keyPressEvent(self, event):
  681.         key = event.key()
  682.  
  683.         # Clear vector preview
  684.         if key == Qt.Key_Escape:
  685.             self.clear_vector_preview()
  686.             return
  687.  
  688.         # Commit vector preview
  689.         if key in (Qt.Key_Return, Qt.Key_Enter):
  690.             # Only commit if a vector is showing
  691.             if self.vector_points and (self.vector_active or self.vector_pinned):
  692.                 self.commit_vector()
  693.                 return
  694.  
  695.         if key == Qt.Key_R:
  696.             self.char_grid = [[' '] * self.width_chars for _ in range(self.height_chars)]
  697.             self.draw_index = 0
  698.             self.clear_vector_preview()
  699.             self.update()
  700.             return
  701.  
  702.         # Color keys
  703.         if key == Qt.Key_1:
  704.             self.text_color = QColor("black")
  705.         elif key == Qt.Key_2:
  706.             self.text_color = QColor("red")
  707.         elif key == Qt.Key_3:
  708.             self.text_color = QColor("blue")
  709.         elif key == Qt.Key_4:
  710.             self.text_color = QColor("green")
  711.         elif key == Qt.Key_5:
  712.             self.text_color = QColor("purple")
  713.         elif key == Qt.Key_6:
  714.             self.text_color = QColor("orange")
  715.         elif key == Qt.Key_7:
  716.             self.text_color = QColor("yellow")
  717.         elif key == Qt.Key_8:
  718.             self.text_color = QColor("cyan")
  719.         elif key == Qt.Key_9:
  720.             self.text_color = QColor("magenta")
  721.         elif key == Qt.Key_0:
  722.             self.text_color = QColor("white")
  723.  
  724.         # Brush selection
  725.         elif key == Qt.Key_T:
  726.             self.draw_string = self.draw_string0
  727.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  728.         elif key == Qt.Key_Y:
  729.             self.draw_string = self.draw_string1
  730.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  731.         elif key == Qt.Key_U:
  732.             self.draw_string = self.draw_string2
  733.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  734.         elif key == Qt.Key_I:
  735.             self.draw_string = self.draw_string3
  736.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  737.         elif key == Qt.Key_J:
  738.             self.draw_string = self.draw_string4
  739.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  740.         elif key == Qt.Key_K:
  741.             self.draw_string = self.draw_string5
  742.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  743.         elif key == Qt.Key_L:
  744.             self.draw_string = self.draw_string6
  745.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  746.         elif key == Qt.Key_M:
  747.             self.draw_string = self.draw_string7
  748.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  749.         elif key == Qt.Key_N:
  750.             self.draw_string = self.draw_string8
  751.             self.set_base_title(f"Awesome ASCII Art {self.draw_string}")
  752.         elif key == Qt.Key_E:
  753.             self.draw_string = self.draw_string9
  754.             self.set_base_title("Awesome ASCII Art Eraser")
  755.  
  756.         # WASD / arrow drawing
  757.         elif key in (Qt.Key_W, Qt.Key_Up):
  758.             self.cursor_y = max(0, self.cursor_y - 1)
  759.             self.draw_at_cursor()
  760.         elif key in (Qt.Key_S, Qt.Key_Down):
  761.             self.cursor_y = min(self.height_chars - 1, self.cursor_y + 1)
  762.             self.draw_at_cursor()
  763.         elif key in (Qt.Key_A, Qt.Key_Left):
  764.             self.cursor_x = max(0, self.cursor_x - 1)
  765.             self.draw_at_cursor()
  766.         elif key in (Qt.Key_D, Qt.Key_Right):
  767.             self.cursor_x = min(self.width_chars - 1, self.cursor_x + 1)
  768.             self.draw_at_cursor()
  769.  
  770.         # Help
  771.         elif key == Qt.Key_H:
  772.             self.show_help()
  773.  
  774.         # Populate
  775.         elif key == Qt.Key_P:
  776.             string, ok = QInputDialog.getText(
  777.                 self, "Populate Text area",
  778.                 "Type the characters to fill area:"
  779.             )
  780.             if ok and string:
  781.                 self.populate(string)
  782.                 self.update()
  783.  
  784.         # Toggle background
  785.         elif key == Qt.Key_B:
  786.             self.BG_Flag = not self.BG_Flag
  787.             self.setAttribute(Qt.WA_TranslucentBackground, self.BG_Flag)
  788.  
  789.         # Toggle cursor display
  790.         elif key == Qt.Key_C:
  791.             self.Show_Cursor = not self.Show_Cursor
  792.  
  793.         # Tiles (examples from your dev keys)
  794.         elif key == Qt.Key_G:
  795.             self.set_base_title("Tile 1")
  796.             self.draw_tile("Tile1")
  797.         elif key == Qt.Key_V:
  798.             self.set_base_title("Tile 2")
  799.             self.draw_tile("Tile2")
  800.         elif key == Qt.Key_Z:
  801.             self.set_base_title("Tile 3")
  802.             self.draw_tile("Tile3")
  803.         elif key == Qt.Key_X:
  804.             self.set_base_title("Tile 4")
  805.             self.draw_tile("Tile4")
  806.  
  807.         self.update()
  808.  
  809.  
  810. if __name__ == "__main__":
  811.     app = QApplication(sys.argv)
  812.  
  813.     if len(sys.argv) < 2:
  814.         window = AsciiArtWindow(80, 80)
  815.         print("Optional")
  816.         print(f"Usage: {sys.argv[0]} [width height]")
  817.     elif len(sys.argv) != 3:
  818.         print(f"Usage: {sys.argv[0]} [width height]")
  819.         sys.exit(1)
  820.     else:
  821.         x = int(sys.argv[1])
  822.         y = int(sys.argv[2])
  823.         if x < 1:
  824.             x = 1
  825.         if y < 1:
  826.             y = 10
  827.         window = AsciiArtWindow(x, y)
  828.  
  829.     sys.exit(app.exec_())
  830.  
Advertisement
Add Comment
Please, Sign In to add comment