• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

Editor in Python (war: Perl/Tk-Editor modifiziert)

abgdf

Guru
Hi,

den Perl/Tk-Editor "ptked", den wohl fast jeder unter "/usr/bin" liegen hat, hab' ich mal so verändert,
daß der Code besser lesbar ist und er auch besser, das heißt schon fast richtig gut benutzbar ist (ich hatte keine Lust, bei schlankem WM (WindowMaker) extra "kate" oder "gedit" zu starten, wenn ich etwas in Webseiten posten, bzw. einfügen wollte (ich benutze sonst den Konsolen-vim)).

Schrift ist jetzt "SuSE Sans 16" (best viewed in 1024x768).

"STRG + a" ist jetzt "select all",
"STRG + s" "save text" und
"STRG + q" "sofort beenden".

Was diese "IO::"-Module (aus dem Originalcode) machen, weiß ich aber nicht so recht. Weiß das jemand ?

Code:
#!/usr/bin/perl -w

# tkedit.pl

if($#ARGV == -1)
{
    $ARGV[0] = "Newfile.txt";
}

use strict;

# use IO::Handle;
# use IO::Seekable;
# use IO::File;
# use IO::Pipe;
# use IO::Dir;

# use IO;

use Socket;
use IO::Socket;

use Cwd;

use vars qw($VERSION $portfile);

$VERSION = '3.5';

my %opt;

INIT
{
    my $home = $ENV{'HOME'} || $ENV{'HOMEDRIVE'}.$ENV{'HOMEPATH'};

    $portfile = "$home/.ptkedsn";

    my $port = $ENV{'PTKEDPORT'};

    return if $^C;

    getopts("s",\%opt);

    unless (defined $port)
    {
        if (open(SN,"$portfile"))
        {
            $port = <SN>;
            close(SN);
         }
     }
    if (defined $port)
    {
        my $sock = IO::Socket::INET->new(PeerAddr => 'localhost',
                                         PeerPort => $port, Proto => 'tcp');
        if ($sock)
        {
            binmode($sock);
            $sock->autoflush;
            foreach my $file (@ARGV)
            {
                unless  (print $sock "$file\n")
                {
                    die "Cannot print $file to socket:$!";
                }
                print "Requested '$file'\n";
            }
            $sock->close || die "Cannot close socket:$!";
            exit(0);
        }
        else
        {
            warn "Cannot connect to server on $port:$!";
        }
    }
}

use Tk;
use Tk::DropSite qw(XDND KDE Sun);
use Tk::DragDrop qw(XDND KDE Sun);
use Tk::widgets qw(TextUndo Scrollbar Menu);
use Getopt::Std;

# use Tk::ErrorDialog;

my $top = MainWindow->new();

if ($opt{'s'})
{
    my $sock = IO::Socket::INET->new(Listen => 5, Proto => 'tcp');
    die "Cannot open listen socket:$!" unless defined $sock;
    binmode($sock);

    my $port = $sock->sockport;
    $ENV{'PTKEDPORT'} = $port;

    open(SN,">$portfile") || die "Cannot open $portfile:$!";
    print SN $port;
    close(SN);

    print "Accepting connections on $port\n";

    $top->fileevent($sock,'readable',
                    sub
                    {
                        print "accepting $sock\n";
                        my $client = $sock->accept;
                        if (defined $client)
                        {
                            binmode($client);
                            print "Connection $client\n";
                            $top->fileevent($client,'readable',[\&EditRequest,$client]);
                        }
                    });
}

Tk::Event::HandleSignals();
$SIG{'INT'} = sub { $top->WmDeleteWindow };

$top->iconify;

# $top->fontCreate('ptked',
#                  -family => 'courier',
#                  -size => 16,
#                  -weight => 'normal',
#                  -slant => 'roman');

# $top->optionAdd('*TextUndo.Font' => 'ptked');

$top->optionAdd("*font", "{Suse Sans} 16 {normal}");
$top->optionAdd('*TextUndo.Background' => '#fff5e1');

$top->optionAdd("*tearOff", "false");
$top->geometry("+48+87");

foreach my $file (@ARGV)
{
    Create_Edit($file);
}

sub EditRequest
{
    my ($client) = @_;
    local $_;
    while (<$client>)
    {
        chomp($_);
        print "'$_'\n",
        Create_Edit($_);
    }
    warn "Odd $!" unless eof($client);
    $top->fileevent($client,'readable','');
    print "Close $client\n";
    $client->close;
}

MainLoop;
unlink("$portfile");
exit(0);

sub Create_Edit
{
    my $path = shift;
    my $ed = $top->Toplevel(-title => $path);
    $ed->withdraw;
    $ed->geometry("+64+99");
    $top->{'Edits'}++;
    $ed->OnDestroy([\&RemoveEdit, $top]);

    my $t = $ed->Scrolled('TextUndo',
                          -wrap => 'word',
                          -scrollbars => 'osre');

    $t->pack(-expand => 1, -fill => 'both');
   
    $t = $t->Subwidget('textundo');

    my $menu = $t->menu;

    $menu->cascade(-label => '~Help',
                   -menuitems => [[Button => '~About...',
                   -command => [\&About,$ed]],]);

    $ed->configure(-menu => $menu);

    my $dd = $t->DragDrop(-event => '<Meta-B1-Motion>');
    $t->bind(ref($t),'<Meta-B1-Motion>',\&Ouch);
    $t->bind(ref($t),'<Meta-ButtonPress>',\&Ouch);
    $t->bind(ref($t),'<Meta-ButtonRelease>',\&Ouch);

    $dd->configure(-startcommand => sub { return 1 unless (eval { $t->tagNextrange(sel => '1.0','end')}); $dd->configure(-text => $t->get('sel.first','sel.last')); });

    $t->DropSite(-motioncommand => sub { my ($x,$y) = @_; $t->markSet(insert => "\@$x,$y"); }, -dropcommand => [\&HandleDrop,$t],);

    $ed->protocol('WM_DELETE_WINDOW', [ConfirmExit => $t]);
    $t->bind('<F3>',\&DoFind);

    # There you go:
    $t->bind(ref($t),'<Control-q>', sub { $t -> ConfirmExit() } );
    $t->bind(ref($t),'<Control-a>', sub { $t -> selectAll() });

    $t->bind(ref($t),'<Control-s>', sub { &saveIt($t, $path) });

    $ed->idletasks;
    if (-e $path)
    {
        $t->Load($path);
    }
    else
    {
        $t->FileName($path);
    }
    $ed->deiconify;
    $t->update;
    $t->focus;
}


sub saveIt
{
    my $t = shift;
    my $file = shift;

    my $text = $t -> get("1.0", "end");

    my @path = split("/", $file);

    $file = getcwd()."/".pop(@path);

    open(OUT, ">".$file) or die "Error opening file.";
    print OUT $text;
    close(OUT);

    $t -> ResetUndo();
}


sub Ouch
{
    warn join(',','Ouch',@_);
}


sub RemoveEdit
{
    my $top = shift;
    if (--$top->{'Edits'} == 0)
    {
        $top->destroy unless $opt{'s'};
    }
}

sub HandleDrop
{
    my ($t,$seln,$x,$y) = @_;
    # warn join(',',Drop => @_);
    my $string;
    Tk::catch { $string = $t->SelectionGet(-selection => $seln,'FILE_NAME') };
    if ($@)
    {
        Tk::catch { $string = $t->SelectionGet(-selection => $seln) };
        if ($@)
        {
            my @targets = $t->SelectionGet(-selection => $seln, 'TARGETS');
           $t->messageBox(-text => "Targets : ".join(' ',@targets));
        }
        else
        {
            $t->markSet(insert => "\@$x,$y");
            $t->insert(insert => $string);
        }
    }
    else
    {
        Create_Edit($string);
    }
}

my $str;

sub DoFind
{
    my $t = shift;
    $str = shift if (@_);
    my $posn = $t->index('insert+1c');
    $t->tag('remove','sel','1.0','end');
    local $_;
    while ($t->compare($posn,'<','end'))
    {
        my ($line,$col) = split(/\./,$posn);
        $_ = $t->get("$line.0","$posn lineend");
        pos($_) = $col;
       if (/\G(.*)$str/g)
       {
           $col += length($1);
           $posn = "$line.$col";
           $t->SetCursor($posn);
           $t->tag('add','sel',$posn,"$line.".pos($_));
           $t->focus;
           return;
       }
       $posn = $t->index("$posn lineend + 1c");
    }
}

sub AskFind
{
    my ($t) = @_;
    unless (exists $t->{'AskFind'})
    {
        my $d = $t->{'AskFind'} = $t->Toplevel(-popover => 'cursor',
                                               -popanchor => 'nw');
        $d->title('Find...');
        $d->withdraw;
        $d->transient($t->toplevel);
        my $e = $d->Entry->pack;
        $e->bind('<Return>', sub { $d->withdraw; DoFind($t,$e->get); });
        $d->protocol(WM_DELETE_WINDOW =>[withdraw => $d]);
    }
    $t->{'AskFind'}->Popup;
    $t->update;
    $t->{'AskFind'}->focusNext;
}

sub About
{
    my $mw = shift;
    $mw->Dialog(-text => <<"END", -popover => $mw)->Show;
tkedit.pl version $VERSION

Based on ptked (Part of perl-tk...rpm) by Nick Ing-Simmons, 2000.

Modified by abgdf at gmx.net in 2008.
END
}

__END__

=head1 NAME

tkedit.pl - an editor in Perl/Tk

=head1 SYNOPSIS

S<  >B<tkedit.pl> [I<file-to-edit>]

=head1 DESCRIPTION

B<tkedit.pl> is a simple text editor based on perl/Tk's TextUndo widget.

=cut
Edit: Funktion "saveIt()" hinzugefügt für schnelleres Speichern mit "STRG+s".

Vielleicht bau ich noch 'ne Statusbar. Mal sehen ...

Viele Grüße
 
OP
A

abgdf

Guru
Hallo,

hab so einen "Editor" noch mal neu geschrieben in Python/Tkinter (dieses "use IO;" im Perl-Skript war mir etwas unheimlich). "SelectAll" zu machen wie in kate usw. ist nicht so ganz leicht, aber so ist's auch ganz witzig:
Code:
#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

"""

tkedit.py

A Primitive Text-Editor in Python/Tkinter.
(C) 2008 by abgdf@gmx.net, License: GNU GPL.

"""

import os
import sys

import Tkinter
import tkMessageBox
import ScrolledText
import tkFileDialog

from Tkconstants import *

class Main:

    def __init__(self):

        self.myclip = ""

        self.newfile = False
        self.filename = self.getFileName()

        self.gui = MainWindow(self)
        self.gui.mw.after_idle(self.loadFile)
        self.gui.mw.mainloop()


    def getFileName(self):

        if len(sys.argv) == 1:

            x = 1

            fname = "new01.txt"

            while(os.access(fname, os.F_OK)):
                x += 1
                fname = "new"
                if x < 10:
                    fname += "0"
                fname += str(x) + ".txt"

            self.newfile = True

            return fname

        s = sys.argv[1].split("/").pop()

        return s


    def loadFile(self):

        if self.newfile:
            return

        a = []

        try:
            fh = file(os.getcwd() + "/" + self.filename, "r")
            a = fh.readlines()
            fh.close()

        except IOError:
            self.gui.sbar.set("Error reading text-file.")

        for i in a:
            # Convert string to unicode: Tkinter.Text likes it that way:
            if type(i) != unicode:
                i = i.decode("iso-8859-1") 
            self.gui.tfield.insert(END, i)


    def openAFile(self):

        fh = tkFileDialog.askopenfile()

        if fh:

            try:
                text = fh.read()

                # Convert string to unicode: Tkinter.Text likes it that way:
                if type(text) != unicode:
                    text = text.decode("iso-8859-1")

                self.gui.tfield.delete("1.0", END)
                self.gui.tfield.insert("1.0", text)
                self.filename = fh.name.split("/").pop()
                fh.close()
                self.gui.mw.title(self.filename)
                self.gui.sbar.set("Text-file loaded.")

            except IOError:
                self.gui.sbar.set("Error reading text-file.")


    def saveText(self):

        text = self.gui.tfield.get("1.0", END)

        # Convert the unicode-string we got from the Tkinter-textfield:
        text = text.encode("iso-8859-1")

        try:
            fh = file(os.getcwd() + "/" + self.filename, "w")
            fh.write(text)
            fh.close()
            os.chmod(os.getcwd() + "/" + self.filename, 0666)
            self.gui.sbar.set('Text saved as "' + self.filename + '".')

        except IOError:
            self.gui.sbar.set("Error writing file !")


    def saveAs(self):

        text = self.gui.tfield.get("1.0", END)

        # Convert the unicode-string we got from the Tkinter-textfield:
        text = text.encode("iso-8859-1")

        fh = tkFileDialog.asksaveasfile(initialfile = self.filename)

        if fh:

            try:
                fh.write(text)
                self.filename = fh.name.split("/").pop()
                fh.close()
                self.gui.mw.title(self.filename)
                self.gui.sbar.set("Text saved.")

            except IOError:
                self.gui.sbar.set("Error writing file !")


    def copyToClipboard(self):

        try:
            self.myclip = self.gui.mw.selection_get()
            self.gui.sbar.set("Text copied to clipboard.")

        except:
            self.gui.sbar.set("Nothing copied.")


    def copyAll(self):

        self.gui.tfield.tag_add("all", "1.0", END)
        self.gui.tfield.tag_config("all", background = "dark grey")

        self.myclip = self.gui.tfield.get("1.0", END)

        # We need this to be able to paste to applications outside:

        self.gui.mw.clipboard_clear()
        self.gui.mw.clipboard_append(self.myclip)

        self.gui.mw.after(ms = 500, func = self.removeTagAll)
        self.gui.sbar.set("Complete text copied to clipboard.")


    def removeTagAll(self):

        self.gui.tfield.tag_delete("all")


    def pasteFromClipboard(self):

        text = ""

        try:
            text = self.gui.mw.selection_get()
        except:
            pass

        if text == "":

            self.gui.tfield.insert(INSERT, self.myclip)
            self.gui.sbar.set("Text pasted from clipboard.")

        else:

            # Convert string to unicode: Tkinter.Text likes it that way:
            if type(text) != unicode:
                text = text.decode("iso-8859-1") 

            self.gui.tfield.insert(INSERT, text)
            self.gui.sbar.set("Text pasted from outside.")


class MainWindow:

    def __init__(self, main):

        self.main = main

        if os.name == "posix":

            self.fonts = {"app" : ("Suse Sans", 16)}
            
            self.keyseqs = {"a" : 38, "c" : 54, "q" : 24, "s" : 39, "v" : 55}

        else:
            
            self.fonts = {"app" : ("Arial", 10)}
            
            self.keyseqs = {"a" : 65, "c" : 67, "q" : 81, "s" : 83, "v" : 86}

        self.mw = Tkinter.Tk()
        self.mw.option_add("*font", self.fonts["app"])
        self.mw.option_add("*tearOff", "false")
        self.mw.geometry("+61+103");
        self.mw.title(self.main.filename)

        self.tfield = ScrolledText.ScrolledText(self.mw,
                                                background = '#fff5e1',
                                                wrap = WORD,
                                                font = self.fonts["app"])

        for i in self.keyseqs:
            self.tfield.bind(sequence = '<Control-' + i + '>', func = self.bindFunc)

       # A frame containing some menubuttons:

        self.menubar = Tkinter.Frame(self.mw, relief = RIDGE, bd = 5)

        # The menubuttons

        self.mb_file = Tkinter.Menubutton(self.menubar, text = "File")
        self.mb_bearb = Tkinter.Menubutton(self.menubar, text = "Edit")
        self.mb_help = Tkinter.Menubutton(self.menubar, text = "Info")

        # Several menues associated with the menubuttons:

        self.menu_file = Tkinter.Menu(self.mb_file)

        self.menu_file.insert_command(0,
                                      label = "Open",
                                      command = self.main.openAFile)

        self.menu_file.insert_command(1,
                                      label = "Save",
                                      command = self.main.saveText)
        self.menu_file.insert_command(2,
                                      label = "SaveAs",
                                      command = self.main.saveAs)

        self.menu_file.insert_separator(3)
        self.menu_file.insert_command(4, label = "Exit", command = self.mw.destroy)
        self.mb_file.config(menu = self.menu_file)
        self.menu_bearb = Tkinter.Menu(self.mb_bearb)

        self.menu_bearb.insert_command(0,
                                       label = "Copy",
                                       command = self.main.copyToClipboard)

        self.menu_bearb.insert_command(1,
                                       label = "CopyAll",
                                       command = self.main.copyAll)

        self.menu_bearb.insert_command(2,
                                       label = "Paste",
                                       command = self.main.pasteFromClipboard)
   
        self.mb_bearb.config(menu = self.menu_bearb)

        self.menu_help = Tkinter.Menu(self.mb_help)
        self.menu_help.add_command(label = "About", command = self.showAbout)
        self.menu_help.add_command(label = "Keys", command = self.showKeys)
        self.mb_help.config(menu = self.menu_help)

        self.sbar = StatusBar(self.mw)

        self.menubar.pack(side = TOP, fill = X)
        self.mb_file.pack(side = LEFT)
        self.mb_bearb.pack(side = LEFT)
        self.mb_help.pack(side = RIGHT)

        self.tfield.pack(expand = TRUE, fill = BOTH)
        self.tfield.focus()

        self.sbar.pack(fill = X)
 
        self.sbar.set("Waiting.")


    def showAbout(self):

        tkMessageBox.showinfo(title = 'tkedit.py', message = "tkedit.py:\n\nA Primitive Text-Editor, written in 2008 by\nabgdf@gmx.net\nusing Python/Tkinter.\nLicense: GNU GPL.")

    def showKeys(self):

        tkMessageBox.showinfo(title = 'Keys', message = "Keys in tkedit.py:\n\nSave:\tCtrl + s\nCopy:\tCtrl + c\nCopyAll:\tCtrl + a\nPaste:\tCtrl + v\nExit:\tCtrl + q\n")


    def bindFunc(self, event):

        # print event.keycode

        if event.keycode == self.keyseqs["s"]:
            self.main.saveText()

        if event.keycode == self.keyseqs["a"]:
            self.main.copyAll()

        if event.keycode == self.keyseqs["q"]:
            self.sbar.set("Bye.")
            self.mw.destroy()

        if os.name == "posix":
            
            if event.keycode == self.keyseqs["c"]:
                self.main.copyToClipboard()
                
            if event.keycode == self.keyseqs["v"]:
                self.main.pasteFromClipboard()


class StatusBar(Tkinter.Frame):

    def __init__(self, master):

        Tkinter.Frame.__init__(self, master)
        self.label = Tkinter.Label(self,
                                   bd = 1,
                                   relief = SUNKEN,
                                   anchor = W)
        self.label.pack(fill = X)

    def set(self, format):
        self.label.config(text = format)
        self.label.update_idletasks()

    def clear(self):
        self.label.config(text = "")
        self.label.update_idletasks()


if __name__ == "__main__":
   app = Main()
Viele Grüße

- 28.5.2008: Encoding-Problem (hoffentlich) behoben.
- 12.11.2008: Noch ein Encoding-Problem behoben. Jetzt sollte es besser gehen :mrgreen:.
 
Oben