You are not logged in.

#1 2011-08-17 23:46:11

kefka
Member
Registered: 2011-06-04
Posts: 4

Small PyQT color swapper program

Hi everyone, today I wrote a small program in python/QT which scans a file you pass it for unique #RRGGBB color tags, and gives you an interface to modify them. I made it specifically for editing SublimeText/SublimeText2/Textmate color schemes, but it could be applied to lots of things, and thought it might be useful to share.

You'll need PyQt4 to run it.

Apologies for the sloppy coding^^

http://git.minornine.com/index.cgi/scri … in/recolor

#!/usr/bin/env python

# GUI to replace #RRGGBB color tags in a file
# Copyright 2011 Josh Bothun
# joshbothun@gmail.com

import sys
import tempfile
import re
import math
import os
import shutil
from copy import copy

from PyQt4.QtCore import *
from PyQt4.QtGui import *

def getColors(path):
    """ Find unique color tags in a file """
    try:
        file = open(path, 'r')
        pat = re.compile(r'#[a-fA-F0-9]{6}')
        tags = set()
        for line in file:
            for match in re.findall(pat, line):
                tags.add(match.upper())
        file.close()
        return tags
    except (OSError, IOError):
        return None

def setColors(inpath, colormap):
    """ Replace color tags in `path` from dict `colormap` """
    try:
        infile = open(inpath, 'r')
        fh, outpath = tempfile.mkstemp()
        outfile = open(outpath, 'w')
        for line in infile:
            for oldcolor, newcolor in colormap.items():
                if oldcolor in line:
                    line = line.replace(oldcolor, newcolor)
            outfile.write(line)
        outfile.close()
        infile.close()
        os.close(fh)
        # Move the temp file to original file location
        os.remove(inpath)
        shutil.move(outpath, inpath)
        return True
    except (OSError, IOError):
        return False

def getVisibleFore(bgcolor):
    # Calculate black/white text
    return '#FFFFFF' if int(bgcolor.strip('#'), 16) < 0x7fffff \
           else "#000000"

class MainWindow(QMainWindow):
    def __init__(self, colors, infile):
        # Init UI
        QMainWindow.__init__(self)
        self.infile = infile
        self.frame = QWidget(self)
        self.frame.setLayout(QVBoxLayout())
        self.setCentralWidget(self.frame)
        self.setStatusBar(QStatusBar(self))

        # Color mapping
        self.colormap = dict(zip(colors, colors))

        # Populate color pickers
        self.colorFrame = QWidget(self.frame)
        self.colorFrame.setLayout(QGridLayout())
        self.frame.layout().addWidget(self.colorFrame)
        self.createColorButtons()
        
        # Static buttons
        staticFrame = QWidget(self.frame)
        staticFrame.setLayout(QHBoxLayout())
        self.frame.layout().addWidget(staticFrame)
        applyButton = QPushButton('Apply')
        discardButton = QPushButton('Discard')
        staticFrame.layout().addWidget(applyButton)
        staticFrame.layout().addWidget(discardButton)

        # Static button callbacks
        self.connect(applyButton, SIGNAL('clicked()'), self.applyColors)
        self.connect(discardButton, SIGNAL('clicked()'), self.discardColors)

    def createColorButtons(self):
        rows = int(math.sqrt(len(self.colormap.keys())))
        for i, color in enumerate(self.colormap.keys()):


            # Create the button
            button = QPushButton(self)
            button.setFlat(True)
            pal = button.palette()
            pal.setColor(button.backgroundRole(), QColor(color))
            pal.setColor(button.foregroundRole(), QColor(getVisibleFore(color)))
            button.setAutoFillBackground(True)
            button.setPalette(pal)
            button.setText(color)
            self.colorFrame.layout().addWidget(button, i % rows, i / rows)

            # HACK: Store color on button object
            setattr(button, '_ORIG_COLOR', color)
            setattr(button, '_COLOR', color)            

            # Create closure around button and color
            self.createSelectorClosure(button)

    def discardColors(self):
        # Reset map
        for key, val in self.colormap.items():
            self.colormap[key] = key
        # Reset button colors
        for widget in self.colorFrame.children():
            if isinstance(widget, QPushButton):
                self.setButtonColor(widget, widget._ORIG_COLOR)
                widget.update()
        self.statusBar().showMessage('Colors reset to default')

    def applyColors(self):
        if setColors(self.infile, self.colormap):
            self.statusBar().showMessage('Colors successfully replaced!')
        else:
            self.statusBar().showMessage('ERROR: Could not replace colors')

    def setButtonColor(self, button, tag):
        color = QColor(tag)
        pal = button.palette()
        pal.setColor(button.backgroundRole(), color)
        button.setPalette(pal)
        button.setText(tag)

    def createSelectorClosure(self, button):
        # Create callback method
        def selectColor():
            color = getattr(button, '_COLOR', '#FFFFFF')
            orig_color = getattr(button, '_ORIG_COLOR', '#FFFFFF')
            new = QColorDialog.getColor(initial=QColor(color))
            if new.isValid():
                tag = str(new.name()).upper()
                # Tag must be unique
                if tag == orig_color or tag not in self.colormap.keys():
                    # Update map
                    self.colormap[orig_color] = tag
                    # HACK: Update button color attr
                    setattr(button, '_COLOR', tag)
                    # Update label
                    self.setButtonColor(button, tag)
                else:
                    QMessageBox.critical(self, 'Error', 'You must select a unique color.')

        # Connect click function
        self.connect(button, SIGNAL('clicked()'), selectColor)

def main():
    if len(sys.argv) < 2:
        print 'Need an input file'
        sys.exit()
    
    infile = sys.argv[1]
    colors = getColors(infile)

    if not colors:
        print 'Unable to read file', infile
        sys.exit()

    # Run qt
    app = QApplication(sys.argv)
    window = MainWindow(colors, infile)
    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Offline

Board footer

Powered by FluxBB