CryptoPunks GUI by Glimmer DSL for Tk
Andy Maleh
Posted on October 25, 2021
CryptoPunks GUI is a Graphical User Interface for the famous cryptopunks Ruby gem, built with Glimmer DSL for Tk.
# From: https://github.com/AndyObtiva/cryptopunks-gui
require 'glimmer-dsl-tk'
require 'cryptopunks'
require 'facets'
require 'fileutils'
require 'net/http'
require 'uri'
require 'glimmer/data_binding/observer'
require 'yaml'
require 'puts_debuggerer'
class CryptopunksGui
include Glimmer
PALETTES = ['Standard'] + (Palette8bit.constants).map(&:name).map {|palette| palette.split('_').map(&:capitalize).join(' ')}.reject { |palette| palette.include?(' ') }.sort
STYLES = ['Normal', 'Led', 'Sketch']
COLLECTION_URL_MAP = {
'Punks' => 'https://raw.githubusercontent.com/larvalabs/cryptopunks/master/punks.png',
'Mohawks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/mohawks.png',
'Blondies' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/blondies.png',
'Zombies' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/zombies.png',
'Apes' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/apes.png',
'Aliens' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/aliens.png',
'Golden Punks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/goldenpunks.png',
'Halloween Punks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/halloweenpunks.png',
'Front Punks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/frontpunks.png',
'More Punks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/morepunks.png',
'Expansion Punks' => 'https://expansionpunks.com/provenance/expansionpunks.png',
'Avalanche Punks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/avalanchepunks.png',
'Intl Punks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/intlpunks.png',
'Ape Punks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/apepunks.png',
'Alien Clan' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/alienclan.png',
'Bored Apes Blue' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/boredapes_blue.png',
'Bored Apes Red' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/boredapes_red.png',
'Bored Apes Neon Glow' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/boredapes_neon_glow.png',
'Bored Apes Stars and Stripes' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/boredapes_stars_and_stripes.png',
'Bored Apes Acid' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/boredapes_acid.png',
'Cool Cats' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats.png',
'Cool Cats Mohawks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats_mohawks.png',
'Cool Cats Ninjas' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats_ninjas.png',
'Cool Cats TV Heads' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats_tvheads.png',
'Cool Cats Pirates' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats_pirates.png',
'Cool Cats Unicorns' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats_unicorns.png',
'Cool Cats Dragons' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats_dragons.png',
'Cool Cats Frogs' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/coolcats_frogs.png',
'Pudgy Penguins' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/pudgypenguins.png',
'Dodge' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/dodge.png',
'Rocks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/rocks.png',
'Punk Rocks' => 'https://raw.githubusercontent.com/cryptopunksnotdead/programming-cryptopunks/master/i/punkrocks.png',
'Tulips' => 'https://raw.githubusercontent.com/cryptopunksnotdead/awesome-24px/master/collection/tulips.png',
}
attr_accessor :collection, :image_index, :zoom, :palette, :style, :led_spacing, :led_round_corner, :sketch_line, :flip, :mirror
def initialize
initialize_punk_directory
initialize_collection
load_config
initialize_defaults
observe_image_attribute_changes
create_gui
self.image_index = 0
@root.open
end
def collection_options
COLLECTION_URL_MAP.keys
end
def palette_options
PALETTES
end
def style_options
STYLES
end
def initialize_punk_directory
@punk_directory = @punk_config_directory = File.join(Dir.home, 'cryptopunks')
FileUtils.mkdir_p(@punk_directory)
end
def initialize_collection
return if @collection && @collection == @last_collection
@collection ||= COLLECTION_URL_MAP.keys.first
url = COLLECTION_URL_MAP[@collection]
@punk_file = File.join(@punk_config_directory, File.basename(url, '.png'))
File.write(@punk_file, Net::HTTP.get(URI(url))) unless File.exist?(@punk_file)
@images ||= {}
@images[@collection] ||= Punks::Image::Composite.read(@punk_file)
@last_collection = @collection
self.image_index = 0
@image_index_spinbox.to = @images[@collection].size - 1 if @image_index_spinbox
end
def load_config
@punk_config_file = File.join(@punk_config_directory, 'cryptopunks.yml')
FileUtils.touch(@punk_config_file)
@punk_config = YAML.load(File.read(@punk_config_file)) || {punk_directory: @punk_directory}
@punk_directory = @punk_config[:punk_directory]
end
def save_config
File.write(@punk_config_file, YAML.dump(@punk_config))
end
def initialize_defaults
@collection = COLLECTION_URL_MAP.keys.first
@zoom = 12
@palette = PALETTES.first
@style = STYLES.first
@led_spacing = 2
@led_round_corner = false
@sketch_line = 1
@mirror = false
@flip = false
end
def observe_image_attribute_changes
observer = Glimmer::DataBinding::Observer.proc { generate_image }
observer.observe(self, :collection)
observer.observe(self, :image_index)
observer.observe(self, :zoom)
observer.observe(self, :palette)
observer.observe(self, :style)
observer.observe(self, :led_spacing)
observer.observe(self, :led_round_corner)
observer.observe(self, :sketch_line)
observer.observe(self, :mirror)
observer.observe(self, :flip)
end
def generate_image
initialize_collection
return if @image_index.to_i > @images[@collection].size
image_location = File.join(@punk_directory, "#{@collection.gsub(' ', '').downcase}-#{@image_index}#{"x#{@zoom}" if @zoom.to_i > 1}#{"-#{@palette.underscore}" if @palette != PALETTES.first}#{"-#{@style.underscore}" if @style != STYLES.first}.png")
puts "Writing punk image to #{image_location}"
selected_punk = @images[@collection][@image_index.to_i]
selected_punk = selected_punk.change_palette8bit(Palette8bit.const_get(@palette.gsub(' ', '_').upcase.to_sym)) if @palette != PALETTES.first
@original_zoom = @zoom
if @style != STYLES.first
style_options = {}
if @style == 'Led'
style_options[:spacing] = @led_spacing.to_i
style_options[:round_corner] = @led_round_corner
end
if @style == 'Sketch'
style_options[:line] = @sketch_line.to_i
end
selected_punk = selected_punk.send(@style.underscore, @zoom.to_i, **style_options)
end
selected_punk = selected_punk.mirror if @mirror
selected_punk = selected_punk.flip if @flip
selected_punk = selected_punk.zoom(@zoom.to_i) if @style == STYLES.first
selected_punk.save(image_location)
@image_label.image = image_location
@output_location_entry.text = image_location
@previous_style = @style
notify_observers(:zoom) if @zoom != @original_zoom
end
def create_gui
@root = root {
title 'CryptoPunks GUI'
iconphoto File.expand_path('../icons/cryptopunks-gui.png', __dir__)
frame {
label {
text 'Collection:'
}
combobox {
readonly true
text <=> [self, :collection]
}
label {
text 'Image Index:'
}
@image_index_spinbox = spinbox {
from 0
to @images[@collection].size - 1
text <=> [self, :image_index]
}
label {
text 'Zoom:'
}
spinbox {
from 1
to 72
text <=> [self, :zoom]
}
label {
text 'Palette:'
}
combobox {
readonly true
text <=> [self, :palette]
}
label {
text 'Style:'
}
combobox {
readonly true
text <=> [self, :style, after_write: ->(val) {add_style_options}]
}
@style_options_frame = frame {
padding 0
}
frame {
padding 0
checkbutton {
grid row: 0, column: 0, column_weight: 0
variable <=> [self, :mirror]
}
label {
grid row: 0, column: 1
text 'Mirror'
on('Button-1') do
self.mirror = !mirror
end
}
checkbutton {
grid row: 0, column: 2
variable <=> [self, :flip]
}
label {
grid row: 0, column: 3
text 'Flip'
on('Button-1') do
self.flip = !flip
end
}
}
label {
text 'Output Location:'
}
frame {
padding 0
@output_location_entry = entry {
grid row: 0, column: 0
readonly true
width 47
}
button {
grid row: 0, column: 1
text '...'
width 1.1
on('command') do
@punk_directory = choose_directory(parent: @root)
@punk_config[:punk_directory] = @punk_directory
save_config
generate_image
end
}
}
@image_label = label {
grid row_weight: 1
}
}
}
end
def add_style_options
@style_options_frame.content {
@style_options_frame.children.each(&:destroy)
if @style == 'Led'
frame {
padding 0
label {
grid row: 0, column: 0, column_weight: 0
text 'Spacing:'
}
spinbox {
grid row: 0, column: 1
from 1
to 72
text <=> [self, :led_spacing]
}
checkbutton {
grid row: 0, column: 2
variable <=> [self, :led_round_corner]
}
label {
grid row: 0, column: 3
text 'Round Corner'
on('Button-1') do
self.led_round_corner = !led_round_corner
end
}
}
elsif @style == 'Sketch'
frame {
padding 0
label {
grid row: 0, column: 0, column_weight: 0
text 'Line:'
}
spinbox {
grid row: 0, column: 1
from 1
to 72
text <=> [self, :sketch_line]
}
}
else
frame { # filler
padding 0
height 0
width 0
}
end
}
end
end
CryptopunksGui.new.launch
Happy Glimmering!
💖 💪 🙅 🚩
Andy Maleh
Posted on October 25, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
privacy Caught in the Crunch My Journey from Snacks to 2 Million Exposed Users Privacy
November 30, 2024
devchallenge Submission for the DevCycle Feature Flag Challenge: Feature Flag Funhouse
November 30, 2024