Últimas Publicaciones

LibreOffice hace crash cuando hago copia-pega

Es un bug realmente enojoso, porque habitualmente todo el mundo hace copia-pega de gráficos, tablas, literalmente de todo cuando hace diapositivas con Impress. No hay mensajes de error de ningún tipo, LibreOffice simplemente se cierra y pierdes todos los cambios que no hayas hecho en disco. Si arrancas el programa desde consola (precisamente para ver cualquier hipotético error), verás que no hay información ninguna. Haces un copy o un paste y el programa se cierra sin mensaje de error ninguno, aunque si tienes forma de capturar códigos de error verás que se cierra con código 139, es decir un SEGFAULT en toda regla.

Este fallo me aparece en Debian 8.6 Jessie, pero por lo que he visto por ahí es bastante ubicuo y aparece en todo tipo de sistemas, en múltiples versiones de LibreOffice, tanto en Linux como en Windows, y no he visto ningún lugar donde se aporte solución. Te dicen que elimines con fichero "~/.config/openoffice/4/user" y que vuelvas a intentarlo. Y efectivamente, durante unos minutos parece que funciona, pero luego vuelven a aparecer los crash.

Es un bug conocido con múltiples quejas pero no he visto workaround alguno, así que la solución que te propongo es de cosecha propia. :-)

La pista me la dio cuando observé que hacía crash sistemáticamente al copiar objetos relativamente complicados pero me permitía copiar objetos muy simples (un poco de texto, por ejemplo). ¿Qué es eso?. ¿Se queda sin memoria?.

Vale, vete a Herramientas -> Opciones -> LibreOffice -> Memoria.

Ahí verás varias opciones que se refieren a cuantos recursos pilla LibreOffice del sistema. Y parece todo como bastante escaso. Recorta pasos de deshacer y amplía la memoria caché. Yo puse esto:

  • Deshacer
    • Número de pasos: 20
  • Caché de Imágenes
    • Usar para LibreOffice: 100 Mb
    • Memoria por objeto: 20 Mb
    • Eliminar de la memoria después de: 00:10 hh:mm
  • Caché para los objetos insertados
    • Número de objetos: 20

Oye, y a la primera. En cuanto puse esos parámetros, LibreOffice empezó a funcionar bien y hacer sin problemas los copia-pega. Ya te diré como va la cosa si surgen novedades.

 

Crear una tabla dinámica, por código en vez de usar XML

A veces necesitas crear una tabla de forma dinámica, por código. Por ejemplo, si quieres mostrar resultados de una base de datos en una pantalla con ScrollView, no deberías hacerlo mediante ListView ni mediante GridView porque incorporan sus propios controles de scroll, lo que interfiere con los de ScrollView. La solución más práctica es poner una tabla. Pero si lo que muestra la tabla procede de una base de datos, a priori no sabes cuántas filas debes poner, con lo que no puedes usar XML sino código. No se si me explico.

Crear una tabla por código es bastante fácil. En primer lugar tienes que crear una tabla vacía en XML. Vacía, sin fila alguna.

<TableLayout
            android:id="@+id/tablacultivos"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:stretchColumns="2"
            android:shrinkColumns="2" >
