Double-Action/Click Event

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

Grüße

Ich hab mir mal ein script gebastelt, mit dem ich eine Art Double Click für Funktionen realisieren möchte.
Gibt es auch wege das sinnvoller so lösen?

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

from threading import Thread
import time

class Clicker():
    def __init__(self):
        self.click_time = time.time() - 1
        self.single_event = True
        self.__double_click_delay = 0.3

    def click(self):
        t = time.time()
        if t - self.click_time < self.__double_click_delay:
            self.single_event = False
            return 2
        else:
            self.click_time = time.time()
            time.sleep(self.__double_click_delay)
            if self.single_event:
                return 1
            else:
                self.single_event = True
                return 0

def as_thread(function, params=[]):
    thread = Thread(target=function, args=params)
    thread.start()

def click_event(singleevent,doubleevent):
    temp = click1.click()
    if temp == 2:
        doubleevent()
    elif temp == 1:
        singleevent()


def single():
    print 'single'

def double():
    print 'double'

def main():
    as_thread(click_event,[single,double])
    time.sleep(0.250)
    as_thread(click_event,[single,double])


click1 = Clicker()

if __name__ == '__main__':
    main()

empty Sig
Benutzeravatar
pillmuncher
User
Beiträge: 1532
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Für sowas würde ich evtl. asyncio oder RxPy verwenden, aber selbstgestrickt geht es auch:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import Queue
import threading
import time


class Clicker:

    def __init__(self, double_click_interval, single_func, double_func):
        self._queue = Queue.Queue()
        latch = threading.Event()

        def handle_clicks():
            latch.set()
            while True:
                self._queue.get()
                try:
                    self._queue.get(True, double_click_interval)
                except Queue.Empty:
                    single_func()
                else:
                    double_func()

        thread = threading.Thread(target=handle_clicks)
        thread.daemon = True
        thread.start()
        latch.wait()

    def click(self):
        self._queue.put(None)


def single():
    print 'single'


def double():
    print 'double'


def main():
    clicker = Clicker(.3, single, double)
    clicker.click()
    clicker.click()
    clicker.click()
    time.sleep(0.250)
    clicker.click()
    clicker.click()
    time.sleep(0.350)
    clicker.click()


if __name__ == '__main__':
    main()
    time.sleep(1)
Das noch:

1) statt /usr/bin/python direkt in die shebang-Zeile zu schreiben, ist es besser, /usr/bin/env python zu verwenden. Dann wird auch in einem virtuellen Environment das richtige Python verwendet, statt immer das aus dem /usr/bin Verzeichnis.

2) die leeren Klammern hinter dem Klassennamen bei deiner Klassendefinition haben keinerlei Funktion. Lass sie weg.

3) doppelte führende Unterstriche bei einem Attributnamen bedeuten nicht, dass das ein privates Attribut ist. So etwas gibt es in Python überhaupt nicht. Statt dessen gibt es die Konvention, dass der Name eines Attributs, das ein Implementationsdetail ist, mit einem einzelnen führenden Unterstrich begimmt. Zwei führende Unterstriche (ohne zwei abschließende solche) führen zum sog Name Mangling. Mehr dazu hier.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

Ergänzend zu 2): Bei Python 2 die Klammern nicht weglassen, sondern ``object`` dort hineinschreiben, denn wenn man von nichts erbt, bekommt man eine ”old style”-Klasse bei der nicht alles funktioniert was Python so bietet.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

echt merkwürdig, e-mail benachrichtigungen bleiben schon eine ganze zeit aus....
danke für die überarbeitung, dass probiere ich gleich mal aus.

zu 2. ja da hab ich nicht ganz aufgepasst, hatte bei in tests davor noch thread drin stehen.

zu 3. das mit den zwei unterstrichen hatte ich aus der doku zu threading. dass unterstriche nix bewirken, im tatsächlichen sinne, weiß ich. aber trotzdem weg lassen, ok. aber pycharm ist so schlau und bietet mir dieses attribut nicht an, wenn ich "click1." eintippe.
empty Sig
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

Kann ich meine funktionen zum ausführen in eine klasse packen und übergeben?
derzeit läufts auf nen fehler.
TypeError: unbound method double() must be called with events instance as first argument (got nothing instead)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import Queue
import threading
import time


