1675827904
An example WPF HelloWorld program written using the ReactiveUI framework.
Original: 3 February 2019
Updated: 5 February 2019
I had an unexpectedly difficult time figuring out how to get a simple starter program working using ReactiveUI. I found some documentation sources useful, and others confusing, or lacking SIMPLE examples for the current software versions.
The current ReactiveUI Getting Started page is pretty decent, but either I missed it when I started, or it wasn't as good then and has improved since. I remember trying to figure stuff out from the DynamicTrader program, which is a wonder to behold, but I found myself hopelessly confused by all the advanced stuff going on in there.
So, I thought I'd post my working example here on github. It's even more basic than the 'Getting Started' example. No network calls or even any input. Just a slightly fancy Hello World that does make use of a few ReactiveUI components.
I'm still learning a lot of this stuff (C#, ReactiveX, ReactiveUI), so I can't yet say I have a firm understanding of it all, even all the parts used in this simple example. This code works, but I wouldn't be at all surprised if someone more experienced could point out at least a few 'bad' things in it.
(My biggest problem was getting the bindings to work. I think that was because I was using a version of the Splat service locater code in my app.xaml.cs file that was incompatible with the rest of my code. I had tried using different versions of that line that I copied from various documentation sources/examples, but not evidently ones that were in sync with the rest of my code. The sample that finally solved my problem was one of the two buried in the ReactvieUI source code itself - ReactiveDemo. This is the code the ReactiveUI 'Getting Started' page currently walks you through.)
What it does:
Opens a Window that shows the greeting "Hello World", cycling between a few different languages.
I'm using Visual Studio 2019 Enterprise Preview. I assume VS2017 Community Edition would work just as well. I'll likely switch to VS2019 Community Edition when the preview expires.
You'll need to install the following NuGet Packages into your environment. They'll in-turn load others that they require.
Look in the packages.config file to see a complete list of current packages and version numbers. (Don't be surprised if the README doesn't stay up to date with the actual repo :)
(Note: I perhaps complicated the example a bit by using Fody, but its pretty straitforward to use, has given me very little trouble, and makes the code look cleaner.)
Nothing really notable here. Standard WPF layout created by the Visual Studio project creater.
Additions to the base template are:
Namespaces
These namespaces are used by the Locator statement below.
using ReactiveUI;
using Splat;
using System.Reflection;
App Class Constructor
Add the following constructor code to the boilerplate produced by VS:
public App()
{
Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());
}
Note
Window properties
<reactiveui:ReactiveWindow
x:Class="HelloWorldRUI.MainWindow"
x:TypeArguments="helloworldrui:AppViewModel"
xmlns:helloworldrui="clr-namespace:HelloWorldRUI"
xmlns:reactiveui="http://reactiveui.net"
...
Title="HelloWorld RUI">
...
</reactiveui:ReactiveWindow>
Window Layout
In this example we are only going to display two strings, each in its own TextBlock. In a normal WPF project you'd bind the UI elements to ViewModel properties explicitly within the XAML. Here, we simply ensure that each TextBlock has a unique name. The binding is done in the MainWindow's constructor code, which references the x:Name values.
<StackPanel>
<TextBlock x:Name="GreetingTextBlock" />
<TextBlock x:Name="LangTextBlock" />
</StackPanel>
In the HelloWorldRUI project, I've left both the View and ViewModel code in the same file. This is not normally done, but it doesn't hurt anything, and I think makes it a little easier to read as an example.
In a WPF MVVM program, the View class constructor is normally responsible for calling InitializeComponent() and creating an instance of the ViewModel. In this ReactiveUI code the constructor also binds the View and ViewModel properties, as shown below.
View Code
using ReactiveUI;
using System.Reactive.Disposables;
namespace HelloWorldRUI
{
public partial class MainWindow : ReactiveWindow<AppViewModel>
{
public MainWindow()
{
InitializeComponent();
ViewModel = new AppViewModel();
this.WhenActivated(d => {
this.OneWayBind(ViewModel, viewModel => viewModel.Lang,
view => view.LangTextBlock.Text).DisposeWith(d);
this.OneWayBind(ViewModel, viewModel => viewModel.Greeting,
view => view.GreetingTextBlock.Text).DisposeWith(d);
});
}
}
}
ViewModel Code
Assuming you understand Reactive Observable pipelines, this code is reasonably straightforward.
Using Fody slightly changes the declarations of the Reactive properties and ObservableAsPropertyHelpers compared to how you've probably seen it described in ReactiveUI documentation. Setting ObservableAsPropertyHelper values using ToProperty() changes slightly as well. See the Fody readme (referenced farther below) for a more detailed explanation.
The Observable.Interval pipeline creates an observable that fires every 2 seconds, eventually resulting in changes to the Language displayed on the UI. The take(100) function terminates the pipeline after 100 iterations. The Select() function specified will increment the Lang to the next one in the Greetings dictionary. ObserveOn(RxApp.MainThreadScheduler) needs to be used when changing any property bound to the View. ToPropertyEx() will set the value of the ObservableAsProperty Lang to the language output in the Select() function. Since Lang is bound to the LangTextBlock in the View, the UI will automatically reflect this change. The initial value of the Observable Lang is passed as an argument to ToPropertyEx(), as it cant be set otherwise.
The final observable pipeline will take care of updating the Greeting. This pipeline is set to 'tick' whenever the ViewModel's Lang property changes (as specified by the WhenAnyValue function). The Where() clause ignores any values that are not contained in the Keys array. The Select() function looks up the Greeting associated with the Lang. Again, ObserveOn makes sure our change is visible to the UI, and the Subscribe() function simply sets the Reactive property Greeting with the result created by Select. Since Greeting is a 'simple' Reactive property (not an Observable) you can just set its value. Its initial value is specified on its declaration line.
(Experiment: try removing one of the ObserveOn lines and see what happens. It's useful for future reference to see for yourself how that error manifests.)
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace HelloWorldRUI
{
public class AppViewModel : ReactiveObject
{
[Reactive] public string Greeting { get; set; } = "Greeting";
public extern string Lang { [ObservableAsProperty] get; }
public AppViewModel()
{
Dictionary<string, string> Greetings = new Dictionary<string, string>() {
{ "English", "Hello World!" },
{ "French", "Bonjour le monde!" },
{ "German", "Hallo Welt!" },
{ "Japanese", "Kon'nichiwa sekai!" },
{ "Spanish", "¡Hola Mundo!" },
};
string[] keys = Greetings.Keys.ToArray();
// select next language every 2 seconds (100 times)
Observable.Interval(TimeSpan.FromSeconds(2))
.Take(100)
.Select(_ => keys[(Array.IndexOf(keys, Lang) + 1) % keys.Count()])
.ObserveOn(RxApp.MainThreadScheduler)
.ToPropertyEx(this, x => x.Lang, "Language");
// update Greeting when language changes
this.WhenAnyValue(x => x.Lang)
.Where(lang => keys.Contains(lang))
.Select(x => Greetings[x])
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => Greeting = x);
}
}
}
If you create these source files by hand, you may get an error when building the project the first time indicating that the FodyWeavers.xml file couldn't be found and had to be generated for you. In that case simply rebuild the project and the error should resolve itself. (Rebuilding after the FodyWeavers file was created also resolved an Intellisense error I had in the MainWindow.xaml file which claimed it couldn't find the AppViewModel.)
Integration of ReactiveUI and Fody with Intellisense seems to be incomplete or slow at times. Occasionally I think I found Intellisense reported errors which resolved themselves by rebuilding the project.
They're updating their website [~1/20/19], so some of these links may break.
Links:
The project source code has been absorbed into the ReactiveUI GitHub repo, but I haven't found the README file from the original repo in ReactiveUI. The original project README has an example of how to use it.
Links:
Reactive Extensions (ReactiveX) is a project which provides Reactive libraries for a number of programming languages and platforms. One such supported combination is C#/DotNet (aka the System.Reactive package). The base form of Reactive programming has no direct support for Graphical User Interfaces (thats where ReactiveUI comes in.) Its been around for quite a while, and there's some pretty good documentation for it, though not necessarily all up to date for C#/DotNet. However the older stuff is still VERY useful. (You may notice that some of the maintainers of dotnet/reactive are also the same people supporting ReactiveUI)
Links:
At the time I'm writing this, this book is the only organized, well written, reasonably complete reference/tutorial for ReactiveUI that I could find. I'm still working my way through it, and I'm finding it very useful. (Thanks to the author for making the effort to write and publish it!)
The biggest shortcoming of the book, for me as a newcomer, was the way the sample code was organized. I found that it made it very difficult for me to observe and emulate each example as a standalone application.
I offer the following observations for my fellow noobs:
Links
Author: richk1
Source code: https://github.com/richk1/HelloWorldRUI
License: MIT license
#ReactiveUI #csharp
1677668905
Mocking library for TypeScript inspired by http://mockito.org/
mock
) (also abstract classes) #examplespy
) #examplewhen
) via:verify
)reset
, resetCalls
) #example, #examplecapture
) #example'Expected "convertNumberToString(strictEqual(3))" to be called 2 time(s). But has been called 1 time(s).'
)npm install ts-mockito --save-dev
// Creating mock
let mockedFoo:Foo = mock(Foo);
// Getting instance from mock
let foo:Foo = instance(mockedFoo);
// Using instance in source code
foo.getBar(3);
foo.getBar(5);
// Explicit, readable verification
verify(mockedFoo.getBar(3)).called();
verify(mockedFoo.getBar(anything())).called();
// Creating mock
let mockedFoo:Foo = mock(Foo);
// stub method before execution
when(mockedFoo.getBar(3)).thenReturn('three');
// Getting instance
let foo:Foo = instance(mockedFoo);
// prints three
console.log(foo.getBar(3));
// prints null, because "getBar(999)" was not stubbed
console.log(foo.getBar(999));
// Creating mock
let mockedFoo:Foo = mock(Foo);
// stub getter before execution
when(mockedFoo.sampleGetter).thenReturn('three');
// Getting instance
let foo:Foo = instance(mockedFoo);
// prints three
console.log(foo.sampleGetter);
Syntax is the same as with getter values.
Please note, that stubbing properties that don't have getters only works if Proxy object is available (ES6).
// Creating mock
let mockedFoo:Foo = mock(Foo);
// Getting instance
let foo:Foo = instance(mockedFoo);
// Some calls
foo.getBar(1);
foo.getBar(2);
foo.getBar(2);
foo.getBar(3);
// Call count verification
verify(mockedFoo.getBar(1)).once(); // was called with arg === 1 only once
verify(mockedFoo.getBar(2)).twice(); // was called with arg === 2 exactly two times
verify(mockedFoo.getBar(between(2, 3))).thrice(); // was called with arg between 2-3 exactly three times
verify(mockedFoo.getBar(anyNumber()).times(4); // was called with any number arg exactly four times
verify(mockedFoo.getBar(2)).atLeast(2); // was called with arg === 2 min two times
verify(mockedFoo.getBar(anything())).atMost(4); // was called with any argument max four times
verify(mockedFoo.getBar(4)).never(); // was never called with arg === 4
// Creating mock
let mockedFoo:Foo = mock(Foo);
let mockedBar:Bar = mock(Bar);
// Getting instance
let foo:Foo = instance(mockedFoo);
let bar:Bar = instance(mockedBar);
// Some calls
foo.getBar(1);
bar.getFoo(2);
// Call order verification
verify(mockedFoo.getBar(1)).calledBefore(mockedBar.getFoo(2)); // foo.getBar(1) has been called before bar.getFoo(2)
verify(mockedBar.getFoo(2)).calledAfter(mockedFoo.getBar(1)); // bar.getFoo(2) has been called before foo.getBar(1)
verify(mockedFoo.getBar(1)).calledBefore(mockedBar.getFoo(999999)); // throws error (mockedBar.getFoo(999999) has never been called)
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(10)).thenThrow(new Error('fatal error'));
let foo:Foo = instance(mockedFoo);
try {
foo.getBar(10);
} catch (error:Error) {
console.log(error.message); // 'fatal error'
}
You can also stub method with your own implementation
let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);
when(mockedFoo.sumTwoNumbers(anyNumber(), anyNumber())).thenCall((arg1:number, arg2:number) => {
return arg1 * arg2;
});
// prints '50' because we've changed sum method implementation to multiply!
console.log(foo.sumTwoNumbers(5, 10));
You can also stub method to resolve / reject promise
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.fetchData("a")).thenResolve({id: "a", value: "Hello world"});
when(mockedFoo.fetchData("b")).thenReject(new Error("b does not exist"));
You can reset just mock call counter
// Creating mock
let mockedFoo:Foo = mock(Foo);
// Getting instance
let foo:Foo = instance(mockedFoo);
// Some calls
foo.getBar(1);
foo.getBar(1);
verify(mockedFoo.getBar(1)).twice(); // getBar with arg "1" has been called twice
// Reset mock
resetCalls(mockedFoo);
// Call count verification
verify(mockedFoo.getBar(1)).never(); // has never been called after reset
You can also reset calls of multiple mocks at once resetCalls(firstMock, secondMock, thirdMock)
Or reset mock call counter with all stubs
// Creating mock
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(1)).thenReturn("one").
// Getting instance
let foo:Foo = instance(mockedFoo);
// Some calls
console.log(foo.getBar(1)); // "one" - as defined in stub
console.log(foo.getBar(1)); // "one" - as defined in stub
verify(mockedFoo.getBar(1)).twice(); // getBar with arg "1" has been called twice
// Reset mock
reset(mockedFoo);
// Call count verification
verify(mockedFoo.getBar(1)).never(); // has never been called after reset
console.log(foo.getBar(1)); // null - previously added stub has been removed
You can also reset multiple mocks at once reset(firstMock, secondMock, thirdMock)
let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);
// Call method
foo.sumTwoNumbers(1, 2);
// Check first arg captor values
const [firstArg, secondArg] = capture(mockedFoo.sumTwoNumbers).last();
console.log(firstArg); // prints 1
console.log(secondArg); // prints 2
You can also get other calls using first()
, second()
, byCallIndex(3)
and more...
You can set multiple returning values for same matching values
const mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(anyNumber())).thenReturn('one').thenReturn('two').thenReturn('three');
const foo:Foo = instance(mockedFoo);
console.log(foo.getBar(1)); // one
console.log(foo.getBar(1)); // two
console.log(foo.getBar(1)); // three
console.log(foo.getBar(1)); // three - last defined behavior will be repeated infinitely
Another example with specific values
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(1)).thenReturn('one').thenReturn('another one');
when(mockedFoo.getBar(2)).thenReturn('two');
let foo:Foo = instance(mockedFoo);
console.log(foo.getBar(1)); // one
console.log(foo.getBar(2)); // two
console.log(foo.getBar(1)); // another one
console.log(foo.getBar(1)); // another one - this is last defined behavior for arg '1' so it will be repeated
console.log(foo.getBar(2)); // two
console.log(foo.getBar(2)); // two - this is last defined behavior for arg '2' so it will be repeated
Short notation:
const mockedFoo:Foo = mock(Foo);
// You can specify return values as multiple thenReturn args
when(mockedFoo.getBar(anyNumber())).thenReturn('one', 'two', 'three');
const foo:Foo = instance(mockedFoo);
console.log(foo.getBar(1)); // one
console.log(foo.getBar(1)); // two
console.log(foo.getBar(1)); // three
console.log(foo.getBar(1)); // three - last defined behavior will be repeated infinity
Possible errors:
const mockedFoo:Foo = mock(Foo);
// When multiple matchers, matches same result:
when(mockedFoo.getBar(anyNumber())).thenReturn('one');
when(mockedFoo.getBar(3)).thenReturn('one');
const foo:Foo = instance(mockedFoo);
foo.getBar(3); // MultipleMatchersMatchSameStubError will be thrown, two matchers match same method call
You can mock interfaces too, just instead of passing type to mock
function, set mock
function generic type Mocking interfaces requires Proxy
implementation
let mockedFoo:Foo = mock<FooInterface>(); // instead of mock(FooInterface)
const foo: SampleGeneric<FooInterface> = instance(mockedFoo);
You can mock abstract classes
const mockedFoo: SampleAbstractClass = mock(SampleAbstractClass);
const foo: SampleAbstractClass = instance(mockedFoo);
You can also mock generic classes, but note that generic type is just needed by mock type definition
const mockedFoo: SampleGeneric<SampleInterface> = mock(SampleGeneric);
const foo: SampleGeneric<SampleInterface> = instance(mockedFoo);
You can partially mock an existing instance:
const foo: Foo = new Foo();
const spiedFoo = spy(foo);
when(spiedFoo.getBar(3)).thenReturn('one');
console.log(foo.getBar(3)); // 'one'
console.log(foo.getBaz()); // call to a real method
You can spy on plain objects too:
const foo = { bar: () => 42 };
const spiedFoo = spy(foo);
foo.bar();
console.log(capture(spiedFoo.bar).last()); // [42]
Author: NagRock
Source Code: https://github.com/NagRock/ts-mockito
License: MIT license
1639054860
Python es un lenguaje versátil y flexible; a menudo hay más de una forma de lograr algo.
En este tutorial, verá algunas de las formas en que puede imprimir una cadena y una variable juntas.
¡Empecemos!
print()
función en PythonPara imprimir cualquier cosa en Python, se utiliza la print()
función - que es la print
palabra clave seguida de un conjunto de apertura y cierre de paréntesis, ()
.
#how to print a string
print("Hello world")
#how to print an integer
print(7)
#how to print a variable
#to just print the variable on its own include only the name of it
fave_language = "Python"
print(fave_language)
#output
#Hello world
#7
#Python
Si omite los paréntesis, obtendrá un error:
print "hello world"
#output after running the code:
#File "/Users/dionysialemonaki/python_articles/demo.py", line 1
# print "hello world"
# ^^^^^^^^^^^^^^^^^^^
#SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?
Si escribe su código Python en Visual Studio Code, con la extensión Python , también obtendrá un subrayado y una pista que indican que algo no está del todo bien:
Como se mencionó anteriormente, la declaración de impresión se utiliza para generar todo tipo de información. Esto incluye datos textuales y numéricos, variables y otros tipos de datos.
También puede imprimir texto (o cadenas) combinado con variables, todo en una declaración.
Verá algunas de las diferentes formas de hacer esto en las secciones siguientes.
Concatenar, según el diccionario, significa enlazar (cosas) juntas en una cadena o serie.
Esto se hace mediante la adición de varias cosas (en este caso la programación - se añaden datos), junto con otros, utilizando el operador de suma Python, +
.
Tenga en cuenta que la concatenación solo se usa para cadenas, por lo que si la variable que desea concatenar con el resto de las cadenas es de un tipo de datos entero, tendrá que convertirla en una cadena con la str()
función.
En el siguiente ejemplo, quiero imprimir el valor de una variable junto con algún otro texto.
Agrego las cadenas entre comillas dobles y el nombre de la variable sin rodearlo, usando el operador de suma para encadenarlos todos juntos:
fave_language = "Python"
print("I like coding in " + fave_language + " the most")
#output
#I like coding in Python the most
Con la concatenación de cadenas, debe agregar espacios usted mismo, por lo que si en el ejemplo anterior no hubiera incluido ningún espacio entre las comillas, la salida se vería así:
fave_language = "Python"
print("I like coding in" + fave_language + "the most")
#output
#I like coding inPythonthe most
Incluso puede agregar los espacios por separado:
fave_language = "Python"
print("I like coding in" + " " + fave_language + " " + "the most")
#output
#I like coding in Python the most
Esta no es la forma más preferida de imprimir cadenas y variables, ya que puede ser propensa a errores y consumir mucho tiempo.
Puede imprimir texto junto a una variable, separados por comas, en una declaración de impresión.
first_name = "John"
print("Hello",first_name)
#output
#Hello John
En el ejemplo anterior, primero incluí un texto que quería imprimir entre comillas dobles; en este caso, el texto era la cadena Hello
.
Después de las comillas de cierre, agregué una coma que separa ese fragmento de texto del valor contenido en el nombre de la variable ( first_name
en este caso) que luego incluí.
Podría haber agregado más texto siguiendo la variable, así:
first_name = "John"
print("Hello",first_name,"good to see you")
#output
#Hello John good to see you
Este método también funciona con más de una variable:
first_name = "John"
last_name = "Doe"
print("Hello",first_name,last_name,"good to see you")
#output
Hello John Doe good to see you
Asegúrate de separar todo con una coma.
Entonces, separa el texto de las variables con una coma, pero también las variables de otras variables, como se muestra arriba.
Si no se hubiera agregado la coma entre first_name
y last_name
, el código habría arrojado un error:
first_name = "John"
last_name = "Doe"
print("Hello",first_name last_name,"good to see you")
#output
#File "/Users/dionysialemonaki/python_articles/demo.py", line 4
# print("Hello",first_name last_name,"good to see you")
# ^^^^^^^^^^^^^^^^^^^^
#SyntaxError: invalid syntax. Perhaps you forgot a comma?
Como puede ver, los mensajes de error de Python son extremadamente útiles y facilitan un poco el proceso de depuración :)
Utiliza el formato de cadena al incluir un conjunto de llaves de apertura y cierre {}
, en el lugar donde desea agregar el valor de una variable.
first_name = "John"
print("Hello {}, hope you're well!")
En este ejemplo hay una variable, first_name
.
Dentro de la declaración impresa hay un conjunto de comillas dobles de apertura y cierre con el texto que debe imprimirse.
Dentro de eso, agregué un conjunto de llaves en el lugar donde quiero agregar el valor de la variable first_name
.
Si intento ejecutar este código, tendrá el siguiente resultado:
#output
#Hello {}, hope you're well!
¡En realidad, no imprime el valor de first_name
!
Para imprimirlo, necesito agregar el .format()
método de cadena al final de la cadena, que es inmediatamente después de las comillas de cierre:
first_name = "John"
print("Hello {}, hope you're well!".format(first_name))
#output
#Hello John, hope you're well!
Cuando hay más de una variable, usa tantas llaves como la cantidad de variables que desee imprimir:
first_name = "John"
last_name = "Doe"
print("Hello {} {}, hope you're well!")
En este ejemplo, he creado dos variables y quiero imprimir ambas, una después de la otra, así que agregué dos juegos de llaves en el lugar donde quiero que se sustituyan las variables.
Ahora, cuando se trata del .format()
método, importa el orden en el que coloque los nombres de las variables.
Entonces, el valor del nombre de la variable que se agregará primero en el método estará en el lugar de la primera llave, el valor del nombre de la variable que se agregará en segundo lugar estará en el lugar de la segunda llave, y pronto.
Asegúrese de separar los nombres de las variables con comas dentro del método:
first_name = "John"
last_name = "Doe"
print("Hello {} {}, hope you're well!".format(first_name,last_name))
#output
#Hello John Doe, hope you're well!
Si hubiera invertido el orden de los nombres dentro del método, la salida se vería diferente:
first_name = "John"
last_name = "Doe"
print("Hello {} {}, hope you're well!".format(last_name,first_name))
#output
#Hello Doe John, hope you're well!
f-strings
f-strings
son una forma mejor, más legible y concisa de lograr el formato de cadena en comparación con el método que vimos en la sección anterior.
La sintaxis es más sencilla y requiere menos trabajo manual.
La sintaxis general para crear un se f-string
ve así:
print(f"I want this text printed to the console!")
#output
#I want this text printed to the console!
Primero incluye el carácter f
antes de las comillas de apertura y cierre, dentro de la print()
función.
Para imprimir una variable con una cadena en una línea, vuelva a incluir el carácter f
en el mismo lugar, justo antes de las comillas.
Luego agrega el texto que desea dentro de las comillas, y en el lugar donde desea agregar el valor de una variable, agrega un conjunto de llaves con el nombre de la variable dentro de ellas:
first_name = "John"
print(f"Hello, {first_name}!")
#output
#Hello, John!
Para imprimir más de una variable, agrega otro conjunto de llaves con el nombre de la segunda variable:
first_name = "John"
last_name = "Doe"
print(f"Hello, {first_name} {last_name}!")
#output
#Hello, John Doe!
El orden en que coloque los nombres de las variables es importante, así que asegúrese de agregarlos de acuerdo con la salida que desee.
Si hubiera invertido el orden de los nombres, obtendría el siguiente resultado:
first_name = "John"
last_name = "Doe"
print(f"Hello, {last_name} {first_name}!")
#output
#Hello, Doe John!
¡Gracias por leer y llegar hasta el final! Ahora conoce algunas formas diferentes de imprimir cadenas y variables juntas en una línea en Python.
Si desea obtener más información sobre Python, consulte la Certificación Python de freeCodeCamp .
Es adecuado para principiantes, ya que comienza desde los fundamentos y se construye gradualmente hacia conceptos más avanzados. También podrás construir cinco proyectos y poner en práctica todos los nuevos conocimientos que adquieras.
¡Feliz codificación!
https://www.freecodecamp.org/news/python-print-variable-how-to-print-a-string-and-variable/
1667425440
Perl script converts PDF files to Gerber format
Pdf2Gerb generates Gerber 274X photoplotting and Excellon drill files from PDFs of a PCB. Up to three PDFs are used: the top copper layer, the bottom copper layer (for 2-sided PCBs), and an optional silk screen layer. The PDFs can be created directly from any PDF drawing software, or a PDF print driver can be used to capture the Print output if the drawing software does not directly support output to PDF.
The general workflow is as follows:
Please note that Pdf2Gerb does NOT perform DRC (Design Rule Checks), as these will vary according to individual PCB manufacturer conventions and capabilities. Also note that Pdf2Gerb is not perfect, so the output files must always be checked before submitting them. As of version 1.6, Pdf2Gerb supports most PCB elements, such as round and square pads, round holes, traces, SMD pads, ground planes, no-fill areas, and panelization. However, because it interprets the graphical output of a Print function, there are limitations in what it can recognize (or there may be bugs).
See docs/Pdf2Gerb.pdf for install/setup, config, usage, and other info.
#Pdf2Gerb config settings:
#Put this file in same folder/directory as pdf2gerb.pl itself (global settings),
#or copy to another folder/directory with PDFs if you want PCB-specific settings.
#There is only one user of this file, so we don't need a custom package or namespace.
#NOTE: all constants defined in here will be added to main namespace.
#package pdf2gerb_cfg;
use strict; #trap undef vars (easier debug)
use warnings; #other useful info (easier debug)
##############################################################################################
#configurable settings:
#change values here instead of in main pfg2gerb.pl file
use constant WANT_COLORS => ($^O !~ m/Win/); #ANSI colors no worky on Windows? this must be set < first DebugPrint() call
#just a little warning; set realistic expectations:
#DebugPrint("${\(CYAN)}Pdf2Gerb.pl ${\(VERSION)}, $^O O/S\n${\(YELLOW)}${\(BOLD)}${\(ITALIC)}This is EXPERIMENTAL software. \nGerber files MAY CONTAIN ERRORS. Please CHECK them before fabrication!${\(RESET)}", 0); #if WANT_DEBUG
use constant METRIC => FALSE; #set to TRUE for metric units (only affect final numbers in output files, not internal arithmetic)
use constant APERTURE_LIMIT => 0; #34; #max #apertures to use; generate warnings if too many apertures are used (0 to not check)
use constant DRILL_FMT => '2.4'; #'2.3'; #'2.4' is the default for PCB fab; change to '2.3' for CNC
use constant WANT_DEBUG => 0; #10; #level of debug wanted; higher == more, lower == less, 0 == none
use constant GERBER_DEBUG => 0; #level of debug to include in Gerber file; DON'T USE FOR FABRICATION
use constant WANT_STREAMS => FALSE; #TRUE; #save decompressed streams to files (for debug)
use constant WANT_ALLINPUT => FALSE; #TRUE; #save entire input stream (for debug ONLY)
#DebugPrint(sprintf("${\(CYAN)}DEBUG: stdout %d, gerber %d, want streams? %d, all input? %d, O/S: $^O, Perl: $]${\(RESET)}\n", WANT_DEBUG, GERBER_DEBUG, WANT_STREAMS, WANT_ALLINPUT), 1);
#DebugPrint(sprintf("max int = %d, min int = %d\n", MAXINT, MININT), 1);
#define standard trace and pad sizes to reduce scaling or PDF rendering errors:
#This avoids weird aperture settings and replaces them with more standardized values.
#(I'm not sure how photoplotters handle strange sizes).
#Fewer choices here gives more accurate mapping in the final Gerber files.
#units are in inches
use constant TOOL_SIZES => #add more as desired
(
#round or square pads (> 0) and drills (< 0):
.010, -.001, #tiny pads for SMD; dummy drill size (too small for practical use, but needed so StandardTool will use this entry)
.031, -.014, #used for vias
.041, -.020, #smallest non-filled plated hole
.051, -.025,
.056, -.029, #useful for IC pins
.070, -.033,
.075, -.040, #heavier leads
# .090, -.043, #NOTE: 600 dpi is not high enough resolution to reliably distinguish between .043" and .046", so choose 1 of the 2 here
.100, -.046,
.115, -.052,
.130, -.061,
.140, -.067,
.150, -.079,
.175, -.088,
.190, -.093,
.200, -.100,
.220, -.110,
.160, -.125, #useful for mounting holes
#some additional pad sizes without holes (repeat a previous hole size if you just want the pad size):
.090, -.040, #want a .090 pad option, but use dummy hole size
.065, -.040, #.065 x .065 rect pad
.035, -.040, #.035 x .065 rect pad
#traces:
.001, #too thin for real traces; use only for board outlines
.006, #minimum real trace width; mainly used for text
.008, #mainly used for mid-sized text, not traces
.010, #minimum recommended trace width for low-current signals
.012,
.015, #moderate low-voltage current
.020, #heavier trace for power, ground (even if a lighter one is adequate)
.025,
.030, #heavy-current traces; be careful with these ones!
.040,
.050,
.060,
.080,
.100,
.120,
);
#Areas larger than the values below will be filled with parallel lines:
#This cuts down on the number of aperture sizes used.
#Set to 0 to always use an aperture or drill, regardless of size.
use constant { MAX_APERTURE => max((TOOL_SIZES)) + .004, MAX_DRILL => -min((TOOL_SIZES)) + .004 }; #max aperture and drill sizes (plus a little tolerance)
#DebugPrint(sprintf("using %d standard tool sizes: %s, max aper %.3f, max drill %.3f\n", scalar((TOOL_SIZES)), join(", ", (TOOL_SIZES)), MAX_APERTURE, MAX_DRILL), 1);
#NOTE: Compare the PDF to the original CAD file to check the accuracy of the PDF rendering and parsing!
#for example, the CAD software I used generated the following circles for holes:
#CAD hole size: parsed PDF diameter: error:
# .014 .016 +.002
# .020 .02267 +.00267
# .025 .026 +.001
# .029 .03167 +.00267
# .033 .036 +.003
# .040 .04267 +.00267
#This was usually ~ .002" - .003" too big compared to the hole as displayed in the CAD software.
#To compensate for PDF rendering errors (either during CAD Print function or PDF parsing logic), adjust the values below as needed.
#units are pixels; for example, a value of 2.4 at 600 dpi = .0004 inch, 2 at 600 dpi = .0033"
use constant
{
HOLE_ADJUST => -0.004 * 600, #-2.6, #holes seemed to be slightly oversized (by .002" - .004"), so shrink them a little
RNDPAD_ADJUST => -0.003 * 600, #-2, #-2.4, #round pads seemed to be slightly oversized, so shrink them a little
SQRPAD_ADJUST => +0.001 * 600, #+.5, #square pads are sometimes too small by .00067, so bump them up a little
RECTPAD_ADJUST => 0, #(pixels) rectangular pads seem to be okay? (not tested much)
TRACE_ADJUST => 0, #(pixels) traces seemed to be okay?
REDUCE_TOLERANCE => .001, #(inches) allow this much variation when reducing circles and rects
};
#Also, my CAD's Print function or the PDF print driver I used was a little off for circles, so define some additional adjustment values here:
#Values are added to X/Y coordinates; units are pixels; for example, a value of 1 at 600 dpi would be ~= .002 inch
use constant
{
CIRCLE_ADJUST_MINX => 0,
CIRCLE_ADJUST_MINY => -0.001 * 600, #-1, #circles were a little too high, so nudge them a little lower
CIRCLE_ADJUST_MAXX => +0.001 * 600, #+1, #circles were a little too far to the left, so nudge them a little to the right
CIRCLE_ADJUST_MAXY => 0,
SUBST_CIRCLE_CLIPRECT => FALSE, #generate circle and substitute for clip rects (to compensate for the way some CAD software draws circles)
WANT_CLIPRECT => TRUE, #FALSE, #AI doesn't need clip rect at all? should be on normally?
RECT_COMPLETION => FALSE, #TRUE, #fill in 4th side of rect when 3 sides found
};
#allow .012 clearance around pads for solder mask:
#This value effectively adjusts pad sizes in the TOOL_SIZES list above (only for solder mask layers).
use constant SOLDER_MARGIN => +.012; #units are inches
#line join/cap styles:
use constant
{
CAP_NONE => 0, #butt (none); line is exact length
CAP_ROUND => 1, #round cap/join; line overhangs by a semi-circle at either end
CAP_SQUARE => 2, #square cap/join; line overhangs by a half square on either end
CAP_OVERRIDE => FALSE, #cap style overrides drawing logic
};
#number of elements in each shape type:
use constant
{
RECT_SHAPELEN => 6, #x0, y0, x1, y1, count, "rect" (start, end corners)
LINE_SHAPELEN => 6, #x0, y0, x1, y1, count, "line" (line seg)
CURVE_SHAPELEN => 10, #xstart, ystart, x0, y0, x1, y1, xend, yend, count, "curve" (bezier 2 points)
CIRCLE_SHAPELEN => 5, #x, y, 5, count, "circle" (center + radius)
};
#const my %SHAPELEN =
#Readonly my %SHAPELEN =>
our %SHAPELEN =
(
rect => RECT_SHAPELEN,
line => LINE_SHAPELEN,
curve => CURVE_SHAPELEN,
circle => CIRCLE_SHAPELEN,
);
#panelization:
#This will repeat the entire body the number of times indicated along the X or Y axes (files grow accordingly).
#Display elements that overhang PCB boundary can be squashed or left as-is (typically text or other silk screen markings).
#Set "overhangs" TRUE to allow overhangs, FALSE to truncate them.
#xpad and ypad allow margins to be added around outer edge of panelized PCB.
use constant PANELIZE => {'x' => 1, 'y' => 1, 'xpad' => 0, 'ypad' => 0, 'overhangs' => TRUE}; #number of times to repeat in X and Y directions
# Set this to 1 if you need TurboCAD support.
#$turboCAD = FALSE; #is this still needed as an option?
#CIRCAD pad generation uses an appropriate aperture, then moves it (stroke) "a little" - we use this to find pads and distinguish them from PCB holes.
use constant PAD_STROKE => 0.3; #0.0005 * 600; #units are pixels
#convert very short traces to pads or holes:
use constant TRACE_MINLEN => .001; #units are inches
#use constant ALWAYS_XY => TRUE; #FALSE; #force XY even if X or Y doesn't change; NOTE: needs to be TRUE for all pads to show in FlatCAM and ViewPlot
use constant REMOVE_POLARITY => FALSE; #TRUE; #set to remove subtractive (negative) polarity; NOTE: must be FALSE for ground planes
#PDF uses "points", each point = 1/72 inch
#combined with a PDF scale factor of .12, this gives 600 dpi resolution (1/72 * .12 = 600 dpi)
use constant INCHES_PER_POINT => 1/72; #0.0138888889; #multiply point-size by this to get inches
# The precision used when computing a bezier curve. Higher numbers are more precise but slower (and generate larger files).
#$bezierPrecision = 100;
use constant BEZIER_PRECISION => 36; #100; #use const; reduced for faster rendering (mainly used for silk screen and thermal pads)
# Ground planes and silk screen or larger copper rectangles or circles are filled line-by-line using this resolution.
use constant FILL_WIDTH => .01; #fill at most 0.01 inch at a time
# The max number of characters to read into memory
use constant MAX_BYTES => 10 * M; #bumped up to 10 MB, use const
use constant DUP_DRILL1 => TRUE; #FALSE; #kludge: ViewPlot doesn't load drill files that are too small so duplicate first tool
my $runtime = time(); #Time::HiRes::gettimeofday(); #measure my execution time
print STDERR "Loaded config settings from '${\(__FILE__)}'.\n";
1; #last value must be truthful to indicate successful load
#############################################################################################
#junk/experiment:
#use Package::Constants;
#use Exporter qw(import); #https://perldoc.perl.org/Exporter.html
#my $caller = "pdf2gerb::";
#sub cfg
#{
# my $proto = shift;
# my $class = ref($proto) || $proto;
# my $settings =
# {
# $WANT_DEBUG => 990, #10; #level of debug wanted; higher == more, lower == less, 0 == none
# };
# bless($settings, $class);
# return $settings;
#}
#use constant HELLO => "hi there2"; #"main::HELLO" => "hi there";
#use constant GOODBYE => 14; #"main::GOODBYE" => 12;
#print STDERR "read cfg file\n";
#our @EXPORT_OK = Package::Constants->list(__PACKAGE__); #https://www.perlmonks.org/?node_id=1072691; NOTE: "_OK" skips short/common names
#print STDERR scalar(@EXPORT_OK) . " consts exported:\n";
#foreach(@EXPORT_OK) { print STDERR "$_\n"; }
#my $val = main::thing("xyz");
#print STDERR "caller gave me $val\n";
#foreach my $arg (@ARGV) { print STDERR "arg $arg\n"; }
Author: swannman
Source Code: https://github.com/swannman/pdf2gerb
License: GPL-3.0 license
1675827904
An example WPF HelloWorld program written using the ReactiveUI framework.
Original: 3 February 2019
Updated: 5 February 2019
I had an unexpectedly difficult time figuring out how to get a simple starter program working using ReactiveUI. I found some documentation sources useful, and others confusing, or lacking SIMPLE examples for the current software versions.
The current ReactiveUI Getting Started page is pretty decent, but either I missed it when I started, or it wasn't as good then and has improved since. I remember trying to figure stuff out from the DynamicTrader program, which is a wonder to behold, but I found myself hopelessly confused by all the advanced stuff going on in there.
So, I thought I'd post my working example here on github. It's even more basic than the 'Getting Started' example. No network calls or even any input. Just a slightly fancy Hello World that does make use of a few ReactiveUI components.
I'm still learning a lot of this stuff (C#, ReactiveX, ReactiveUI), so I can't yet say I have a firm understanding of it all, even all the parts used in this simple example. This code works, but I wouldn't be at all surprised if someone more experienced could point out at least a few 'bad' things in it.
(My biggest problem was getting the bindings to work. I think that was because I was using a version of the Splat service locater code in my app.xaml.cs file that was incompatible with the rest of my code. I had tried using different versions of that line that I copied from various documentation sources/examples, but not evidently ones that were in sync with the rest of my code. The sample that finally solved my problem was one of the two buried in the ReactvieUI source code itself - ReactiveDemo. This is the code the ReactiveUI 'Getting Started' page currently walks you through.)
What it does:
Opens a Window that shows the greeting "Hello World", cycling between a few different languages.
I'm using Visual Studio 2019 Enterprise Preview. I assume VS2017 Community Edition would work just as well. I'll likely switch to VS2019 Community Edition when the preview expires.
You'll need to install the following NuGet Packages into your environment. They'll in-turn load others that they require.
Look in the packages.config file to see a complete list of current packages and version numbers. (Don't be surprised if the README doesn't stay up to date with the actual repo :)
(Note: I perhaps complicated the example a bit by using Fody, but its pretty straitforward to use, has given me very little trouble, and makes the code look cleaner.)
Nothing really notable here. Standard WPF layout created by the Visual Studio project creater.
Additions to the base template are:
Namespaces
These namespaces are used by the Locator statement below.
using ReactiveUI;
using Splat;
using System.Reflection;
App Class Constructor
Add the following constructor code to the boilerplate produced by VS:
public App()
{
Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());
}
Note
Window properties
<reactiveui:ReactiveWindow
x:Class="HelloWorldRUI.MainWindow"
x:TypeArguments="helloworldrui:AppViewModel"
xmlns:helloworldrui="clr-namespace:HelloWorldRUI"
xmlns:reactiveui="http://reactiveui.net"
...
Title="HelloWorld RUI">
...
</reactiveui:ReactiveWindow>
Window Layout
In this example we are only going to display two strings, each in its own TextBlock. In a normal WPF project you'd bind the UI elements to ViewModel properties explicitly within the XAML. Here, we simply ensure that each TextBlock has a unique name. The binding is done in the MainWindow's constructor code, which references the x:Name values.
<StackPanel>
<TextBlock x:Name="GreetingTextBlock" />
<TextBlock x:Name="LangTextBlock" />
</StackPanel>
In the HelloWorldRUI project, I've left both the View and ViewModel code in the same file. This is not normally done, but it doesn't hurt anything, and I think makes it a little easier to read as an example.
In a WPF MVVM program, the View class constructor is normally responsible for calling InitializeComponent() and creating an instance of the ViewModel. In this ReactiveUI code the constructor also binds the View and ViewModel properties, as shown below.
View Code
using ReactiveUI;
using System.Reactive.Disposables;
namespace HelloWorldRUI
{
public partial class MainWindow : ReactiveWindow<AppViewModel>
{
public MainWindow()
{
InitializeComponent();
ViewModel = new AppViewModel();
this.WhenActivated(d => {
this.OneWayBind(ViewModel, viewModel => viewModel.Lang,
view => view.LangTextBlock.Text).DisposeWith(d);
this.OneWayBind(ViewModel, viewModel => viewModel.Greeting,
view => view.GreetingTextBlock.Text).DisposeWith(d);
});
}
}
}
ViewModel Code
Assuming you understand Reactive Observable pipelines, this code is reasonably straightforward.
Using Fody slightly changes the declarations of the Reactive properties and ObservableAsPropertyHelpers compared to how you've probably seen it described in ReactiveUI documentation. Setting ObservableAsPropertyHelper values using ToProperty() changes slightly as well. See the Fody readme (referenced farther below) for a more detailed explanation.
The Observable.Interval pipeline creates an observable that fires every 2 seconds, eventually resulting in changes to the Language displayed on the UI. The take(100) function terminates the pipeline after 100 iterations. The Select() function specified will increment the Lang to the next one in the Greetings dictionary. ObserveOn(RxApp.MainThreadScheduler) needs to be used when changing any property bound to the View. ToPropertyEx() will set the value of the ObservableAsProperty Lang to the language output in the Select() function. Since Lang is bound to the LangTextBlock in the View, the UI will automatically reflect this change. The initial value of the Observable Lang is passed as an argument to ToPropertyEx(), as it cant be set otherwise.
The final observable pipeline will take care of updating the Greeting. This pipeline is set to 'tick' whenever the ViewModel's Lang property changes (as specified by the WhenAnyValue function). The Where() clause ignores any values that are not contained in the Keys array. The Select() function looks up the Greeting associated with the Lang. Again, ObserveOn makes sure our change is visible to the UI, and the Subscribe() function simply sets the Reactive property Greeting with the result created by Select. Since Greeting is a 'simple' Reactive property (not an Observable) you can just set its value. Its initial value is specified on its declaration line.
(Experiment: try removing one of the ObserveOn lines and see what happens. It's useful for future reference to see for yourself how that error manifests.)
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace HelloWorldRUI
{
public class AppViewModel : ReactiveObject
{
[Reactive] public string Greeting { get; set; } = "Greeting";
public extern string Lang { [ObservableAsProperty] get; }
public AppViewModel()
{
Dictionary<string, string> Greetings = new Dictionary<string, string>() {
{ "English", "Hello World!" },
{ "French", "Bonjour le monde!" },
{ "German", "Hallo Welt!" },
{ "Japanese", "Kon'nichiwa sekai!" },
{ "Spanish", "¡Hola Mundo!" },
};
string[] keys = Greetings.Keys.ToArray();
// select next language every 2 seconds (100 times)
Observable.Interval(TimeSpan.FromSeconds(2))
.Take(100)
.Select(_ => keys[(Array.IndexOf(keys, Lang) + 1) % keys.Count()])
.ObserveOn(RxApp.MainThreadScheduler)
.ToPropertyEx(this, x => x.Lang, "Language");
// update Greeting when language changes
this.WhenAnyValue(x => x.Lang)
.Where(lang => keys.Contains(lang))
.Select(x => Greetings[x])
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => Greeting = x);
}
}
}
If you create these source files by hand, you may get an error when building the project the first time indicating that the FodyWeavers.xml file couldn't be found and had to be generated for you. In that case simply rebuild the project and the error should resolve itself. (Rebuilding after the FodyWeavers file was created also resolved an Intellisense error I had in the MainWindow.xaml file which claimed it couldn't find the AppViewModel.)
Integration of ReactiveUI and Fody with Intellisense seems to be incomplete or slow at times. Occasionally I think I found Intellisense reported errors which resolved themselves by rebuilding the project.
They're updating their website [~1/20/19], so some of these links may break.
Links:
The project source code has been absorbed into the ReactiveUI GitHub repo, but I haven't found the README file from the original repo in ReactiveUI. The original project README has an example of how to use it.
Links:
Reactive Extensions (ReactiveX) is a project which provides Reactive libraries for a number of programming languages and platforms. One such supported combination is C#/DotNet (aka the System.Reactive package). The base form of Reactive programming has no direct support for Graphical User Interfaces (thats where ReactiveUI comes in.) Its been around for quite a while, and there's some pretty good documentation for it, though not necessarily all up to date for C#/DotNet. However the older stuff is still VERY useful. (You may notice that some of the maintainers of dotnet/reactive are also the same people supporting ReactiveUI)
Links:
At the time I'm writing this, this book is the only organized, well written, reasonably complete reference/tutorial for ReactiveUI that I could find. I'm still working my way through it, and I'm finding it very useful. (Thanks to the author for making the effort to write and publish it!)
The biggest shortcoming of the book, for me as a newcomer, was the way the sample code was organized. I found that it made it very difficult for me to observe and emulate each example as a standalone application.
I offer the following observations for my fellow noobs:
Links
Author: richk1
Source code: https://github.com/richk1/HelloWorldRUI
License: MIT license
#ReactiveUI #csharp
1591676029
Android Hello World example in Android Studio – In this tutorial, we’ll show you how to start android development with the very first android project.
As a beginner first of all you must develop the hello world application like we do while learning a new language. This project doesn’t involve any business logic rather it ensures whether our development, as well as the deployment environment, is working. This is indeed a very simple android project and you do not really need to do a lot of coding here.
#android tutorials #android hello world #android project #android studio hello world #first android app #hello world program in android