</TableLayout>
Vale, ahora nos vamos al código. Vamos a crear una tabla con 3 columnas y n filas. Primero enlazamos con la tabla y creamos unas cuantos patrones Layout básicos: para la fila y para cada una de las columnas.
TextView txtCultivos=(TextView) getView().findViewById(R.id.LblCultivos);
TableLayout tabladatos=(TableLayout) getView().findViewById(R.id.tablacultivos);
TableRow.LayoutParams layoutFila=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutFecha=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutMuestra=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutResultado=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
Como ves, vamos a poner las tres columnas y la fila todas iguales: con anchura WRAP_CONTENT y altura WRAP_CONTENT;
Ahora creamos variables para la fila y las columnas.
TableRow fila;
TextView txtFecha;
TextView txtMuestra;
TextView txtResultado;
Es conveniente que reseteemos la tabla a cero. En mi experiencia, si entras en una tabla dinámica todo bien. Pero si sales y vuelves a entrar, las filas se añaden otra vez con lo que aparecen duplicadas. Para evitarlo, empieza reseteando la tabla.
tabladatos.removeAllViews();
Ahora tienes que consultar tu base de datos y obtener un cursor. Eso es material para unos cuantos temas aparte y no te lo voy a explicar aquí. Si no sabes cómo consultar SQLite ni lo que es un cursor, nada de todo esto tiene sentido. Así que vete a tu base de datos, consúltala y obtén un listado en forma de Cursor. ¿Vale?.
De acuerdo, ya tienes un Cursor. Ahora vamos a recorrerlo y al tiempo vamos creando la tabla añadiendo una fila nueva en cada pasada. Observa que para añadir la fila hay que crear los TextView, puedes alterar luego sus propiedades con setGravity, setPadding, etc. También tienes que añadir los parámetros que creaste al principio. Y por último, añades los TextView al layout de fila. Y para terminar añades el layout de Fila a la tabla. Esto se repetirá una vez por pasada a medida que avanzamos por el cursor. Pongo sólo el código relevante.
No me digas que no es fácil.
if (cursor.moveToFirst()) {
         do {
               fila=new TableRow(this); // Si, necesita contexto
               fila.setLayoutParams(layoutFila);

               txtFecha=new TextView(this);
               txtMuestra=new TextView(this);
               txtResultado=new TextView(this);

               txtFecha.setText(cursor.getString(0));
               txtFecha.setGravity(Gravity.RIGHT);
               txtFecha.setPadding(0, 0, 5, 0);
               txtFecha.setLayoutParams(layoutFecha);

               txtMuestra.setText(cursor.getString(1));
               txtMuestra.setPadding(0,0,5,0);
               txtMuestra.setLayoutParams(layoutMuestra);

               txtResultado.setText(cursor.getString(2));
               txtResultado.setLayoutParams(layoutMuestra);
               fila.addView(txtFecha);

               fila.addView(txtMuestra);
               fila.addView(txtResultado);

               tabladatos.addView(fila);
         } while (cursor.moveToNext());
}

Variables globales en Android

Vale, estás haciendo un programa Android y a veces te interesa tener variables globales, accesibles desde cualquier Activity. Una forma muy sencilla de hacerlo es crear una clase que extienda de Application. Con esta clase, podrás almacenar cualquier cosa, desde variables básicas hasta objetos completos, lo que resulta muy práctico. Crea un fichero que contenga una clase tal que así:

public class ClaseGlobal extends Application {
       private final static int versiondb=1;
       private int posicionlistview;
       private int toplistview;
       private FichaPaciente paciente;
       private FichaEnvin envin;

       public void ponPaciente(FichaPaciente p) {
                paciente=p;
       }
       public FichaPaciente dimePaciente() {
                return paciente;
       }
       // etc...
}

Para simplificar, solo he puesto los métodos para meter y extraer un objeto de clase FichaPaciente, pero sería lo mismo para objetos de otra clase o variables básicas.

Para poder utilizar la clase ClaseGlobal sólo tendríamos que instanciarla en cualquier Activity. No se instancia de la forma normal, teniendo en cuenta que funcionará un poco a lo Singleton, es decir, siempre se instanciará el mismo objeto de forma global.

ClaseGlobal global=(ClaseGlobal) getActivity().getApplicationContext();
global.ponPaciente(paciente);

Y ya.

Recordar la posición de un ListView

Si cambias a otra Activity, cuando regreses tu ListView habrá perdido la posición. Eso, a veces puede resultar bastante desconcertante. Una solución nos la cuentan en StackOverflow y es bastante sencilla de aplicar.

Puedes recoger la posición actual cuando abandonas la Activity obteniendo dos valores integer en tu onPause.

@Override 
public void onPause() {
	super.onPause();
	int index=lstListado.getFirstVisiblePosition();

	View v=lstListado.getChildAt(0);
	int top=(v==null) ? 0:(v.getTop()-lstListado.getPaddingTop());

	global.ponPosicion(index);
	global.ponTop(top);
}

Observa que que usamos la variable index para saber cual es la entrada que se veía en primer lugar en el listview. Pero puede ocurrir que esa entrada se viera a medias, así que usamos una segunda variable top para obtener cuánto se mostraba de ella.

Después usamos el método que queramos para guardar ambos valores. En mi caso, he almacenado ambos int usando variables globales, pero ese es otro tema para otra entrada.

Y ahora en el onResume, recogemos ambos valores y con ellos usamos el método setSelectionFromTop para poner el ListView como antes:

@Override
public void onResume() {
        super.onResume();
        int index=global.dimePosicion();
        int top=global.dimeTop();
        lstListado.setSelectionFromTop(index, top);
}
Fácil sí que es.

AutoCompleteTextView

Supongamos que quieres un EditText pero un poco más sofisticado, con sugerencias. Esto es, que según escribes en el EditText te vaya sugiriendo posibilidades que concuerden con lo que vas metiendo. Bueno, pues eso es AutoCompleteTextView. Crear uno es bastante fácil, pero es un widget que tiene su miga.