class Clicker:

    def __init__(self, double_click_interval, single_func, double_func):
        self._queue = Queue.Queue()
        latch = threading.Event()

        def handle_clicks():
            latch.set()
            while True:
                self._queue.get()
                try:
                    self._queue.get(True, double_click_interval)
                except Queue.Empty:
                    single_func()
                else:
                    double_func()

        thread = threading.Thread(target=handle_clicks)
        thread.daemon = True
        thread.start()
        latch.wait()

    def click(self):
        self._queue.put(None)

class events:

    def single(self):
        print 'single'

    def double(self):
        print 'double'


def main():
    clicker = Clicker(0.3, events.single, events.double)
    print '1st click'
    clicker.click()
    print '2nd click'
    clicker.click()

    time.sleep(0.350)

    print '3rd click'
    clicker.click()

if __name__ == '__main__':
    main()
    time.sleep(1)
  
oder sollte ich einfach nur meine funktionen umbenennen?

Code: Alles auswählen

def event_single(self):
    print 'single'

def event_double(self):
    print 'double'
Zuletzt geändert von harryberlin am Sonntag 14. Februar 2016, 11:57, insgesamt 1-mal geändert.
empty Sig
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

haha, bin grad selbst auf die lösung gekommen.
musste die classe noch in eine instanz setzen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import Queue
import threading
import time


class Clicker:
    def __init__(self, double_click_interval, single_func, double_func):
        self._queue = Queue.Queue()
        latch = threading.Event()

        def handle_clicks():
            latch.set()
            while True:
                self._queue.get()
                try:
                    self._queue.get(True, double_click_interval)
                except Queue.Empty:
                    single_func()
                else:
                    double_func()

        thread = threading.Thread(target=handle_clicks)
        thread.daemon = True
        thread.start()
        latch.wait()

    def click(self):
        self._queue.put(None)


class events:
    def single(self):
        print 'single'

    def double(self):
        print 'double'

event = events()

def main():
    clicker = Clicker(0.3, event.single, event.double)
    print '1st click'
    clicker.click()
    print '2nd click'
    clicker.click()

    # time.sleep(0.350)

    print '3rd click'
    clicker.click()


if __name__ == '__main__':
    main()
    time.sleep(1)

empty Sig
BlackJack

@harryberlin: `events` sollte mit einem grossen `E` anfangen. Das ist aber auch semantisch gar keine Klasse, sondern zwei einzelne Funktionen, dementsprechend sollten die auch nicht syntaktisch in einer Klasse stecken.

Der Fehler, der zu der Ausnahme führt, ist das Du kein Exemplar von `events` erstellt hast, sondern direkt die ungebundenen Methoden angibst, und da fehlt dann beim Aufruf das Exemplar als erstes Argument. Wenn die Methoden nicht von der Klasse sondern von einem Exemplar kommen, dann sind sie ja an dieses Exemplar gebunden, dass heisst es wird automatisch als erstes Argument übergeben. Was, wie im letzten Absatz schon erwähnt, allerdings keinen Sinn macht, weil man das hier überhaupt gar nicht braucht. Wenn man `self` nicht benutzt, sollte man dafür eine nachvollziehbare Erklärung haben, sonst macht man etwas falsch.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

Stimmt, es war nicht groß geschrieben. Verzeih mir den Fehler.

Als Idee hätte ich jetzt noch ein dictionary. Aber da wäre es mir am liebsten, wenn ich die funktion direkt darin schreiben könnte.
das simple print war nur zum testen.
Hintergrund ist, ich möchte Funktionen, die aus meiner sicht zusammen gehören, irgendwie gebündelt sind.
ein script zum ewig langen scrollen find ich umständlich. in einer art klasse geht es noch, weil man die in pycharm einklappen kann.
empty Sig
BlackJack

@harryberlin: Namensräume für Funktionen sind Module/Packages und nicht Klassen. Wobei Du natürlich Klassen verwenden kannst und solltest, wenn dort wirklich zusammengehöriger Zustand *und* Operationen zusammengefasst werden.
Antworten