Quan anem d'excursió amb la càmera portem una motxilla amb altres coses a més amés de la càmera. Però a vegades si anem a fer un passeig, no ens volem complicar la vida amb la motxilla, ens cal alguna funda més petita i menys molesta. Teníem una Lowepro de l'any de la picor, quan la càmera era més petita i l'objectiu també era més petit, així que no servia. La vaig agafar per anar al Born i només portava el EF 50mm F1.4, però el EF 24-105 F4 IS L no hi cap. Per això hem comprat aquesta ToploaderZoom TRZ AW 50. Ens ha costat 26EUR a Jordi Bas. I la càmera cap sense problemes. Ara la podré treure a passejar sense motxilles molestes. Aprofitant la compra hem agafat 2 bateries de recanvi, aquest cop sense marca (genèriques) a veure que tal surten. Les Canon han durat uns 5 anys, però ara es gasten molt depresa.
Gràcies al meu nou monitor estic descobrint coses lletges de la programació, i coses lletges de la navegació. Gràcies a que el nou monitor té High DPI veig les coses més nítides, però també descobreixo que no tot està preparat per funcionar amb High DPI. Ja he vist que les aplicacions d'escriptori en Winforms no van bé, però ara resulta que al navegar també hi ha defectes.
Normalment quan el navegador carrega una imatge, la mostra a 1px de la imatge 1px de la pantalla, però jo estic al 125% d'augment, els píxels són més petits, i clar, cal fer un estirament del 25%. Quan les imatges són suficientment grosses, i són fotos aquest 25% no es nota. El problema ve quan les imatges no són tan grosses i contenen text, llavors el text es torna borrós. I jo per fer uns pocs dibuixos per explicar quatre coses sobre programació faig servir text, i clar, al meu xupimonitor es veuen malament, i això és en quatre paraules in-to-lera-ble, inclús afegiria inacceptable.
Remenant pel Internets he trobat una forma de generar imatges i que es vegin bé a qualsevol resolució. Primer he vist l'element HTML5 canvas, però el "canvas" no deixa de ser un bitmap on es dibuixa (normalment amb Javascript) i depenent del navegador pateix de problemes d'escalat a HighDPI. Després he vist el format bo, el SVG, que guarda la imatge com un conjunt de vectors. Clar com són vectors si cal estirar un 25% la figura no hi ha problema, es torna a calcular el resultat i llestos. El problema està en editar els SVG ja que és un llenguatge força espès, cal fer servir un editor de SVG. He trobat el InkScape que és un editor gratuït que he pogut fer servir. No és que sigui la meravella de l'usabilitat però és força acceptable per ser de codi lliure.
Un cop es té el SVG hi ha varies possibilitats per posar-lo a la pàgina. Els navegadors entenen el tag svg, per tant es pot posar dins del HTML directament (obrir amb editor de texts el SVG i copiar i enganxar), és el que s'anomena "inline". O bé fer servir el tag img, especificant en l'atribut src el fitxer .svg. De moment he provat la versió "inline", aquí amb molts bons resultats.
Cal tenir en compte que ara vindrà una onada de dispositius HighDPI, els nous monitors 4K. Als que cal sumar els dispositius que ja són HighDPI, com els nous SmartPhones i Tablets. Per això amb paciència aniré modificant els meus dibuixos per fer-los amb SVG... Si es que se m'acumula la feina.
#20/02/2014 11:42 Software HTML/CSS Autor: Alex Canalda
La clsDades segueix creixent i cada cop es sembla més a una navalla suïssa de les bases de dades. Els mètodes del primer post d'aquesta sèrie executen un SQL i retornen una taula, els mètodes que poso a continuació són els encarregats d'executar SQLs que no retornen una taula com a resultat. Normalment són els SQL relacionats amb INS/UPD/DEL que retornen el nombre de registres afectats. Aquesta funció està disponibles en tres sabors, depenent del detall que volem, la primera que rep un SQL i la funció ja s'apanya a executar-lo, la segona que rep la connexió sobre la que executar l'SQL i la tercera que també rep la transacció. Com sempre el codi:
/// <summary>/// Executa una sentència SQL que no retorna resultats, /// només els registres afectats. /// Obre i tanca conexió a BBDD (la que retorna GetConnStr)/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <returns>Registres afectats</returns>publicint ExecNonQuery(string SQL)
{
int Results = 0;
SqlConnection localSqlConn;
bool Local = false;
if (SQLConn != null)
{
localSqlConn = SQLConn;
}
else
{
Local = true;
localSqlConn = newSqlConnection();
localSqlConn.ConnectionString = CadConnBBDD;
localSqlConn.Open();
}
Results = ExecNonQuery(SQL, localSqlConn);
if (Local) localSqlConn.Close();
return Results;
}
/// <summary>/// Executa una sentència SQL que no retorna resultats/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <returns>Registres afectats</returns>publicint ExecNonQuery(string SQL, SqlConnection pSqlConn)
{
return ExecNonQuery(SQL, pSqlConn, SQLTrans);
}
/// <summary>/// Executa una sentència SQL que no retorna resultats/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <param name="SqlTrann">Transacció ja creada sobre la conexió (admet null)</param>/// <returns>Registres afectats</returns>publicint ExecNonQuery(string SQL, SqlConnection SqlConn, SqlTransaction SqlTrann)
{
int Results = 0;
SqlCommand SqlComm = newSqlCommand();
SqlComm.Connection = SqlConn;
if (SqlTrann != null) SqlComm.Transaction = SqlTrann;
SqlComm.CommandText = SQL;
SqlComm.CommandType = CommandType.Text;
Results = SqlComm.ExecuteNonQuery();
return Results;
}
#19/02/2014 17:10 Programació C# Autor: Alex Canalda
Acabo de llegir aquest llibre, que és força gruixut. L'acció es situa a l'any 1200 i tracta de les aventures de Diego de Malagón, un albeitar o veterinari. En aquell temps el cavalls eren molt importants ja que eren com el cotxe...
[Spoilers!] El llibre en sí és d'aventures, lleugeret de llegir i sense gaires complicacions, per tal de que no es faci massa "fàcil" de tant en tant maten personatges secundaris per donar-li algo d'intrigulis. El personatge principal és massa heroic i ple de virtuts al meu gust, però bueno. El guió està bé, però es podria haver ambientat la mateixa història en un altra època i funcionaria igual. I el final és la batalla de Navas de Tolosa, on la victòria cristiana va fer que actualment parlem castellà i no àrab.
I quan acaba la batalla tot és molt rosa, troba a l'estimada i l'anomenen cavaller (però seguint el patró del llibre li maten a la seva estima euga Sabba, perque no sigui extra-rosa)... Gairebé em recorda a StarWars amb la cerimònia final repartint medalles, en comparació a los Reyes Malditos aquest és força light...
Al principi de fer el blog em vaig plantejar en quin idioma escriure. Vaig escollir el català per comoditat, ja que és el que parlo i penso. Però després vaig pensar que també en anglès, ja que quina millor forma de demostrar algo que fer-ho. Sempre penso en els currículums inflats que veig de tant en tant i que Einstein era un mindundi al costat, però que després alhora de fer algo fan llufa. Així que alehop! Blog multiidioma.
Això a nivell de software suposa varies coses, primer afegir a tots els posts un camp idioma, a les categories també. I després a la part de manteniment del blog cal afegir el corresponent combo amb l'idioma i que carregui en cascada les categories del idioma sel·leccionat. Dins de la pàgina que mostra el blog hi ha un switch-case on posa a tots els texts del blog el que toca segons l'idioma.
A nivell d'HTML5 l'idioma es pot definir a diferents nivells, jo l'he posat a nivell de pàgina on queda així:
<!DOCTYPE html>
<html lang="ca">
[.....]
Com a última cosa és que tinc que re-escriure els posts amb anglés, en HTML serà lang="en"... Yummi! Això ja dependrà de les ganes i el temps disponible.
#19/02/2014 11:34 Programació HTML/CSS Autor: Alex Canalda
La clsDades és el punt de l'aplicació web que per una banda dóna servei als ASHX mitjançant les classes derivades i per l'altra interactua amb les Stored Procedures (SP) de la BBDD. Però a vegades una consulta, o una operació no s'ajusta a la forma d'operar de les SP i per això es fa una consulta adhoc directament al codi. Però per evitar muntar tota la infraestructura necessària per accedir i executar la consulta he ampliat la clsDades. També hi ha tasques i problemes que es van donant en la interacció amb la BBDD de forma habitual, també he posat els mètodes corresponents a la clsDades. Diguem que ara té una part "oficial" amb el GET, GETTOP, INS, UPD, DEL, QUERY i una d'utilitats que aniré posant.
Un del punts problemàtics alhora de interactuar amb la BBDD és l'idioma, que si l'idioma del webserver, que l'idioma de la BBDD, etc... La solució és fer servir un format de data que sigui independent de l'idioma, per exemple el ISO 8601. Per això he fet una mini funció que genera una data en aquesta format.
Altres funcions són un xic més complicades, per exemple, executar una SQL i retornar una DataTable. Aquesta està disponible en tres sabors: un que només rep la sentència SQL que s'ha d'executar, una altra SQL i connexió i la tercera SQL, connexió i transacció. Com sempre el codi:
/// <summary>/// Executa una SQL que retorna resultats contra una conexió, no modifica l'estat de la conexió ni de la transaccio/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <param name="SqlTrann">Transacció ja creada sobre la conexió (admet null)</param>/// <returns>Retorna un NEW DataTable</returns>publicDataTable Exec(string SQL, SqlConnection pSqlConn, SqlTransaction pSqlTrann)
{
DataTable TBL = newDataTable();
SqlCommand SqlComm = newSqlCommand();
SqlDataReader SqlReader;
SqlComm.Connection = pSqlConn;
if (pSqlTrann != null) SqlComm.Transaction = pSqlTrann;
SqlComm.CommandText = SQL;
SqlComm.CommandType = CommandType.Text;
SqlReader = SqlComm.ExecuteReader();
TBL.Load(SqlReader);
return TBL;
}
/// <summary>/// Executa una SQL que retorna resultats contra una conexió, no modifica l'estat de la conexió/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <param name="SqlConn">Conexió a BBDD, ha d'estar oberta</param>/// <returns>Retorna un NEW DataTable</returns>publicDataTable Exec(string SQL, SqlConnection pSqlConn)
{
return Exec(SQL, pSqlConn, SQLTrans);
}
/// <summary>/// Executa una sentència SQL que retorna resultats, /// si està informada la variable Conn la fa servir de conexio (ha d'estar oberta), si no crea una i la tanca/// </summary>/// <param name="SQL">Sentència SQL a executar</param>/// <returns>Retorna un NEW DataTable</returns>publicDataTable Exec(string SQL)
{
DataTable TBL;
SqlConnection localSqlConn;
bool Local = false;
if (SQLConn != null)
{
localSqlConn = SQLConn;
}
else
{
Local = true;
localSqlConn = newSqlConnection();
localSqlConn.ConnectionString = CadConnBBDD;
localSqlConn.Open();
}
TBL = Exec(SQL, localSqlConn);
if (Local) localSqlConn.Close();
return TBL;
}
Altres métodes que segueixen ampliant la clsDades les tractare en futurs posts.
#17/02/2014 12:31 Programació C# Autor: Alex Canalda
M'he animat i he posat RSS al blog, ja que també m'ho han demanat. Doncs dit i fet. El cas és que tampoc és gaire difícil fer-ho. Hi ha unes classes de .NET SyndicationFeed, que ho fan fàcil. Cal fer un ASHX i posar el "using System.ServiceModel.Syndication;", després un parell de bucles, es recupera de la BBDD amb la clsDades els posts i voilà. Com sempre el codi.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Syndication;
using System.IO;
using System.Xml;
using System.Data;
using Blog.data;
namespace Blog
{
/// <summary>/// Summary description for RSS/// </summary>publicclass RSS : IHttpHandler
{
private clsConfig Conf = new clsConfig();
privatestring ConnStr = "";
//Cal posar al Blog.aspx://<link rel="alternate" type="application/rss+xml" href="RSS.ashx" title="Blog Feed"/>publicvoid ProcessRequest(HttpContext context)
{
Conf.Initialize();
ConnStr = Conf.GetVal("CadConn");
SyndicationFeed feed = CreateRecentPostsFeed();
var output = new StringWriter();
var writer = new XmlTextWriter(output);
new Rss20FeedFormatter(feed).WriteTo(writer);
context.Response.ContentType = "application/rss+xml";
context.Response.Write(output.ToString());
}
private SyndicationFeed CreateRecentPostsFeed()
{
List<SyndicationItem> syndicationItems = GetPostsRecents();
returnnew SyndicationFeed(syndicationItems)
{
Title = new TextSyndicationContent("Blog"),
Description = new TextSyndicationContent("Una mica de tot i a vegades més"),
ImageUrl = new Uri("http://blog.canalda.net/Img/laughing_man_p.jpg")
};
}
privateList<SyndicationItem> GetPostsRecents()
{
List<SyndicationItem> Posts = newList<SyndicationItem>();
clsPosts PostsTA = new clsPosts(ConnStr);
Dictionary<string, string> Params = newDictionary<string, string>();
DateTime FPubs = DateTime.Now;
DataTable PostsTBL;
FPubs = FPubs.Subtract(TimeSpan.FromDays(7));
Params.Add("DataPubMesGran", PostsTA.DateTimeToSQL(FPubs, "0"));
Params.Add("Actiu", "true");
PostsTBL = PostsTA.GET(Params);
foreach (DataRow Post in PostsTBL.Rows)
{
Posts.Add(GeneraSyndicacioPost(Post));
}
return Posts;
}
private SyndicationItem GeneraSyndicacioPost(DataRow DR)
{
string id = DR["IdPost"].ToString();
string title = String.Format("Post: {1}", id, DR["Titol"]);
string content = DR["PostText"].ToString();
DateTimeOffset DataPub = new DateTimeOffset((DateTime) DR["DataPub"]);
Uri url = new Uri(String.Format("http://blog.canalda.net/Post.aspx?IdPost={0}", id));
returnnew SyndicationItem(title, content, url, id, DataPub);
// { Summary = new TextSyndicationContent(content) };
}
publicbool IsReusable
{
get
{
returnfalse;
}
}
}
}
#17/02/2014 10:51 Programació C# Autor: Alex Canalda
Ja he pogut veure que a alts DPI les aplicacions Winforms es mostren malament.
Buscant solucions pels Internetes diuen que amb un fitxer Manifest la cosa s'arregla, jo ho he provat i res de res. Al final m'he cansat de perdre temps amb el Manifest i l'he enviat a pastar.
Una altra solució és modificar les propietats d'execució de l'aplicació, potser és la més sencilla per aplicacions de les que no es disposa el codi font.
En canvi si es disposa del codi font i no es vol preocupar-se de si l'opció està marcada o no es pot afegir el següent. Totes les aplicacions Winforms tenen un fitxer que es diu Program.cs que és on s'especifica el punt d'entrada al programa, i queda tal que:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace CodeGenerator
{
staticclass Program
{
/// <summary>/// The main entry point for the application./// </summary>
[STAThread]
staticvoid Main()
{
if (Environment.OSVersion.Version.Major >= 6) SetProcessDPIAware();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmStoredGenerator());
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
privatestaticexternbool SetProcessDPIAware();
}
}
Es a dir s'afegeix una crida a SetProcessDPIAware. Curiosament aquest funció està definida a la DLL user32.dll. No cal que ens amoïnem de que l'aplicació que estem fent sigui de 64bits, aquesta DLL és de 64bits, encara que el nom enganyi. Suposo que hi ha tantes aplicacions que la tenen referenciada que no va haver-hi collons de canviar-li el nom a user64.dll i que l'univers es col·lapses.
Amb això s'aconsegueix que les aplicacions es vegin bé, però com s'ha mogut el sistema de coordenades és molt possible que els controls (encara que es vegin nítids) estiguin mal col·locats. Hauré de buscar una solució per això altra.
#13/02/2014 12:17 Programació C# Autor: Alex Canalda
Fins que no he tingut el nou monitor he viscut a 96DPI (i jo sense saber-ho), ara ja entro en un nou món: el de alts DPI. Diguem que 96DPI és el 100%, i que segons augmentem aquest percentatge augmenta la mida del que es veu per pantalla. Seria similar a fer Ctrl i '+' en el navegador.
I que és això dels DPI? El DPI és una unitat de mesura que indica quants píxels hi ha en una polzada. També és indicatiu de la mida que té un píxel, ja que pocs píxels en un espai gran vol dir píxels grossos, i al contrari, molts píxels en un espai petit vol dir píxels petits.
Els DPI afecten a com es mostren les aplicacions, els texts, tot per pantalla. Per exemple, suposem que una lletra ocupa 12 píxels d'alçada, això en un monitor vol dir 8 mili metres, però en un altra on els píxels són més grans potser aquests 12px són 12mm, o en un on són més petits 6mm. Clar, això que l'aplicació canvii de mida no mola, ja que si tenim un monitor amb píxels petits ens deixarem la vista, a més que no l'aprofitarem. Lo ideal és disposar de píxels petits (quan més petits millor), però que el software faci més grans els elements de pantalla (textos, controls, ...). Que el software faci les coses més grans es diu escalat i es mesura en percentatge respecte a 96DPI, que es considera el 100%, un 200% és el doble de gran.
Quan es fa un escalat sorgeixen problemes, intenteu agafar una imatge de 1000px i convertir-la a una de 2000px... No es veu nítida. A les aplicacions els passa el mateix, hi ha aplicacions que no escalen bé ja que parteixen d'unitats de mesura basades en píxels, altres escalen bé perquè parteixen de fonts basades en vectors... Les aplicacions d'escriptori, (diferents de les aplicacions web), normalment les faig en Winforms, que es basen píxels i per tant escalen malament... Ara tinc que buscar una solució a això.
En una altra aplicació també Winforms, amb una pantalla molt més complicada, amb molts més controls, tots han sortit moguts. He perdut una estona posant-los a lloc i finalment s'han vist bé a alts DPI, però m'he portat el projecte a un ordinador amb baixos DPI i el formulari estava desquadrat. Cal doncs dissenyar dos cops el formulari? Pffffff, haig de pensar algo.
#11/02/2014 18:16 Programació Software Autor: Alex Canalda