AutoCompleteTextView se describe en multitud de sitios. Pero pocos te hablan de que tiene un bug por culpa del cual, dependiendo del tema que uses, las sugerencias te salen de color blanco sobre blanco: o sea, no se lee nada. Nosotros vamos a obviarlo especificando nuestra propia plantilla, que es lo primero que vamos a hacer. Hala, crea un fichero XML en layout al que llamaremos item_simple.xml.

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@android:id/text1"
      style="?android:attr/spinnerDropDownItemStyle"
      android:singleLine="true"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:minHeight="40dip"
      android:ellipsize="marquee"
      android:textColor="#222222" />

Vale, ahora necesitas definir tres cosas:

private AutoCompleteTextView editGermen;
ArrayAdapter<String> adaptadorGermen;
String[] germenes;

Lo primero de todo, vamos a rellenar el array de Strings al que hemos llamado germenes, y que es el que alimentará las sugerencias. Por ejemplo, si estamos usando una base de datos SQLite, podemos cargar todos los gérmenes que hayamos introducido en ella. Lo más práctico es llamar a una subrutina desde el onCreate, por ejemplo:

public void rellenaGermenes() {
pacientesdb=new PacientesSQLiteHelper(getActivity(), "Pacientes", null, global.dimeVersionDB());
db=pacientesdb.getReadableDatabase();
String consultaDBInterna="select distinct germen from germenes order by germen";
Cursor c=db.rawQuery(consultaDBInterna, null);
int nGermenes=c.getCount();
germenes=new String[nGermenes];
int x=0;
if (c.moveToFirst()) {
do {
germenes[x]=c.getString(0);
x++;
} while (c.moveToNext());
}
db.close();
pacientesdb.close();
}

Vale, ahora llamamos a esta rutina desde el onCreate, y a continuación definimos el AutoCompleteView, el adaptador (especificando nuestro XML), y los ponemos en contacto:

editGermen=(AutoCompleteTextView) vista.findViewById(R.id.editGermen);
adaptadorGermen=new ArrayAdapter<String>(this, R.layout.item_simple, germenes);
editGermen.setThreshold(3);
editGermen.setAdapter(adaptadorGermen);

Chrome da continuos mensajes del tipo “Shockwave Flash has crashed” en Debian Wheezy 7

Me volví loco intentando una solución a través de chrome://plugins, que es la solución que aparece en todas partes. Pero, oye, que nada.

Al final, lo que me lo solucionó sin problemas fue lo que se propone en un foro italiano. Si, está en italiano, pero los comandos se entienden a la perfección, y el problema no tanto pero prestando un poco de atención y con ayuda de Google ya está. El fallo está en el plugin Pepper Flash, que viene incorporado en el Chrome. Resulta que a partir de la versión 37.0.2062.120 de Chrome, el plugin Pepper Flash necesita las librería glibc 2.14 para funcionar. Sin embargo la versión estable de Debian Wheezy proporciona glibc 2.13. Oh, si, Chrome se actualizará sin quejarse de ninguna dependencia, pero el plugin Pepper Flash no parará de lloriquear y de romperse una y otra vez. Puedes comprobar la versión de tu navegador Chrome en el menú, "Información de Google Chrome", si te pasa a ti.

¿Hay alguna solución?. Bueno, nuestro amigo italiano tomberry propone descargar una versión anterior de Pepper Flash y sobreescribir la que viene con el Chrome. Es muy fácil, nos da los comandos listos para ejecutar (eso si, hazte root).

Para amd64 (es decir, la versión 64 bit de Debian):

cd /opt/google/chrome/PepperFlash && sudo wget http://goo.gl/4dJvJE -O libpepflashplayer.so && exit

Para i386 (es decir, la versión 32 bit de Debian):

cd /opt/google/chrome/PepperFlash && sudo wget http://goo.gl/4awX8D -O libpepflashplayer.so && exit

Ignoro qué ocurre en otras versiones de Linux tal que Ubuntu, pero es muy posible que sus librerías glibc estén más actualizadas y no les falle el Pepper Flash. ¡Eh, no te quejes, es el precio que pagas por usar la versión estable de Debian!.

Después de ejecutar el comando no te olvides de que tienes que cerrar todas las ventanas de Chrome y luego volverlo abrir. Hombre, si eres muy paranoico, puedes copiar antes el fichero /opt/google/chrome/PepperFlash/libpepflashplayer.so en un lugar seguro por si ocurre un accidente nuclear o tu ordenador es devorado por un agujero negro. A mi me funcionó como la seda, qué quieres que te diga.

