2

I have written a GUI program which takes a black and white image, converts it into 2 character ASCII art and counts the sections of characters in each row. It's really hard to explain so I'll paste the code below so you can see what it does.

The program is for my wife, to help her do some sort of knitting technique, it saves her from writing it down.

I want to make a button on the second window which, when clicked, highlights a certain amount of characters from a single line in the Multiline element (e.g. the first row of '.'s in the line get selected, then the first row of '#'s, then the next row or '.' etc).

I'm using PySimpleGUI and I've looked through all the documentation I could find, I've tried Google but I cant seem to find what I want, or work it out. I don't mind if the text if selected or highlighted a different colour.

Here is my code:

import PIL.Image
import os
from itertools import groupby
import PySimpleGUI as sg
import time


def main_window():
  #Set theme and layout
  sg.theme('reddit')

  layout = [
    [sg.Text("This program is designed to turn a black and white image into a double-knit template")],
    [sg.Text("If your image doesn't display as desired, adjusting the contrast might help.")],
    [sg.Text("Enter file name + extention (eg. 'image.jpg')"), sg.InputText(size=(50),key='pic')],
    [sg.Text("Enter the size you'd like in # of stitches"), sg.Text("Width: "), sg.InputText(size=(10),key='wth'), sg.Text("Length: "), sg.InputText(size=(10),key='lth')],
    [sg.Text("Contrast"), sg.Slider(range=(1,11), default_value=6, orientation='h', key='sli')],
    [sg.Button('Generate')],
    [sg.ProgressBar(50, orientation='h', border_width=4, key='pbar')]
    ]

  window = sg.Window('Knitify', layout)

  #Contrast variables
  chars1 = ["#", ".", ".", ".", ".", ".", ".", ".", ".", ".", "."]
  chars2 = ["#", "#", ".", ".", ".", ".", ".", ".", ".", ".", "."]
  chars3 = ["#", "#", "#", ".", ".", ".", ".", ".", ".", ".", "."]
  chars4 = ["#", "#", "#", "#", ".", ".", ".", ".", ".", ".", "."]
  chars5 = ["#", "#", "#", "#", "#", ".", ".", ".", ".", ".", "."]
  chars6 = ["#", "#", "#", "#", "#", "#", ".", ".", ".", ".", "."]
  chars7 = ["#", "#", "#", "#", "#", "#", "#", ".", ".", ".", "."]
  chars8 = ["#", "#", "#", "#", "#", "#", "#", "#", ".", ".", "."]
  chars9 = ["#", "#", "#", "#", "#", "#", "#", "#", "#", ".", "."]
  chars10 = ["#", "#", "#", "#", "#", "#", "#", "#", "#", "#", "."]
  chars11 = ["#", "#", "#", "#", "#", "#", "#", "#", "#", "#", "#"]

  
  while True:
    event, values = window.read()
    print(event, values)
    #Get contrast slider value
    i = int(values['sli'])
    #Get Image file
    p = str(values['pic'])
    #Grab contrast level
    if i == 1:
      con = chars1
    elif i ==2:
      con = chars2
    elif i ==3:
      con = chars3
    elif i ==4:
      con = chars4
    elif i ==5:
      con = chars5
    elif i ==6:
      con = chars6
    elif i ==7:
      con = chars7
    elif i ==8:
      con = chars8
    elif i ==9:
      con = chars9
    elif i ==10:
      con = chars10
    elif i ==11:
      con = chars11
    
    if event == sg.WIN_CLOSED:
      break
    if event == 'Generate':
      #Check if given image file exists, if not, do nothing, wait for valid image.
      if os.path.exists(p):
        #Check if output file already exists, if it does, delete and replace with newly generated image.
        if os.path.exists('convertedImage.txt'):
          os.remove('convertedImage.txt')
        #Open and prepare image with Pillow
        path = values['pic']
        img = PIL.Image.open(path)

        width, height = img.size

        aspect_ratio = height/width
        #Get user to enter desired width and height (in stitches) of output image
        new_width = int(values['wth'])
        new_height = int(values['lth'])
        #If values are impossible to deal with then terminate and wait for correct values
        if new_width <= 0:
          break
        if new_height <= 0:
          break
        #Resize and convert image to ASCII
        img = img.resize((int(new_width), int(new_height)))

        img = img.convert('L')

        pixels = img.getdata()
        new_pixels = [con[pixel//25] for pixel in pixels]
        new_pixels = ''.join(new_pixels)
        new_pixels_count = len(new_pixels)
        ascii_image = [new_pixels[index:index + new_width] for index in range(0, new_pixels_count, new_width)]
        ascii_image = "\n".join(ascii_image)
        #Save ASCII image to temp file
        with open('testout.txt', 'w') as f:
          f.write(ascii_image)
          f.close()
        #Use temp file to scan each line and number alternating stitches in each row
        with open('testout.txt', 'r') as f:
          for line in map(lambda l: l.strip(), f):
            runs = [sum(1 for _ in g) for _, g in groupby(line)]
            x = f"{line} {' '.join(map(str, runs))}"
            #Write result to new file
            with open("convertedImage.txt", 'a') as n:
              n.write("\n" + x)
              n.close()
        f.close()
        #Remove temp file
        os.remove('testout.txt')
        #Progress bar bit
        bar = 1
        for i in range(8):
          window['pbar'].update_bar(bar)
          bar = bar+bar
          time.sleep(0.2)
        #Open second window
        output_window()

        window['pbar'].update_bar(0)

  window.close()

def output_window():
  #Read contents of converted image
  with open('convertedImage.txt', 'r') as f:
    contents = f.read()
    f.close()
  #Find the length of the longest line and set multiline box to that width.
  #Paste the contents of convertedImage to multiline box
  long = max(open('convertedImage.txt'), key=len)
  layout = [
    [sg.Multiline(size=(len(long),50), font='Consolas', default_text=contents)]
    ]
  window = sg.Window("Knitified", layout, modal=True)

  while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED:
      break


main_window()

1 Answers1

4

Following code demo the way to add/remove highlight on 'image' string, here tkinter code is required.

import PySimpleGUI as sg

text = """
This program is designed to turn a black and white image into a double-knit template.
If your image doesn't display as desired, adjusting the contrast might help.
""".strip()

lines = text.split('\n')
index1 = lines[0].index("image")
index2 = lines[1].index("image")
indexes = [(f'1.{index1}', f'1.{index1+5}'), (f'2.{index2}', f'2.{index2+5}')]

sg.theme('DarkBlue3')
font1 = ('Courier New', 10)
font2 = ('Courier New', 10, 'bold')
sg.set_options(font=font1)

layout = [
    [sg.Multiline(text, size=(40, 8), key='-MULTILINE')],
    [sg.Push(), sg.Button('Highlight'), sg.Button('Remove')],
]
window = sg.Window('Title', layout, finalize=True)
multiline = window['-MULTILINE']
widget = multiline.Widget
widget.tag_config('HIGHLIGHT', foreground='white', background='blue', font=font2)
# widget.tag_config('HIGHLIGHT', foreground=multiline.BackgroundColor, background=multiline.TextColor, font=font2)

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED:
        break
    elif event == 'Highlight':
        for index1, index2 in indexes:
            widget.tag_add('HIGHLIGHT', index1, index2)
        window['Highlight'].update(disabled=True)
    elif event == 'Remove':
        for index1, index2 in indexes:
            widget.tag_remove('HIGHLIGHT', index1, index2)
        window['Highlight'].update(disabled=False)

window.close()

enter image description here

Jason Yang
  • 11,284
  • 2
  • 9
  • 23