Threading notwendig?

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Handas
User
Beiträge: 7
Registriert: Freitag 17. Februar 2017, 23:32

Freitag 17. Februar 2017, 23:39

Moin,

Ich hab ein Raspberry pi 2 mit 2 Temperatursensoren. Beide Sensoren sind angeschlossen, laufen und geben Werte zurück.
Beim Python Programm hat momentan nur einen Button zum aktualisieren und 2 editFields zum Anzeigen der Werte.
Die GUI wurde mit Glade entworfen.
Solange ich den Button zur manuellen Dateiabfrage nutze, funktioniert auch alles.
Wenn ich aber automatisch (alle 10s) die Werte aktualisieren möchte, hängt sich die Oberfläche auf und ich bekomme keine werte.

Denn Intervall habe ich durch eine Endlosschleife und einem time.sleep realisiert.
Muss ich hier vielleicht einen neuen Thread benutzen, damit das funktioniert? Oder geht das auch simpler?

Gruß
Handas
BlackJack

Samstag 18. Februar 2017, 00:05

@Handas: Das geht auch ohne Thread mit `timeout_add()`.
Handas
User
Beiträge: 7
Registriert: Freitag 17. Februar 2017, 23:32

Samstag 18. Februar 2017, 17:03

Gobject konnte ich soweit erfolgreich importieren. Aber beim Aufruf haperts noch.

Beim Versuch timeout_add_seconds aufzurufen, bekomme ich folgende Meldung: "Argument 2 does not allow none as Value".
Wie würden denn ein Aufruf aussehen, bei dem zum Beisspiel alle 5 sek ein print('Test') ausgeführt werden soll?


Gruß
Handas
BlackJack

Samstag 18. Februar 2017, 18:26

@Handas: Na dann solltest Du nicht `None` als zweites Argument übergeben. Kann es sein das Du da nicht die Funktion oder Methode übergeben hast, sondern sie *aufgerufen* hast und damit deren Rückgabewert als zweites Argument übergeben hast?
Handas
User
Beiträge: 7
Registriert: Freitag 17. Februar 2017, 23:32

Samstag 18. Februar 2017, 19:31

Wie kann ich den die Funktion übergeben, ohne sie aufzurufen?
BlackJack

Samstag 18. Februar 2017, 20:00

@Handas: Äh, indem Du sie nicht aufrufst? Aufruf sind diese runden Klammern, potentiell mit Argumenten‽ Nicht machen! Nur die Funktion übergeben.

Code: Alles auswählen

In [30]: print(print)  # Yes!
<built-in function print>

In [31]: print(print())  # Noooo!

None
Wenn Deine Funktion Argumente hat, dann musst Du eine ohne erstellen die das macht was Du willst. Entweder mit ``def`` eine schreiben, oder beispielsweise bei einer vorhandenen Funktion Argumente mit `functools.partial()` binden. Allerdings muss für regelmässiges Aufrufen von der Hauptschleife aus, die Funktion ja einen passenden Rückgabewert haben:

Code: Alles auswählen

In [32]: GObject.timeout_add_seconds?
Docstring:
timeout_add(interval, callable, user_data=None,
            priority=None) -> source_id
  callable receives (user_data)
Sets a callable be called repeatedly until it returns False.
Use this if you want to have a timer in the "seconds" range
and do not care about the exact time of the first call of the
timer, use this for more efficient system power usage.
Type:      builtin_function_or_method
Handas
User
Beiträge: 7
Registriert: Freitag 17. Februar 2017, 23:32

Samstag 18. Februar 2017, 20:07

ahh ok. das hilft mir weiter. Danke dafür.

Ich habe hauptsächlich mit Delphi zu tun und dort gibt es nur Funktionsaufrufe und keine Verweise

Gruß
Handas
BlackJack

Samstag 18. Februar 2017, 21:23

@Handas: Kann ich mir kaum Vorstellen. Wie laufen den dort Rückruffunktionen? Letztlich ist Delphi ja ObjectPascal und Pascal kennt Prozedur- und Funktionstypen.
BlackJack

Samstag 18. Februar 2017, 22:01

So sieht das in (Free)Pascal aus:
[codebox=delphi file=Unbenannt.pas]program Test;

uses glib2;

var loop: PGMainLoop;

function PrintTest(userData: GPointer): GBoolean; cdecl;
begin
WriteLn('Test');
PrintTest := True;
end;

begin
loop := g_main_loop_new(nil, False);
g_timeout_add(1000, @PrintTest, nil);
g_main_loop_run(loop);

{Somewhat pointless because the mainloop is never quit.}
g_main_loop_unref(loop);
end.[/code]
Handas
User
Beiträge: 7
Registriert: Freitag 17. Februar 2017, 23:32

Samstag 18. Februar 2017, 23:11

Was dazu gelernt :D
Aber in Delphi hätte ich die Timer Klasse gehabt. Damit wäre das alles ein wenig leichter zu relasieren
BlackJack

Samstag 18. Februar 2017, 23:27

@Handas: Wie kann denn das mit einer Klasse einfacher werden als *ein* Funktionsaufruf?
Handas
User
Beiträge: 7
Registriert: Freitag 17. Februar 2017, 23:32

Samstag 18. Februar 2017, 23:47

Timer importieren;
timer.intervall:=1000;
timer.enabled:=true;
Programmablauf in der automatisch erzeugten Timer1timer Funktion klatschen;

Das kann ich persönlich leichter nachvollziehen
BlackJack

Sonntag 19. Februar 2017, 00:31

@Handas: Wenn ich das so verkürzt darstelle wie Du den Timer, dann bleibt von meinem genau das hier übrig:

Code: Alles auswählen

  g_timeout_add(1000, @PrintTest, nil);
Und dann muss man halt noch die `PrintTest`-Funktion schreiben. Konzeptionell einfacher und weniger Code würde ich mal sagen.

Wenn Du unbedingt mit Klassen arbeiten möchtest, dann würde ich Java empfehlen. ;-)
Handas
User
Beiträge: 7
Registriert: Freitag 17. Februar 2017, 23:32

Sonntag 19. Februar 2017, 10:46

Java ist mir zu überladen.

Vielleicht erscheint der timer mir auch nur leichter wegen der Gewohnheit.

Jetzt funktioniert ja alles. Danke dafür 8)
BlackJack

Sonntag 19. Februar 2017, 13:54

@Handas: In Java wird einem viel von der IDE abgenommen und dieses konkrete Problem, in einer GUI-Anwendung alle x Sekunden etwas ausgeben, ist mit `javax.swing.Timer` ein Einzeiler:
[codebox=java5 file=Unbenannt.java] new Timer(1000, e -> System.out.println("Test")).start();[/code]
Das war früher mal schlimm, als es noch keine Lambda-Syntax in Java gab und man das noch so schreiben musste:
[codebox=java5 file=Unbenannt.java] new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Test");
}
}).start();[/code]

Oder mal in ein minimales Programm verpackt in dem noch ein Dialogfenster angezeigt wird damit die GUI-Schleife läuft und man es beenden kann:

Code: Alles auswählen

import javax.swing.*;

public class Main {
    public static void main(String[] args) {
        new Timer(1000, e -> System.out.println("Test")).start();
        JOptionPane.showMessageDialog(null, "Press OK button to end this.");
    }
}
Antworten