¡¡Eh, un momento!!

Bueno, sí. Estás instalando una versión anterior de libpepflashplayer.so que está colgada en un Dropbox de no-se-quién. Y con privilegios de root. ¿No te fías?. Paranoia cosa fina, ¿eh?.

Vaaale, vale. Vete al directorio /var/cache/apt/archives. Ahí deberías tener todos los paquetes legales de google-chrome que has ido actualizando desde el principio de los tiempos. El sistema los deja ahí almacenados cuando los descarga de los repositorios. Elige una versión anterior a la 37.0.2062.120, descomprímela, pilla el fichero libpepflashplayer.so y copialo a /opt/google/chrome/PepperFlash. Para descomprimir el paquete puedes usar los comandos siguientes (sustituye el número de versión por uno que tengas):

cp /var/cache/apt/archives/google-chrome-stable-35.0.1916.153-1_amd64.deb ~
cd ~
ar x google-chrome-stable_35.0.1916.153-1_amd64.deb
tar --lzma --extract --verbose --file data.tar.lzma
sudo cp opt/google/chrome/PepperFlash/libpepflashplayer.so /usr/lib/pepperflashplugin-nonfree/libpepflashplayer.so

Y ya está, estás instalando un Peeper Flash que procede directamente de los repositorios. ¿Más tranquilo así?. Yo no lo he hecho, preferí vivir peligrosamente, pero debería funcionar.

Instalar Spotify en Linux Debian Wheezy 7.x

Si lo has intentado, sabrás ya que no se puede por culpa de unas dependencias anticuadas (libssl0.9.8). Con todo, aún se puede instalar Spotify en una versión un poco más antigua. Pero no necesito explicártelo porque ya lo hace muy bien Javier Lleó. A mi me funcionó sin problemas. Las gracias, a él.

Excel me cambia las fórmulas

He configurado un Excel para que actualice una de las hojas a partir de un fichero CSV. Funciona genial, porque una vez configurado que importe los datos CSV, lo seguirá haciendo una y otra vez cada vez que abras el Excel o le des a "Actualizar todo". Eso significa que solo tengo que exportar de nuevo a CSV la base de datos de la tablet y automáticamente Excel se dará cuenta y lo recalculará todo para mostrarme estadísticas actualizadas.

Solo que, por algún motivo extraño, hay determinadas fórmulas que cambian de forma absurda tras cada actualización, y en vez de hacer referencia a las casillas que quiero hacen referencia a otras que no pretendía. Cambiarlas a mano es sencillo, pero fastidia un poco porque hay que hacerlo siempre tras cada actualización del CSV.

Después de googlear un poco, parece ser que es "feature" de Excel para corregir errores comunes en la creación de fórmulas. Vale, pero lo que me interesa es que puede ser desactivado. Para ello, en Excel 2013, vete a Archivo, Opciones, pestaña Avanzadas, y desactiva la casilla que dice "Extender formatos de rangos de datos y fórmulas".

Fallos en una unidad USB después de hibernar/suspender mi ordenador

Si usas hibernar / suspender en tu ordenador y tienes unidades USB montadas habitualmente, es posible que cuando vuelva a despertar no reconozca las unidades USB y te de algún tipo de error.

La forma más rápida de evitar problemas es desmontar todas las unidades USB justo antes de hibernar y volver a montarlas después de despertar. Podemos automatizarlo todo para que ocurra sin tener que hacer nada, solo hay que añadir un script en el directorio /etc/pm/sleep.d/ (suponiendo que usas un sistema Debian o Ubuntu). Observa el script, es muy sencillo, y cámbialo según tus necesidades. Recuerda que, como cualquier script, debe tener permisos de ejecución.

#!/bin/sh
case "$1" in
       hibernate|suspend)
             umount /media/LinBkp1
             umount /media/LinBkp2
             ;;
       thaw|resume)
             mount /media/LinBkp1
             mount /media/LinBkp2
             ;;
       *)
             exit 0
             ;;
esac

Debian no reconoce mi Kindle Touch

Si, si lo reconoce. Incluso podrás montarlo con el comando mount manualmente después de enchufar el kindle al ordenador por USB. Pero no será reconocido automáticamente ni será detectado por Calibre aunque lo hagas.

Para que haga todo eso necesitas instalar las mtp tools (apt-get install mtp-tools mptfs libmtp8). Y con eso ya está. Ahora debería reconocerlo de forma automática tanto Debian como Calibre en cuanto lo conectes por USB.