Araç Oluşturma
Widget - Araçlar, farklı veri kaynakları aracılığıyla yayınlanan verileri görüntülememize, değiştirmemize ve verilerle etkileşim kurmamıza olanak tanımaktadır. CameraServer, NetworkTables ve Base eklentileri, (FRC’ye özgü veri türleri dahil olmak üzere) temel veri türlerini kontrol etmek için pencere öğeleri sağlamaktadır. Bununla birlikte, kişisel araçlar, önceki bölümlerde veya Java Nesnelerinde oluşturduğumuz özel veri türlerimizi kontrol etmemize izin verir.
Temel Widget arayüzü, Component - Bileşen ve Sourced - Kaynaklı arayüzlerinin mirasçısıdır. Component, Shuffleboard’da görüntülenebilecek bileşenlerin en temel yapı taşıdır. Sourced, verileri görüntülemek veya değiştirmek için veri kaynaklarını işleyebilen ve bunlarla arayüz oluşturabilen şeyler için bir arayüzdür. Veri bağlamalarını desteklemeyen ancak sadece alt düğümlere sahip olan araçlar Sourced arayüzü değil, sadece Component arayüzünü kullanmaktadır. Her ikisi de widget oluşturmaya yönelik temel yapı taşlarıdır ve verileri değiştirip görüntülememizi sağlamaktadır.
İyi bir araç, son kullanıcının aracı ihtiyaçlarına göre kişiselleştirmesine olanak tanımaktadır. Bu duruma bir örnek olarak, kullanıcının sayı kaydırıcısının aralığını, diğer bir deyişle maksimum ve minimum aralığını veya kaydırıcının yönünü kontrol etmesine olanak tanıması gösterilebilmektedir. Widget’ın görünümü veya nasıl görüntülendiği FXML kullanılarak tanımlanmaktadır. FXML, aracın statik düzenini (Bölmeler, Etiketler ve Kontroller) tanımlamak için yararlı olan XML tabanlı bir dildir.
FXML’e dair daha fazla biliye buradan ulaşabilirsiniz.
Bir Aracın FXML’inin Tanımlanması
Bu örnekte, önceki bölümlerde oluşturduğumuz Point2D veri türümüzün X ve Y koordinatlarını kontrol etmemize yardımcı olması için iki kaydırıcı oluşturacağız. FXML dosyasınını Java sınıfıyla aynı pakete yerleştirmeniz faydalı olacaktır.
Aracımız için boş, üzerinde işlem yapılmamış bir pencere oluşturmak için bir Pane - Bölme oluşturmamız gerekmektedir. Bölme, diğer alt düğümleri, bu durumda 2 kaydırıcıyı içeren bir ana düğümdür. Aşağıda belirtildiği üzere birçok farklı Pane türü vardır:
- Stack Pane - Yığın Bölmesi
Stack Pane, öğelerin üst üste bindirilmesine izin vermektedir. Ayrıca, StackPanes varsayılan olarak alt düğümleri ortalamaktadır.
- Grid Pane - Izgara Bölmesi
Grid Panes are extremely useful defining child elements using a coordinate system by creating a flexible grid of rows and columns on the pane.
- Flow Pane - Akış Bölmesi
Flow Pane, tüm alt düğümleri bir sınır kümesinde sarmaktadır. Alt düğümler (pane’in yükseklik sınırına sarılmış) dikey veya (pane’in genişlik sınırına sarılmış) yatay olarak akabilmektedir.
- Anchor Pane - Bağlantı Bölmesi
Anchor Pane, alt öğelerin pane’in üstüne, altına, sol tarafına, sağ tarafına veya ortasına yerleştirilmesine izin vermektedir.
Layout pane - Düzen bölmeleri, alt düğümleri HBox <https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/HBox.html>`__ kullanılarak tek bir yatay satıra veya VBox kullanarak tek bir dikey sütuna yerleştirmek için faydalıdır.
FXML kullanarak bir Pane’i tanımlayan temel sözdizimi aşağıdaki şekildedir:
<?import javafx.scene.layout.*?>
<StackPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="/path/to/widget/class" fx:id="root">
...
</StackPane>
fx: controller özniteliği, widget sınıfının adını içermektedir. FXML dosyası yüklendiğinde bu sınıfın bir örneği oluşturulmaktadır. Sınıfın çalışması için, kontrolör sınıfının bağımsız değişken olmayan bir “constructor”a sahip olması gerekmektedir.
Widget Sınıfı Oluşturmak
Artık elimizde bir Pane olduğuna göre, şimdi bu pane içerisine alt öğeler ekleyebiliriz. Bu örnekte, iki kaydırıcı nesnesi ekleyebilmekteyiz. Her öğeye bir fx:id eklemeyi unutmayınız, bu eklenti sayesinde öğeler daha sonra oluşturacağımız Java sınıfımızda referans alınabilirler. Kaydırıcılarımızı üst üste konumlandırmak için bir VBox kullanacağız.
<?import javafx.scene.layout.*?>
<StackPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="/path/to/widget/class" fx:id="root">
<VBox>
<Slider fx:id = "xSlider"/>
<Slider fx:id = "ySlider"/>
</VBox>
</StackPane>
FXML dosyamızı oluşturduğumuza göre, artık bir araç sınıfı oluşturabiliriz. Araç sınıfı, aracın desteklediği veri türlerini ve adını belirten bir @Description ek açıklaması içermelidir. Bir @Description ek açıklaması yoksa, eklenti sınıfı, araçlarını döndürmek için get () metodu uygulamalıdır.
Eklenti sınıfı pencere aracının düzenini içeren FXML dosyasına işaret eden bir @ParametrizedController ek açıklaması da içermelidir. Sınıf yalnızca bir veri kaynağını destekliyorsa, SimpleAnnotatedWidget sınıfını genişletmelidir. Sınıf birden çok veri kaynağını destekliyorsa, ``ComplexAnnotatedWidget``sınıfını genişletmelidir. Daha fazla bilgi için bkz. : Widget Types.
import edu.wpi.first.shuffleboard.api.widget.Description;
import edu.wpi.first.shuffleboard.api.widget.ParametrizedController;
import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget;
/*
* If the FXML file and Java file are in the same package, that is the Java file is in src/main/java and the
* FXML file is under src/main/resources or your code equivalent package, the relative path will work
* However, if they are in different packages, an absolute path will be required.
*/
@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class)
@ParametrizedController("Point2DWidget.fxml")
public final class Point2DWidget extends SimpleAnnotatedWidget<MyPoint2D> {
}
Özel bir veri türü kullanmıyorsanız, herhangi bir Java veri türüne (yani Double.class) başvurabilirsiniz veya araç veri bağlamaya ihtiyaç duymuyorsa, NoneType.class öğesini iletebilirsiniz.
Artık sınıfımızı oluşturduğumuza göre, @ FXML ek açıklamasını kullanarak FXML dosyamıza eklediğimiz araçlar için alanlar oluşturabiliriz. İki kaydırıcımız özelinde bir örnek aşağıdaki şekildedir:
import edu.wpi.first.shuffleboard.api.widget.Description;
import edu.wpi.first.shuffleboard.api.widget.ParametrizedController;
import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget;
import javafx.fxml.FXML;
@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class)
@ParametrizedController("Point2DWidget.fxml")
public final class Point2DWidget extends SimpleAnnotatedWidget<MyPoint2D> {
@FXML
private Pane root;
@FXML
private Slider xSlider;
@FXML
private Slider ySlider;
}
Bölmemizi kişisel aracımızda görüntülemek için getView() metodunu geçersiz kılmamız ve StackPane’i geri almamız gerekmektedir.
import edu.wpi.first.shuffleboard.api.widget.Description;
import edu.wpi.first.shuffleboard.api.widget.ParametrizedController;
import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget;
import javafx.fxml.FXML;
@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class)
@ParametrizedController("Point2DWidget.fxml")
public final class Point2DWidget extends SimpleAnnotatedWidget<MyPoint2D> {
@FXML
private StackPane root;
@FXML
private Slider xSlider;
@FXML
private Slider ySlider;
@Override
public Pane getView() {
return root;
}
}
Dinleyicilerin Eklenmesi ve Bağlama Öğeleri
Bağlama, JavaFX araçlarının veri kaynağı ile doğrudan bir ilişki ifade etmesine izin veren bir düzenektir. Örneğin aracın değiştirilmesiyle aracın ilgili olduğu NetworkTableEntry’nin değişmesi veya bu ilişkinin tam tersini mümkün kılmaktadır.
Bu duruma bir örnek, sırasıyla xSlider ve ySlider değerlerini değiştirerek 2B noktamızın X ve Y koordinatlarını değiştirmek olabilir.
Metod public değilse, metodu FXML’den çağırmak için gerekli olan @FXML ek açıklaması ile etiketlenmiş initialize() metoduna bağlamaları ayarlamak doğru bir uygulama olacaktır.
import edu.wpi.first.shuffleboard.api.widget.Description;
import edu.wpi.first.shuffleboard.api.widget.ParametrizedController;
import edu.wpi.first.shuffleboard.api.widget.SimpleAnnotatedWidget;
import javafx.fxml.FXML;
@Description(name = "MyPoint2D", dataTypes = MyPoint2D.class)
@ParametrizedController("Point2DWidget.fxml")
public final class Point2DWidget extends SimpleAnnotatedWidget<MyPoint2D> {
@FXML
private StackPane root;
@FXML
private Slider xSlider;
@FXML
private Slider ySlider;
@FXML
private void initialize() {
xSlider.valueProperty().bind(dataOrDefault.map(MyPoint2D::getX));
ySlider.valueProperty().bind(dataOrDefault.map(MyPoint2D::getY));
}
@Override
public Pane getView() {
return root;
}
}
Yukarıdaki initialize metodu kaydırıcının değer özelliğini MyPoint2D veri sınıfının ilgili X ve Y değerlerine bağlamaktadır. Diğer bir deyişle kaydırıcının değiştirilmesi koordinatları değiştirecek veya bunun tam tersi bir ilişkiyi mümkün kılacaktır. dataOrDefault.map() metodu veri kaynağının değerini elde edecektir veya hiçbir kaynak mevcut değilse değeri varsayılana döndürecektir.
Dinleyici kullanımı, kaydırıcı veya veri kaynağı değiştiğinde değerleri değiştirmenin başka bir yoludur. Örneğin kaydırıcımız için bir dinleyici aşağıdaki gibi olacaktır:
xSlider.valueProperty().addListener((observable, oldValue, newValue) -> setData(getData().withX(newValue));
In this case, the setData() method sets the value in the data source of the widget to the newValue.
Özel Bileşenlerin Keşfedilmesi
Widgets are not automatically discovered when loading plugins; the defining plugin must explicitly export it for it to be usable. This approach is taken to allow multiple plugins to be defined in the same JAR.
@Override
public List<ComponentType> getComponents() {
return List.of(WidgetType.forAnnotatedWidget(Point2DWidget.class));
}
Veri Türü için Varsayılan Aracı Ayarlamak
In order to set your widget as default for your custom data type, you can override the getDefaultComponents() in your plugin class that stores a Map for all default widgets as noted below:
@Override
public Map<DataType, ComponentType> getDefaultComponents() {
return Map.of(Point2DType.Instance, WidgetType.forAnnotatedWidget(Point2DWidget.class));
}