request_url_withoutHTTP=,canonical_url_withHTTP=,canonical_url_withoutHTTP=,request_url_withoutHTTP_realspaces=. App (Seite 1 von 3) « Tags « Blog « Bernhard Häussner
Bernhard Häussner
Tags: Artikel mit dem Tag «App» durchstöbern

Qt4 Designer und Ruby - erste Schritte

06.01.2010, 11:23

Vor Kurzem habe ich ein bisschen mit Ruby herumgespielt und festgestellt, dass es keine ordentliche und aktuelle Kurzeinführung gibt, wie man die Interfaces aus dem Qt4 Designer mit Ruby Programmen verbindet. Darum habe ich die ersten Schritte hier kurz zusammengefasst:

Als erstes installiert man die Qt4-Development-Tools und die Ruby-Qt-Komponenten mit dem Paketmanager.

Interface im Qt4 Designer erstellen

Dann kann man sich ans Buttons zusammenklicken machen. Das Beispielinterface sieht im Qt Designer so aus:

Ich habe zwei QPushButtons und ein QListWidget in Layouts gepackt. Was es an Widgets gibt und was diese können steht in der Qt4 Dokumentation. In der Ruby API heißen die Klassen stets Qt::Name stat QName. Beim Basteln wichtig ist, dass ObjectName irgendwie Sinn hat, weil damit die Widgets dann benutzt werden können.

Rubycode mit rbuic erstellen

Als nächstes muss man die vom Designer erzeugte XML-Datei mit der Endung .ui in Ruby-Code umwandeln. Dazu gitb es rbuic bzw. rbuic4:

rbuic4 Sample.ui -o Sample.ui.rb

Jetzt haben wir ein Rubyscript in dem das Interface erstellt wird.

Interface in Rubyscript einbinden

Jetzt ist es Zeit dem Interface etwas Leben einzuhauchen. Dazu verbindet man die Signale - wer Webinterfaces Programmiert kennt das als „Events“ - mit den Entsprechenden Slots, die so ähnlich wie callbacks funktionieren. Das geht recht fix mit QMetaObject::connectSlotsByName. Damit dies funktioniert müssen die Funktionen, die aufgerufen werden sollen das Namensmuster on_ObjectName_Signal einhalten, wobei der ObjectName vorher im Designer festgelegt wurde und das Signal heißt z.B. triggered.

Wie man die GUI-Komponente in der Anwendung benutzt, z.B. hier durch Subclassing, und anderes will ich direkt am Beispiel erklären:

#!/usr/bin/ruby

#Laden der Qt-Klasse:
require 'Qt'

#Manchmal auch:
#require 'Qt4'

#Laden des generierten Interfaces:
require 'Sample.ui.rb'

#Initialisieren der Anwendung
app = Qt::Application.new ARGV

#Eine Klasse, um das Verhalten des Interfaces festzulegen:
class AppCommunicate < Qt::MainWindow
  #Die Liste der Slots
  #Sie werden von setupUi automatisch verbunden
  slots :on_actionQuit_triggered, :on_add_clicked, :on_rem_clicked
  def initialize
    puts "init"
    @item_count=0
    #Da wir vererben initialisieren wir so:
    super
    #Jetzt erstellen wir das Interface
    @ui=Ui_MainWindow.new
    #Und weisen es dem MainWindow zu
    @ui.setupUi self
    #Man könnte Slots auch manuell verbinden:
    #connect a.actionQuit, SIGNAL('triggered()'), $qApp, SLOT('quit()')
  end
  #Ein Handler für das Beenden:
  def on_actionQuit_triggered
    puts "Shutting down."
    #So wird die Anwendung gestoppt:
    $qApp.quit
  end
  # "add" ist der name des Buttons, bei click wird dieses signal gesendet
  def on_add_clicked
    puts "Adding item"
    #Es gibt attr_readers für alle Widgets
    #So können wir ihre Methoden aufrufen
    @ui.listWidget.addItem("Hallo Welt ##{@item_count} at #{Time.now.ctime}")
    #Hier könnten z.B. Daten der Anwendung ausgegeben werden
    #Zu demonstrationszwecken nummerieren wir die Einträge sichtbar
    @item_count+=1
  end
  def on_rem_clicked
    puts "Removing item"
    #In cpp könten wir die items deleten
    #Aber in ruby loopen wir über die markierten indizes
    @ui.listWidget.selectedIndexes.each do |i|
      #und nehmen sie aus der Liste
      @ui.listWidget.takeItem i.row
      #da wir keine Referenz speichern löscht sie der GC
    end
  end
end

#Wir erstellen eine Instanz unseres Verhaltens
wdg=AppCommunicate.new do |w|
    #Wir müssen das Interface zeigen
    #Wird auch alle child-widgets zeigen
    w.show
