Comment dessiner des lignes et écrire du texte sur une image
Patyom
Membre
Bonjour,
J'ai importé des graphiques d'internet et je voudrais les "agrémenter" d'annotations et de tracés verticaux représentant les heures de la journée pour ensuite enregistrer le tout.
J'ai déjà trouvé comment créé des ligne verticales mais je n'arrive pas à changer la couleur.
Voici le code de mon projet :
import Cocoa
final class Line: NSView {
override func draw(_ dirtyRect: NSRect) {
let myPath = NSBezierPath()
myPath.move(to: CGPoint(x: 20, y: 20))
myPath.line(to: CGPoint(x: 20, y: 300))
myPath.lineWidth = 0.3
myPath.stroke()
}
}
final class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Représention de l'image ou image extraite d'un fichier
let frame = CGRect(x: 0, y: 0, width: 100, height: 600)
// Incrustation dans l'image sélectionnée
let line = Line(frame: frame)
view.addSubview(line)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Se sont des lignes de codes que j'ai récupérées et dont je comprends le cheminement.
Là , maintenant j'aimerais
. Changer la couleur de la ligne,
. écrire un texte dedans,
. et sauvegarder le tout.
Merci
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Je n'ai pas le temps de me pencher sur la question, maintenant. Mais le sujet a été abordé souvent dans les pages du forum. Il faut utiliser un contexte graphique et/ou la fonction drawRect() draw() des views. Fait une recherche sur ces thèmes dans le forum. Les réponses y sont nombreuses.
EDIT : fichu macOS ! iOS c'est mieux quand même .. * évite la chaussure de Tablier *
Ok, je vais faire comme çà
merci
Un premier jet pour afficher une vue " composite " formée d'une image et d'un ensemble de lignes.
C'est une NSView personnalisée avec deux paramètres :
- une NSImage?
- un tableau de lignes
Les lignes sont définies dans une structure TypeLigne :
- premier point
- second point
- épaisseur
- couleur
Exemple d'utilisation :
Nouvelle version avec les textes. Chaque texte est défini par une position et un NSAttributedString (une version évoluée de String pouvant encapsuler du texte, des informations de couleurs, des polices de caractères, des informations de formatage, etc.. Un outil puissant, mais pas spécialement simple d'emploi). On en parle dans plusieurs sujets sur le forum.
Mise en oeuvre :
Pour paramétrer les textes (font, taille, couleur) il faut entrer dans les détails des NSAttributedString. Pas le temps ce soir. J'ai rendez-vous avec un Arrow Saison 5 sur Netflix (épisode S5x08 avec des envahisseurs aliens. C'est un super crossover de la chaine The CW faisant intervenir les personnages de ses 4 séries de supers héros - Arrow, Flash, SuperGirl et Legends of Tomorrow)
Il n'y a pas de solution simple pour faire une copie d'écran d'une NSView. Voici un lien avec quelques lignes de code pour y arriver :
https://pastebin.com/d4puXBi8
Variante avec définition des textes incluant :
- position
- String
- Font
- couleur d'affichage
Le système génère un NSAttributedString à la volée lors de l'affichage.
Mise en application :
Petit détail : Je n'ai pas vérifié, mais je présume qu'en cas de modifications des données, il faut utiliser needsDisplay pour dire à la NSView que son contenu n'est plus valable, et qu'elle doit se redessiner.
Merci, je vais pouvoir étudier tout cela et le mettre en application.
A+
Au lieu de recalculer à chaque mise à jour les NSBezierPath à dessiner il peut être plus simple de les calculer une fois pour toutes et de faire un simple [NSBezierPath store] à chaque mise à jour. D'autant plus que les paramètres de dessin épaisseur (lineWidth), type de raccord (lineCapStyle,lineJoinStyle, miterLimit etc) et pointillé (LineDash) peuvent rester associé au NSBezierPath mais pas la couleur !
En plus NSBezierPath conforms to NSCoding
donc sauvegarde et lecture automatiques.
Il fallait lire [NSBezierPath stoke] et non pas [NSBezierPath store] bien sûr ! (maudit correcteur)
Tu voulais dire -[NSBezierPath stroke], non ? ;-)
Magnifique le tuto, j'ai déjà inséré la plus part de mes lignes dans mes graphs mais comme je ne connais pas le nombre peut différé pour chaque graph, je suis obliger de modifier un peu le code pour automatiser la chose. J'avance de plus en plus dans ce logiciel et ce n'est que le début.
Merci à tous, allez j'y retourne.
Oui, c'est largement perfectible avec différentes optimisations. On peut aussi modifier le système de positionnement dans la vue, utilisant des coordonnées exprimés en pourcentage, plutôt qu'en valeurs absolue, de manière à permettre le redimensionnement de l'image. Quoi que c'est peut-être moins utile sous macOS que sous iOS.
j'ai une question toujours sur le positionnement de mes lignes verticales, voilà :
j'ai 3 imageViews l'une au-dessus de l'autre, de même dimension contenant les graphiques et bien entendu mes lignes ne vont pas toutes dans la même imageView. Comment faire ?
Une idée
Je pose la question non sans avoir tenté quelque chose, évidemment
Une superposition de 3 imagesview ??? ? Tu n'as pas peur que le contenu de la première soit totalement caché par les deux autres ?
C'est difficile de te conseiller sans en savoir davantage sur ton application. Les lignes et les textes d'une imageview peuvent-ils être partiellement effacés par les vues situées plus haut, ou sont-elles prioritaires sur les graphismes ?
Tu as trois architectures possibles :
- a/ le sandwich
- Fond d'écran
- imageView1
- VuePersonnalisée1 pour les lignes et les textes de l'image 1
- imageView2
- VuePersonnalisée2 pour les lignes et les textes de l'image 2
- imageView3
- VuePersonnalisée3 pour les lignes et les textes de l'image 3
Inconvénient : les lignes et les textes d'une couche peuvent être recouverts par une autre
- b/ la pile d'assiettes
- Fond d'écran
- imageView1
- imageView2
- imageView3
- VuePersonnalisée1 pour les lignes et les textes de l'image 1
- VuePersonnalisée2 pour les lignes et les textes de l'image 2
- VuePersonnalisée3 pour les lignes et les textes de l'image 3
- c/ Le couvercle
- Fond d'écran
- imageView1
- imageView2
- imageView3
- VuePersonnalisée regroupant TOUTES les illustrations ( lignes et les textes)
J'ai réfléchi et je crois que je vais faire une seule imageView regroupant les trois graphiques et positionner mes lignes verticales en changeant l'origine des y à volonté.
Ce sera surement plus simple mais encore faut-il que j'arrive à le faire
mais bon.
Je vous tiens au courant.
Merci
J'ai cru comprendre que toutes tes images avaient la taille. Et maintenant tu parles de changer la position des lignes en modifiant l'origine des y. Est-ce que tu fabrique tes images en assemblant verticalement des graphismes, dans ce style ?
++++++++++++++++++++++++++
+ Image 1 : Haut du graphisme +
++++++++++++++++++++++++++
+ Image 2 : Milieu du graphisme +
++++++++++++++++++++++++++
+ Image 3 : Bas du graphisme +
++++++++++++++++++++++++++
Avec des éléments graphique de base :
++++++++++++++++++++++++++
+ Image 1 : Haut du graphisme +
++++++++++++++++++++++++++
+ +
+ TRANSPARENCE +
+ +
++++++++++++++++++++++++++
+ TRANSPARENCE +
++++++++++++++++++++++++++
+ Image 2 : Milieu du graphisme +
++++++++++++++++++++++++++
+ TRANSPARENCE +
++++++++++++++++++++++++++
+ +
+ TRANSPARENCE +
+ +
++++++++++++++++++++++++++
+ Image 3 : Bas du graphisme +
++++++++++++++++++++++++++
EDIT : Ah zut, ce n'est pas facile d'aligner les lettres pour dessiner des cadres de même taille, le forum n'utilisant pas la même police pour l'édition et l'affichage des posts terminés. Enfin, tant que cela reste compréhensible ..
En fait, j'ai 1 graph avec une tranche horaire de 8:00 à 11:00
en dessous 1 graph avec la tranche horaire de 11:00 à 14:00
et en dessous 1 graph avec la tranche horaire de 14:00 à 17:45
donc les lignes dont l'horaire est compris entre 8:00 et 11:00 vont s'afficher dans le 1er graph,
les lignes dont l'horaire est compris entre 11:00 et 14:00 vont s'afficher dans le 2ème graph,
Bin non, c'est facile à faire. Il suffit d'utiliser Storyboard pour placer une vue personnalisée au-dessus de chaque imageView, et récupérer 3 nouveaux outlets. Ce n'est pas bien loin à faire. Je t'explique ça demain.
Bonjour,
Petite remarque, j'arrive bien à créer mon graphique avec les bonnes ligne verticales dans le Rect mais ce que je ne sais pas faire c'est recopier la totalité de ce qu'il y a dans le Rect (graph + lignes + textes) dans mon imageView prédéfinie dans le Main Storyboard.
Je fais :
mais bien entendu il ne copie que l'image.
il faut que j'ajoute la couche avec les lignes et celle avec les textes.
Là , je ne sais pas faire
Une version légèrement modifiée de la classe ne gérant plus d'image. Tu n'en a pas besoin, si tu utilises une imageView en fond d'écran.
Mise en oeuvre :
J'ai utilisé Storyboard pour créer 3 imagesView et 3 ViewAvecCalque. Chaque imageView est recouverte d'une ViewAvecCalque. J'ai défini un jeu de contraintes de sorte que chaque ViewAvecCalque possède les mêmes dimensions et la même position que la vue qu'elle recouvre.
Si tu ne sais pas te servir de Storyboard pour créer graphiquement des vues personnalisées, ou créer des contraintes pour cloner une caractéristique graphique d'un objet (taille et dimension) dis-le je t'expliquerait.
Pour en revenir au sujet, j'ai 3 liens (IBOutlet) vers les imagesView et 3 vers les Calques. Il suffit ensuite d'envoyer les bonnes données sur les différents calques, pour dessiner les lignes et les textes.
Ton approche est assez compliqué, avec les 3 imagesView.
Si j'avais eu à faire la même chose, j'aurais viré toutes les imagesView pour n'utiliser qu'une vue personnalisée UNIQUE s'occupant d'assembler les images de fond et de l'affichage des lignes et textes.
C'est beaucoup mieux comme ça
Bien, alors pouvez-vous m'expliquer comment regrouper 3 images dans une seule imageView ou Rect ?
La solution est déjà présente dans l'une des premières versions de ViewAvecCalque :
Plus précisément :
L'image est affichée sur la totalité du rectangle de la vue personnalisée. En modifiant le rectangle d'affichage, on peut donner n'importe taille ou position à l'image.
Nouvelle version de la fonction utilisant cette technique pour afficher les 3 images dans la Vue Personnalisée.
EDIT : Bon, ça marche PRESQUE ! Il y a une inversion de l'ordre d'affichage des bandes. J'ai codé avec une logique iOS alors que l'orientation de l'axe y est inversé sur ce fichu macOS.. Je dois filer, je n'ai pas le temps de régler le problème. Ce n'est pas difficile à faire. Tu devrais y arriver par toi-même.
c'est bon, je suis arrivé à édifier mon prog d'affichage images + lignes (textes pas encore, mais çà va venir) et je vous remercie de vos conseils, j'ai eu un peu de mal avec les horaires pour placer les lignes (pb avec les Floats et les minutes à transformer) oui parce que les lignes verticales correspondaient à des heures précises.
Maintenant il faut que j'enregistre tout ce que j'ai empilé dans mon Rect global (j'ai opté pour le Rect global, c'est plus simple).
là , non plus c'est pas gagné.
Tout frais, tout chaud :
Utilisation :
Et juste pour te montrer qu'il n'est pas facile à dessiner, voici du code d'un ancien projet (oui, c'est ObjC 8--) ) qui montre comment séparer le dessin des éléments pour le faire plus facile à lire
* hurle d'horreur avant de s'évanouir ! *
Je peux te montrer plus 8--)