Je sens le passage du temps avec Emacs-Qiita J'ai senti que l'idée était très bonne, mais malheureusement je n'étais pas un utilisateur d'Emacs, alors quand Je ne pouvais pas sentir le flux de.
Afin de ressentir moi-même le passage du temps, j'ai créé une application de type accessoire de bureau avec le Java familier. J'ai reçu la permission de l'auteur original, @zk_phi, de le publier, je voudrais donc le présenter ici.
Vous pouvez obtenir le fichier jar pré-construit (littlesky.jar
) depuis ici.
Le code source est posté sur GitHub> https://github.com/opengl-8080/little-sky
OS Nous avons confirmé que cela fonctionne sur Windows 10.
Je n'ai pas d'environnement Mac sous la main, donc je ne sais pas si cela fonctionnera sur un Mac. Je ne vais rien écrire qui dépend du système d'exploitation, mais si cela ne fonctionne pas sur votre Mac, veuillez faire une pull request.
JRE Il devrait fonctionner sur Java 8u40 et supérieur (car vous utilisez Dialog) .. J'ai vu que cela fonctionnait également sur Java 9.
$ java -jar littlesky.jar
Le fichier de configuration est enregistré dans le répertoire courant lors de l'exécution (littlesky.xml
).
Le deuxième démarrage et les suivants font référence au fichier de paramètres dans le répertoire en cours. Si vous souhaitez modifier l'emplacement de démarrage, déplacez également le fichier de configuration.
Au premier démarrage, une boîte de dialogue de saisie des informations de localisation s'affiche.
Entrez la latitude («Latitude») et la longitude («Longitude»). (Utilisé pour obtenir les heures de lever / coucher du soleil et les informations météorologiques)
Comme pour l'original, l'icône du ciel (icône vide), l'heure et les informations de température s'affichent. L'icône du ciel montre la phase lunaire lorsqu'il fait beau et le temps lorsqu'il pleut ou qu'il neige.
La couleur de fond change en fonction du temps.
Faites un clic droit sur l'heure pour afficher le menu contextuel. Vous pouvez fermer l'application et modifier les paramètres.
Comme pour l'original, vous pouvez utiliser la fonction météo en utilisant OpenWeatherMap.
Obtenez la clé API d'OpenWeatherMap.
La clé API peut être obtenue en enregistrant un compte avec OpenWeatherMap. Si vous recherchez "Clé API Open Weather Map", vous trouverez diverses informations.
Sélectionnez Options dans le menu contextuel pour afficher un formulaire de saisie des clés API.
Entrez la clé API obtenue dans [Clé API] et cliquez sur le bouton [Enregistrer] pour enregistrer les paramètres.
Après avoir entré la clé API, vous pourrez sélectionner Service météo> Démarrer dans le menu contextuel. Sélectionnez ceci pour activer la fonction météo.
Une fois toutes les 15 minutes, les informations sur la météo, le volume des nuages et la température seront acquises en fonction des informations de position définies et reflétées sur l'écran.
Une fois que vous avez défini la clé API, la fonction météo démarre automatiquement la prochaine fois que vous démarrez l'application.
Si vous souhaitez arrêter la fonction météo, sélectionnez Service météo> Arrêter dans le menu contextuel ou videz la clé API et enregistrez.
Dans un environnement où un proxy est requis pour accéder à Internet, il est nécessaire de définir le proxy.
Ouvrez la boîte de dialogue des paramètres avec Options dans le menu contextuel.
Dans Proxy HTTP, entrez les paramètres du proxy.
Si aucun port n'est entré, «80» est utilisé par défaut. Saisissez le nom d'utilisateur et le mot de passe uniquement si le proxy requiert une authentification.
littkesky.xml
).Vous pouvez modifier les paramètres d'affichage depuis [Affichage] dans le menu contextuel.
Si vous cochez [Toujours en haut], la fenêtre sera toujours au premier plan du bureau. La valeur par défaut est désactivée.
En commutant la vérification de [Afficher les secondes], vous pouvez afficher / masquer les secondes de temps. La valeur par défaut est activée.
En commutant le contrôle [Afficher la température], vous pouvez afficher ou masquer la température. La valeur par défaut est activée.
En commutant la vérification de [Afficher l'icône d'état du ciel], vous pouvez changer l'affichage / non-affichage de l'icône du ciel (phase mensuelle / icône météo). La valeur par défaut est activée.
J'ai été signalé par Bukome, et j'ai trouvé que c'était un peu mauvais, donc je suis désolé de l'ajouter plus tard, mais j'aimerais résumer ce que j'ai appris en faisant cette application.
package sample.javafx;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class NoFrameWindow extends Application {
public static void main(String[] args) {
launch(NoFrameWindow.class, args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Pane pane = new Pane();
pane.setPrefWidth(200);
pane.setPrefHeight(100);
pane.setStyle("-fx-background-radius: 50; -fx-background-color: yellow;");
Scene scene = new Scene(pane);
scene.setFill(Color.TRANSPARENT);
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
}
}
** Résultat d'exécution **
La description
StageStyle.TRANSPARENT
dans style
de Stage
.Scene
n'est pas transparent.
--Si Scene
n'est pas transparent, même si vous arrondissez les coins du nœud, il restera carré en apparence.Color.TRANSPARENT
avec setFill ()
pour rendre la Scène
transparente
--Ensuite, si vous arrondissez les coins du nœud enregistré dans Scene
, vous pouvez créer une fenêtre sans cadre de fenêtre de quelque forme que ce soit, comme indiqué dans ↑.-fx-background-radius
en CSS.
--Si vous le définissez dans l'implémentation, il ressemblera probablement à ↓Définir l'arrière-plan par programmation
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.paint.Color;
...
BackgroundFill backgroundFill = new BackgroundFill(Color.YELLOW, new CornerRadii(50), null);
Background background = new Background(backgroundFill);
pane.setBackground(background);
Si vous supprimez le cadre de la fenêtre, vous ne pouvez pas faire glisser la fenêtre telle quelle. Cette mise en œuvre vous oblige à faire de votre mieux par vous-même.
Il y a des questions similaires lors de la recherche de Stackoverflow, et la méthode d'implémentation est également introduite, je vais donc l'utiliser telle quelle.
Moving an undecorated stage in javafx 2 - Stack Overflow
package sample.javafx;
...
public class NoFrameWindow extends Application {
...
private double mouseX;
private double mouseY;
@Override
public void start(Stage primaryStage) throws Exception {
Pane pane = new Pane();
...
pane.setOnMousePressed(e -> {
this.mouseX = primaryStage.getX() - e.getScreenX();
this.mouseY = primaryStage.getY() - e.getScreenY();
});
pane.setOnMouseDragged(e -> {
primaryStage.setX(e.getScreenX() + this.mouseX);
primaryStage.setY(e.getScreenY() + this.mouseY);
});
...
}
}
** Résultat d'exécution **
Quant à la couleur de fond du ciel, la couleur de fond n'est décidée que pour certains moments clés (en référence à la méthode de mise en œuvre d'origine). La couleur entre un certain temps clé A et un temps clé B interpole la couleur entre les deux couleurs A et B.
L'interpolation de couleur entre deux couleurs est directement appliquée à la classe Color
, qui est la classe de couleurs JavaFX, car c'est [interpolate (Color, double)](https://docs.oracle.com/javase/jp/8/javafx/ Il y avait une méthode appelée api / javafx / scene / paint / Color.html # interpolate-javafx.scene.paint.Color-double-), et je l'ai utilisée.
ʻInterpolate () calcule la couleur à la position du rapport (valeur de" 0.0 "à" 1.0 ") spécifié par le deuxième argument de la couleur qui devient le récepteur à la couleur spécifiée par le premier argument. revenir. Par exemple,
color1.interporate (color2, 0.5)calculera et retournera une valeur qui est exactement entre
color1 et
color2 (
0.5`).
Le rapport est compris entre l'heure définie pour chaque clé et l'heure actuelle [Durée] de l'API Date & Time (https://docs.oracle.com/javase/jp/8/docs/api/java/time/Duration.html) Il a été calculé en utilisant. L'implémentation spécifique se trouve dans la classe SkyColorGradation.
Récemment [j'ai étudié JavaFX dur](https://qiita.com/opengl-8080/items/51bef25aa05ecd929a3d#javafx-%E3%83%97%E3%83%AD%E3%83%91%E3%83% 86% E3% 82% A3) Dans cette implémentation, j'ai activement utilisé les propriétés JavaFX que j'avais apprises à l'époque.
Profitant du fait que le modèle d'observateur peut être implémenté simplement en utilisant la propriété JavaFX, la configuration globale ressemble à ↓.
Par exemple, l'affichage de l'heure est le suivant.
model
ClockBase.java
package littlesky.model.clock;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import java.time.LocalDate;
import java.time.LocalTime;
public class ClockBase implements Clock {
protected final ReadOnlyObjectWrapper<LocalDate> date = new ReadOnlyObjectWrapper<>();
protected final ReadOnlyObjectWrapper<LocalTime> time = new ReadOnlyObjectWrapper<>();
@Override
public LocalDate getDate() {
return this.date.get();
}
@Override
public LocalTime getTime() {
return this.time.get();
}
@Override
public ReadOnlyObjectProperty<LocalDate> dateProperty() {
return this.date.getReadOnlyProperty();
}
@Override
public ReadOnlyObjectProperty<LocalTime> timeProperty() {
return this.time.getReadOnlyProperty();
}
}
ReadTimeClock.java
package littlesky.model.clock;
import javafx.application.Platform;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class RealTimeClock extends ClockBase {
public RealTimeClock() {
this.updateDateTime();
}
public void start() {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor((runnable) -> {
Thread thread = new Thread(runnable);
thread.setDaemon(true);
return thread;
});
executor.scheduleAtFixedRate(() -> {
Platform.runLater(this::updateDateTime);
}, 0, 500, TimeUnit.MILLISECONDS);
}
private void updateDateTime() {
LocalDateTime now = LocalDateTime.now();
if (this.getDate() == null || !this.getDate().equals(now.toLocalDate())) {
this.date.set(now.toLocalDate());
}
this.time.set(now.toLocalTime());
}
}
Cette classe compte l'heure actuelle. Il génère un thread distinct du thread d'interface utilisateur et compte l'heure actuelle à des intervalles de 500 millisecondes.
Pour l'heure actuelle, la date («date») et l'heure («heure») sont divulguées à l'extérieur en utilisant respectivement «ReadOnlyObjectProperty».
ClockBase.java
protected final ReadOnlyObjectWrapper<LocalDate> date = new ReadOnlyObjectWrapper<>();
protected final ReadOnlyObjectWrapper<LocalTime> time = new ReadOnlyObjectWrapper<>();
...
@Override
public ReadOnlyObjectProperty<LocalDate> dateProperty() {
return this.date.getReadOnlyProperty();
}
@Override
public ReadOnlyObjectProperty<LocalTime> timeProperty() {
return this.time.getReadOnlyProperty();
}
ReadOnlyObjectWrapper
est une classe qui peut être utilisée pour exposer des propriétés en lecture seule au monde extérieur.
Vous pouvez l'utiliser pour empêcher la réécriture de la valeur depuis l'extérieur du modèle.
Une chose à noter est que la valeur est mise à jour dans un thread distinct du thread de l'interface utilisateur, vous devez donc toujours utiliser Platform.runLater ()
pour mettre à jour la valeur de la propriété à partir du thread de l'interface utilisateur. ..
Si vous mettez à jour la valeur en utilisant runLater ()
dans le modèle, le côté vue peut utiliser la propriété en toute confiance.
view
TimeLabelViewModel.java
package littlesky.view.main;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
...
import littlesky.model.clock.Clock;
import littlesky.model.option.ViewOptions;
...
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static littlesky.util.BindingBuilder.*;
public class TimeLabelViewModel {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
private static final DateTimeFormatter formatterWithoutSeconds = DateTimeFormatter.ofPattern("HH:mm");
private final ReadOnlyStringWrapper text = new ReadOnlyStringWrapper("00:00:00");
...
public void bind(Clock clock, SkyColor skyColor, ViewOptions viewOptions) {
this.text.bind(
binding(clock.timeProperty(), viewOptions.showSecondsProperty())
.computeValue(() -> this.formatClockTime(clock, viewOptions))
);
...
}
private String formatClockTime(Clock clock, ViewOptions viewOptions) {
LocalTime time = clock.getTime();
return time.format(viewOptions.isShowSeconds() ? formatter : formatterWithoutSeconds);
}
...
public ReadOnlyStringProperty textProperty() {
return this.text.getReadOnlyProperty();
}
...
}
TimeLabelViewModel
est une classe qui contrôle l'affichage de l'heure.
Dans la méthode bind ()
, nous définissons la valeur du texte (propriété text
) à afficher sur l'étiquette d'heure en fonction de Clock
et ViewOptions
.
public void bind(Clock clock, SkyColor skyColor, ViewOptions viewOptions) {
this.text.bind(
binding(clock.timeProperty(), viewOptions.showSecondsProperty())
.computeValue(() -> this.formatClockTime(clock, viewOptions))
);
...
}
--binding (...) .computeValue (...)
est un constructeur qui vous permet d'écrire des instances de Binding
avec moins de description, et tout ce que vous faites est de créer une classe anonyme pour ʻObjectBinding` est.
text
) dépend de la propriété time
de Clock
et de la propriété showSeconds
de ViewOptions
.
--Et si la valeur de «time» ou «showSeconds» change, la méthode «formatClockTime ()» est exécutée et la valeur de la propriété «text» est mise à jour. private String formatClockTime(Clock clock, ViewOptions viewOptions) {
LocalTime time = clock.getTime();
return time.format(viewOptions.isShowSeconds() ? formatter : formatterWithoutSeconds);
}
--formatClockTime ()
se réfère à l'heure mise à jour
, showSeconds
pour générer le texte de l'heure à afficher sur l'écran.
--Enfin, la propriété text
est exposée au monde extérieur en tant que propriété en lecture seule, tout comme le modèle.
private final ReadOnlyStringWrapper text = new ReadOnlyStringWrapper("00:00:00");
...
public ReadOnlyStringProperty textProperty() {
return this.text.getReadOnlyProperty();
}
text
exposée est associée à l'élément sur l'écran dans le contrôleur.controller
MainController.java
package littlesky.controller.main;
...
public class MainController implements Initializable {
...
@FXML
private Label timeLabel;
...
@Override
public void initialize(URL url, ResourceBundle resources) {
...
this.replaceClockAndWeather(this.realTimeClock, this.openWeatherMap);
}
...
private void replaceClockAndWeather(Clock newClock, Weather weather) {
...
TimeLabelViewModel timeLabelViewModel = new TimeLabelViewModel();
timeLabelViewModel.bind(newClock, skyColor, this.options.getViewOptions());
this.timeLabel.textProperty().bind(timeLabelViewModel.textProperty());
...
}
...
}
--MainController
est injecté avec timeLabel
, qui affiche l'heure à l'écran.
RealTimeClock
TimeLabelViewModel
le détecte et met à jour le texte affiché, en tenant compte des paramètres d'affichage à ce moment-là.text
de timeLabel
, qui était liée à la propriété text
de TimeLavelViewModel
, est mise à jour et l'affichage de l'écran change.
La mise à jour du modèle est reflétée dans la vue, par exemple ---Ce n'est pas la seule bonne réponse, car c'est une méthode à laquelle nous sommes arrivés par essais et erreurs lors de sa mise en œuvre. Cependant, je pense que cette méthode d'utilisation du mécanisme de surveillance des propriétés JavaFX comme moyen de propager les modifications du modèle à la vue n'est pas mauvaise dans la direction.
** Ce que je pensais était bon **
«Je sens que les rôles de chaque classe sont clairement séparés. --Modèle met à jour la logique principale et les valeurs --View détermine ce qu'il faut afficher en fonction des valeurs du modèle --Control est un pont entre la vue et le modèle «Je pense que la coopération de ces classes est simplifiée par le mécanisme de surveillance des propriétés JavaFX.
** Points que je souhaite améliorer **
En ce qui concerne le calcul des heures de lever et de coucher du soleil, j'ai cherché une bibliothèque en Java et j'ai trouvé les deux suivantes.
Le premier calcule l'heure du lever et du coucher du soleil, et le second calcule également la position du soleil, la position de la lune et la phase lunaire.
La première semble ne pas avoir été entretenue depuis plusieurs années, et la seconde bibliothèque semble être meilleure compte tenu de cette utilisation.
Cependant, lorsque je l'ai réellement utilisé, les heures de lever et de coucher du soleil étaient toutes deux calculées avec une précision raisonnable (quelques minutes d'erreur), mais le calcul de la phase de lune par cette dernière bibliothèque était considérablement différent de celui réel. C'était.
Donc, au final, j'ai décidé d'utiliser l'ancienne bibliothèque pour calculer le lever et le coucher du soleil.
SunriseSunsetTime
package littlesky.model.sun;
import com.luckycatlabs.sunrisesunset.SunriseSunsetCalculator;
import com.luckycatlabs.sunrisesunset.dto.Location;
import littlesky.model.location.UserLocation;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class SunriseSunsetTime {
private final LocalDate localDate;
private final SunriseSunsetCalculator calculator;
private final TimeZone timeZone = TimeZone.getDefault();
public SunriseSunsetTime(UserLocation userLocation, LocalDate localDate) {
this.localDate = localDate;
Location location = new Location(userLocation.getLatitude(), userLocation.getLongitude());
this.calculator = new SunriseSunsetCalculator(location, this.timeZone);
}
public LocalTime sunriseTime() {
Calendar today = this.toCalendar(this.localDate);
Calendar sunriseTime = this.calculator.getOfficialSunriseCalendarForDate(today);
return this.toLocalTime(sunriseTime);
}
public LocalTime sunsetTime() {
Calendar today = this.toCalendar(this.localDate);
Calendar sunsetTime = this.calculator.getOfficialSunsetCalendarForDate(today);
return this.toLocalTime(sunsetTime);
}
private Calendar toCalendar(LocalDate localDate) {
Date date = Date.from(localDate.atStartOfDay(this.timeZone.toZoneId()).toInstant());
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar;
}
private LocalTime toLocalTime(Calendar calendar) {
return calendar.getTime().toInstant().atZone(this.timeZone.toZoneId()).toLocalTime();
}
}
L'objet de date requis par la bibliothèque est de type «Calendrier», qui peut ne pas avoir été maintenu pendant plusieurs années. Puisqu'il s'agit de Java 8, il était nécessaire de le convertir et de l'utiliser avec l'API Date & Time.
Ensuite, qu'est-il arrivé au calcul de la phase lunaire? Je n'ai pas trouvé la bibliothèque, j'ai donc décidé de la calculer moi-même.
Cependant, il n'est pas possible de calculer la trajectoire, donc
En référence à ce domaine, j'ai écrit un programme pour trouver l'âge de la lune avec une précision raisonnable.
Plus précisément, à partir de l'âge de la lune le 1er janvier 1999 (13,17), le nombre de jours jusqu'à la date actuelle est calculé, et l'âge de la lune progressant pendant cette période est calculé en tenant compte du cycle méton.
L'implémentation est en MoonPhase.
Recommended Posts