end

#Hier wird die GUI-Schleife gestartet:
app.exec

#TADA!

Wer das ganze ausprobieren will, kann sich die Beispieldateien hier herunterladen:

Ich hoffe das war hilfreich, ich finde es doch hin und wieder ganz nett ein Interface zu haben, vor allem für Scripte, die länger laufen und interaktiv sein sollen.

Kommentare: keine

Symmetrisch Zeichnen

17.12.2009, 10:43

Neben Symmetrie in der Natur bin ich auch begeistert von den Symmetrien, die der Computer erstellen kann. Und was wäre diese Symmetrie ohne die, für den Computer typische, Interaktivität? Also habe ich (zunächst mit Processing aber dann mit) HTML5-Canvas eine kleine Javascript-Sache gebastelt, mit der man direkt im Browser symmetrisch zeichnen kann.

Irgendwie macht das Spaß und, naja, es sieht auch oft erstaunlich gut aus.

[Updated 2011-09-29] Da mir das einfache herumzeichnen schnell zu langweilig wurde, habe ich das Script immmer weiter ausgebaut. Deswegen finden sich unter dem Link nun mehrere Variationen, die es sich auszuporbieren lohnt![/Updated]

Zum Zeichenbrett

Und hier noch ein Video:


Drawing Symmetry (6)

Viel Spaß beim Malen!

Kommentare: 2 Einträge

XMPP (Jabber) mit Ruby am Beispiel FelicianXMPPBot

15.12.2009, 21:53
FelicianXMPPBot in action

FelicianXMPPBot in action

Das Protokoll XMPP für den (Nahezu-)Echtzeitaustausch von XML-Nachrichten (meist verwendet für IM, als „Jabber“) steht in Konkurrenz und Verbindung mit einigen aktuell mehr oder weniger neuen Technologien, die mit XML und realtime zu tun haben, wie RSS/Atom pings, realtime web oder AJAX. Und ein paar Spielereien auf diesem Gebiet können sicher nicht schaden, also habe ich mit XMPP etwas herumgebastelt.

Weil dies mit PHP irgendwie seltsam wäre und David Strohmayers Text und Präsentation über XMPP mit Java noch nicht existierte, habe ich das ganze mit Ruby gebastelt, da gibt es nämlich den Gem xmpp4r, der mit vielen Beispielen kommt. So konnte ich mal ein bisschem mit Ruby herumspielen. (Zwei Fliegen mit einer Klappe usw.)

Aus den Experimenten ist ein kleiner Server geworden, der einen Jabber-Bot betreibt und über ein einfaches TCP-Protokoll erreicht werden kann, sodass aus allen TCP-Fähigen Skripten (bash mit netcat, PHP…) einfach Nachrichten an einen Jabber-Endpunkt versendet werden können.

Code und Funktionalität bestehen aus drei Teilen:

Die Klasse Netx kümmert sich um den TCP-Server und parst das einfache Protokoll: Man schickt Zeilen und beendet einzelne Nachrichten mit einem Punkt, und die Verbindung (und damit auch die aktuelle Nachricht) mit zwei Punkten. So lassen sich die TCP-Clients leicht implementieren.

Die Klasse XmppRoute bietet nur ein wenig Grundfunktion für den Jabber-Bot. Sie vereinfacht Verbinden und Senden noch weiter, zudem extrahiert sie beim Empfang einer Nachricht einen Befehlsteil.

Der Rest kümmert sich um die Verbindung der beiden Komponenten und um das Parsen von Startup-Parametern.

Aber genug der vielen Worte, jetzt folgt der Code:

jabot.rb

#!/usr/bin/ruby

# Felician 0.9 - XMPP Wrapper
# (c) 2009 by Bernhard Häussner
# Licence: GPLv2+ http://www.gnu.org/licenses/gpl-2.0.txt
#
# This jabber wrapper will recieve messages via simple tcp.
#
# Quit by sending "exit" via jabber. 
# Send "info" via jabber to get running parameters. 
# Send "feedback [msg]" via jabber to post from jabber. 
# Connect to specified port and send messages terminated by lone "." in a line to post via TCP. 
# Send a line with ".." to quit connection. 
#
# Based on xmpp4r-0.5/data/doc/xmpp4r/examples/basic
#

require 'optparse'
require 'XmppRoute'
require 'Netx'

class FelicianXMPPBot
  def start(his_JID,my_txt_JID,my_password,listen_port)
    puts "FELC: Started. Sending to #{his_JID} from #{my_txt_JID} listening to #{listen_port}."
    my_JID=JID.new(my_txt_JID)
    netx=Netx.new
    XmppRoute.connect(my_JID,my_password) do
      on_recieve do |from,cmd,param|
        if from.strip.to_s.eql?his_JID then
          case cmd.downcase
            when 'exit' then
              send(from,"-> I've got to go!")
              puts "FELC: Got shutdown signal, waiting for children to terminate..."
              netx.halt
            when 'feedback' then send(from,param)
            when 'info'then send(from,"-> Felician sending to #{his_JID} listening to #{listen_port}. ")
            else send(from,"-> I don't know: #{cmd} #{param}")
          end
        else
          puts "XMPP: Ignored a message. "
          send(from,"Hi, #{from.strip.to_s}. Sorry, could not be deliverd. Try again later! ")
        end
      end
      
      netx.on_sended do |msg|
        send(his_JID,msg.strip)
        puts "XMPP: Client message sent. "
      end
      netx.listen(listen_port)
    end
    puts "FELC: Shutting down. "
  end
end

if $0 == __FILE__
  #defaults:
  listen_port=2000
  his_JID = "bot_admin@gmx.de"
  my_txt_JID = "i_am_a_bot@jabber.ccc.de/felician"
  my_password = "top_secret"
  # cli settings
  OptionParser.new do |opts|
  opts.banner = 'Usage: ruby jabot.rb -j jid/ressource -p password -t reciever_jid -l port'
  opts.separator ''
  opts.on('-j', '--jid JID', 'sets the sender jid') { |j| my_txt_JID = j }
  opts.on('-p', '--password PASSWORD', 'sets the sender password') { |p| my_password = p }
  opts.on('-t', '--to-jid JID', 'sets the message reciever') { |t| his_JID = t }
  opts.on('-l', '--listen PORT', 'sets the server listen port') { |l| listen_port = l }
    opts.on_tail('-h', '--help', 'Show this message') {
    puts opts
    exit
  }
  opts.parse!(ARGV)
  end
  FelicianXMPPBot.new.start(his_JID,my_txt_JID,my_password,listen_port)
end

XmppRoute.rb

require 'rubygems'
require 'xmpp4r/client'
include Jabber

class XmppRoute
  def initialize(jid,pwd)
    @cmd=Proc.new { |a,b,c| puts "XMPP: No reciever. " }
    @cl = Client.new(jid)
    @cl.connect
    @cl.auth(pwd)
    @cl.send(Presence.new)
    puts "XMPP: Connected! send messages to #{jid.strip.to_s}."
    @cl.add_message_callback { |m| process m }
  end
  def close
    @cl.close
  end
  def on_recieve(&cb)
    @cmd=cb
  end
  def send(to,mes)
    ms = Message.new(to,mes) # [_CY4_L8-R_4LLY-G4T3-R_]
    ms.type = :chat
    @cl.send(ms)
  end
  private
  def process(m)
    puts "XMPP: Got msg of type #{m.type} / #{m.name} with -#{m.body}- "
    if m.type == :chat && ! m.body.nil?
      index=(m.body.index(" ") || 0)-1
      com=m.body[0..index]
      param=(index==-1?"":m.body[index+2..-1])
      puts "XMPP: Call #{com} with --#{param}-- (caused from #{m.from})"
      @cmd.call(m.from,com,param)
    end
  end
  def self.connect(jid,pwd,&block)
    bot=XmppRoute.new(jid,pwd)
    bot.instance_eval(&block)
    bot.close
  end
end

Netx.rb

require 'socket' # Get sockets from stdlib

class Netx
  def initialize; @run=true; end
  def listen(port)
    puts "NETX: Bind to Port #{port}"
    #server = TCPServer.open(port)
    #only local:
    server = TCPServer.open("127.0.0.1",port) # Socket to listen
    while @run
      # this timeout allows us to kill this lop every few seconds...
      begin
        session = Timeout::timeout(10) { server.accept }
        Thread.start(session) { |c| connection c }
      rescue TimeoutError
        #puts "NETX: Refreshing TCPServer."
      end
    end
  end
  def on_sended(&cb); @client_cb=cb; end
  def halt; @run=false; end
  private
  def connection (client)
    puts "NETX: Client conntected..."
    client.puts("-- Hello on Felician at #{Time.now.ctime}. ") # Send the helo & time to the client
    conn_open=true;
    msg_buffer="";
    while conn_open do
      l = client.gets
      if (l.strip.eql?'..') || (l.strip.eql?'.') then
        puts "NETX: Client issued a message..."
        client.puts("-- accepted. ")
        @client_cb.call(msg_buffer)
        msg_buffer="";
        conn_open=false if l.strip.eql?'..'
      else
        msg_buffer << l
      end
    end
  ensure
    client.puts "-- Closing the connection. Bye!"
    puts "NETX: Client disconnect. "
    client.close # Disconnect from the client
  end
end

Erwartet nicht zu viel von dem Code, ist schließlich mein erster Ruby-Code, abgesehen von einigen Hello-World-Sachen. Funktioniert dafür allerdings erstaunlich gut und könnte sich schon fast eigenen, um z.B. einen Systemadministrator über wichtige Aktivitäten und Warnungen aus den verschiedensten Programmen und Wartungsscripten schnell zu informieren.

Man beachte vielleicht noch das alles in dem Code, was in anderen Programmiersprachen als schlechter Stil gälte, hier eher „the ruby way“ ist ;).

Benutzung

Eine Nachricht von der Shell schicken:

09:15  burny@home:~> echo -e "Hallo Welt\n.." | netcat localhost 2000
-- Hello on Felician at Tue Dec 15 21:16:02 2009.
-- accepted.
-- Closing the connection. Bye!

Mal sehen, ob ich das Ganze noch weiter entwickle, meistens ist doch das Senden einer ganz normalen eMail einfacher.

Kommentare: 2 Einträge

Javascript Library Builder

31.07.2009, 13:07
JS-Build

JS-Build

Es gibt einige Methoden Javascript auf HTML-Seiten einzubinden. Ein Block im <body>, in onclick-Attributen und ähnlichem, oder als Verknüpfungen im <head> aber am besten in einer externen Datei, die nach dem HTML-Dokument geladen wird. Nur will man beim Entwickeln die einzelnen Javascript-Teile meist in getrennte Dateien legen und auf der Seite dann möglichst viel in eine (am besten möglichst kleine) Datei packen, um Requests zu sparen. Dieses Problem kann der Javascript Library Builder lösen, ein recht kurzes Shell-Script, welches eine Sammlung von .js-Dateien in wenige Dateien minimiert.

Man nehme zum Beispiel eine solche Dateistruktur:

lib/
  php/...
  js_frontend/
    00_jQuery.js
    01_jQ_plugin.js
    02_effekte.js
    licence.txt
  js_backend/
    03_ajaxCalls.js

Der Scriptaufruf wäre so einfach wie:

jslibbuild.sh -o build lib/js* 

Ich habe auf dieser Seite 27 einzelne Dateien, die ich in zwei Javascript Dateien packe, nämlich für die Seite und für das CMS. Diese Beispiel-Dateistruktur würde jetzt in den zwei Dateien js_frontend.min.js und js_backend.min.js im Ordner build resultieren. Der Datei js_frontend.min.js würde zusätzlich nach dem minimieren mit jsmin die Datei licence.txt vorgehängt, sodass z.B. in Kommentaren (die von jsmin geschluckt werden) vermerkte Autorenhinweise erhalten bleiben, was unter anderem bei den CC-Lizenzen nötig ist.

Dazu akzeptiert das Script die optionale Option -l nach der der Name der Lizenz-Dateien angegeben wird. Eine weitere optionale Option ist -c, um das build-Verzeichnis vor dem Anlegen der neuen Dateien zu löschen. Das ist nötig, wenn ein ganzer source-Ordner gelöscht wird, aber dringend zu vermeiden, wenn weitere Dateien im build-Ordner liegen. Hier noch ein Beispiel:

jslibbuild.sh -c -l gpl.txt -o /srv/www/htdocs/js ~/web/lib/js/* 

Das Script benötigt jsmin, steht unter GPLv2+ und kann hier heruntergeladen werden:

Zum „installieren“ einfach in den Ordner ~/bin kopieren. Das Script ist eigentlich ziemlich einfach, kann aber doch einiges an Arbeit ersparen.

Kommentare: keine

Twitter Bookmarklet III

11.07.2009, 20:16

For twitter users: Here's an easy-to-use toolbar button for your browser that substitutes the URL shortening and copy-pasting for you. There's no installation, I don't want to know your twitter login or password, and it will shorten the URL using bit.ly.

The bookmarklet will:

  • Receive a short URL from the bit.ly API
  • Open up your twitter homepage in a new tab (Firefox) or in a pop up with the short URL and the page title included in the textarea
  • (Or take you to the twitter login page, which will then redirect)

To install just drag this link/button into your browser's bookmarks toolbar:

tweet

The bookmarklet is even supporting flickr-Photos (using their short URL like http://flic.kr/p/6qxGR2) and other web sites (Ars technica, PHP-Manual, this blog...) if they provide own shortening services.

The code is a bit inspired by John Resig's „retweet“ button but it's not on the page you are viewing, but in your browser's toolbar, so you don't rely on webmasters cluttering their designs with social bookmarking services' buttons.

For IE-Users: You will have to right-click on the link and choose save bookmark. After that, you will be warned, because it's not a simple link, but a bookmarklet.

Kommentare: 2 Einträge
[ Seite 1 2 3 ] Nächste Seite »
 
Χρόνογραφ
© 2008-2017 by Bernhard Häussner - Impressum - Login
Kurz-Link zur Homepage: http://1.co.de/